It's really difficult to find a job during the epidemic. If you want to get a satisfactory salary, you really need strength!
Interviewer: have you used @ Value in Spring? Let me introduce it
Me: @ Value can be marked on the field. You can put the data in the external configuration file, such as some configuration information of the database in the configuration file, and then inject it into some fields of the bean through @ Value
Interviewer: so @ Value data comes from the configuration file?
Me: Well, the most common way for our project is to reference the configuration in the Properties file through @ Value
Interviewer: @ Value data source is there any other way?
Me: I'm very happy at this time. I've studied everything I just asked. I said: of course, you can put the configuration information in db or other storage media. When the container starts, you can load this information into the Environment. The Value applied in @ Value is finally resolved through the Environment, so you only need to expand the Environment.
Interviewer: not bad. It seems that you can still study spring. Do you like studying spring source code?
Me: I said with a smile, well, I really like the source code when I am free. I feel that I know spring well. I can't be regarded as proficient, but also semi proficient
Interviewer: look at me and smile. Can the injected Value of @ Value be refreshed dynamically?
Me: it should be OK. I remember that the @ RefreshScope annotation in springboot can realize the function you said
Interviewer: can you tell us how @ RefreshScope is implemented? Can you give us a general introduction?
Me: Um... I read this before, but I didn't understand it
Interviewer: it doesn't matter. You can go back and study it again; What salary do you expect?
Me: 30000
Interviewer: today's interview is OK, but it would be better if @ RefreshScope could answer. This is a bonus item, but it's really difficult. How about 25000?
I: (thought silently in my heart: 25000, that's a question. I didn't answer well. I cut 5000. It's a little cruel. I'll go back and study it again. 30000 must be no problem). I said: the minimum is 29000
Interviewer: Thank you. That's all for today's interview. Turn right when you go out. Don't give it away!
I have a good habit. Every time I go back from the interview, I will make a reply. I must find a way to solve the problems that haven't been solved, so it's not empty.
The interview questions are as follows
-
@Usage of Value
-
@Value data source
-
@Value dynamic refresh problem
Let's sort out these questions one by one to help you pass the interview and get a high salary during the epidemic.
@Usage of Value
The system needs to connect to db, which has a lot of configuration information.
Mail needs to be sent in the system. To send mail, you need to configure the information of the mail server.
There are other configuration information.
We can put these configuration information in a configuration file, which can be modified by the operation and maintenance department when we go online.
How to use these configuration information in the system? spring provides @ Value annotation to solve this problem.
Usually, we will store the configuration information in the properties configuration file in the form of key=value.
Use @ Value("${key}" in the configuration file) to reference the value corresponding to the specified key.
@Value usage steps
Step 1: use the @ PropertySource annotation to import the configuration file
Place @ PropertySource on the class as follows
@PropertySource({"Profile path 1","Profile path 2"...})
@The PropertySource annotation has a value attribute, a string array type, which can be used to specify the path of multiple configuration files.
For example:
@Component @PropertySource({"classpath:com/javacode2018/lesson002/demo18/db.properties"}) public class DbConfig { }
Step 2: use the @ Value annotation to reference the Value of the configuration file
Reference the Value in the above configuration file through @ Value:
grammar
@Value("${In the configuration file key:Default value}") @Value("${In the configuration file key}")
For example:
@Value("${password:123}")
If the above password does not exist, take 123 as the value
@Value("${password}")
If the above password does not exist, the value is ${password}
If the configuration file is as follows
jdbc.url=jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8 jdbc.username=javacode jdbc.password=javacode
The usage is as follows:
@Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password;
Let's look at the case
case
A configuration file db.properties
jdbc.url=jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8 jdbc.username=javacode jdbc.password=javacode
To create a configuration class, use @ PropertySource to import the above configuration file
package com.javacode2018.lesson002.demo18.test1; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; @Configurable @ComponentScan @PropertySource({"classpath:com/javacode2018/lesson002/demo18/db.properties"}) public class MainConfig1 { }
To a class, use @ Value to use the information in the configuration file
package com.javacode2018.lesson002.demo18.test1; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class DbConfig { @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "DbConfig{" + "url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
The above focuses on the @ Value annotation, and pay attention to the @ Value annotation
Let's have a test case
package com.javacode2018.lesson002.demo18; import com.javacode2018.lesson002.demo18.test1.DbConfig; import com.javacode2018.lesson002.demo18.test1.MainConfig1; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class ValueTest { @Test public void test1() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig1.class); context.refresh(); DbConfig dbConfig = context.getBean(DbConfig.class); System.out.println(dbConfig); } }
Run output
DbConfig{url='jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8', username='javacode', password='javacode'}
The above is relatively simple to use. Many people who have used it can understand it at a glance. This is also the first problem. Most people are ok. Let's see whether there are other methods for the data source in @ Value in addition to the configuration file.
@Value data source
Usually, our @ Value data comes from the configuration file, but we can also use other methods. For example, we can put the contents of the configuration file in the database, which makes it easier to modify.
We need to know where the data in @ Value comes from spring.
There are classes in spring
org.springframework.core.env.PropertySource
It can be understood as a configuration source, which contains the configuration information of key - > value. You can obtain the value information corresponding to the key through the methods provided in this class
There is an internal method:
public abstract Object getProperty(String name);
Get the corresponding configuration information through name.
The system has an important interface
org.springframework.core.env.Environment
Used to represent environment configuration information, this interface has several important methods
String resolvePlaceholders(String text); MutablePropertySources getPropertySources();
resolvePlaceholders is used to parse ${text}, and the @ Value annotation is finally resolved by calling this method.
getPropertySources returns MutablePropertySources object. Take a look at this class
public class MutablePropertySources implements PropertySources { private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>(); }
It contains a list of propertySourceList.
There will be an Environment object in the spring container. Finally, the resolvePlaceholders method of this object will be called to resolve @ Value.
You can review the process of final parsing @ Value:
1. take@Value Annotated value Parameter value as Environment.resolvePlaceholders Method parameters are parsed 2. Environment Internal access MutablePropertySources To analyze 3. MutablePropertySources There are multiple internal PropertySource,The traversal is performed PropertySource List, calling PropertySource.getProperty Method to analyze key Corresponding value
Through the above process, if we want to change the source of @ Value data, we only need to wrap the configuration information as a PropertySource object and throw it into MutablePropertySources in the Environment.
Let's follow this idea.
To a mail configuration information class, use @ Value internally to inject mail configuration information
package com.javacode2018.lesson002.demo18.test2; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * Mail configuration information */ @Component public class MailConfig { @Value("${mail.host}") private String host; @Value("${mail.username}") private String username; @Value("${mail.password}") private String password; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "MailConfig{" + "host='" + host + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
Another class is DbUtil. The getMailInfoFromDb method simulates obtaining mail configuration information from db and storing it in map
package com.javacode2018.lesson002.demo18.test2; import java.util.HashMap; import java.util.Map; public class DbUtil { /** * Simulate getting mail configuration information from db * * @return */ public static Map<String, Object> getMailInfoFromDb() { Map<String, Object> result = new HashMap<>(); result.put("mail.host", "smtp.qq.com"); result.put("mail.username", "Passerby"); result.put("mail.password", "123"); return result; } }
A spring configuration class
package com.javacode2018.lesson002.demo18.test2; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class MainConfig2 { }
Here are the key codes
@Test public void test2() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); /*The following paragraph is the key start*/ //Get configuration information from db Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb(); //Throw it into MapPropertySource (MapPropertySource class is a class provided by spring and a subclass of PropertySource) MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb); //Drop mailPropertySource at the first of the PropertySource list in the Environment to get the highest priority context.getEnvironment().getPropertySources().addFirst(mailPropertySource); /*The above paragraph is the key end*/ context.register(MainConfig2.class); context.refresh(); MailConfig mailConfig = context.getBean(MailConfig.class); System.out.println(mailConfig); }
The notes are quite detailed, so I won't explain them in detail.
Run directly and see the effect
MailConfig{host='smtp.qq.com', username='Passerby', password='123'}
Do you feel good? At this time, you can modify DbUtil.getMailInfoFromDb at will. The specific data comes from db, redis or other media for everyone to play.
The above focuses on the following code, which you need to understand
/*The following paragraph is the key start*/ //Get configuration information from db Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb(); //Throw it into MapPropertySource (MapPropertySource class is a class provided by spring and a subclass of PropertySource) MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb); //Drop mailPropertySource at the first of the PropertySource list in the Environment to get the highest priority context.getEnvironment().getPropertySources().addFirst(mailPropertySource); /*The above paragraph is the key end*/
Let's move on to the next question
If we put the configuration information in db, we may modify the configuration information through an interface, and then save it. We hope that these values will take effect immediately in the spring container without restarting the system.
@The problem of dynamic refresh of Value is implemented in springboot using @ RefreshScope.
Realize @ Value dynamic refresh
First understand a knowledge point
We need to talk about a knowledge point first. We don't use too much, so many people don't know much, but we have a very important point. Let's take a look.
This knowledge point is the custom bean scope. If you don't know this, please take a look at this article: bean scope details
There is a place not mentioned in the bean Scope. Take a look at the source code of the @ Scope annotation. One parameter is:
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
The value of this parameter is an enumeration of ScopedProxyMode type, and the values are listed in the following 4
public enum ScopedProxyMode { DEFAULT, NO, INTERFACES, TARGET_CLASS; }
Let's not talk about the first three, but directly talk about what the last value is for.
When proxyMode in @ Scope is target_ During class, a proxy object will be generated for the currently created bean through cglib to access the target bean object.
It's rather obscure to understand. Let's look at the code. It's easier to understand. Let's take a custom Scope case.
Customize the annotation of a bean scope
package com.javacode2018.lesson002.demo18.test3; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Scope(BeanMyScope.SCOPE_MY) //@1 public @interface MyScope { /** * @see Scope#proxyMode() */ ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;//@2 }
@1: The @ Scope annotation is used. Value refers to a constant and the value is my. You can see it later.
@2: Note that the parameter name is proxyMode and the type is ScopedProxyMode. There is a parameter of the same type in the @ Scope annotation. When the spring container resolves, the value of this parameter will be assigned to the proxyMode parameter of the @ Scope annotation above the @ MyScope annotation. Therefore, we set the proxyMode value here, The final effect is to directly change the value of proxyMode parameter in @ Scope. The default value here is ScopedProxyMode.TARGET_CLASS
@The Scope corresponding to the MyScope annotation is implemented as follows
package com.javacode2018.lesson002.demo18.test3; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.Scope; import org.springframework.lang.Nullable; /** * @see MyScope Scope implementation */ public class BeanMyScope implements Scope { public static final String SCOPE_MY = "my"; //@1 @Override public Object get(String name, ObjectFactory<?> objectFactory) { System.out.println("BeanMyScope >>>>>>>>> get:" + name); //@2 return objectFactory.getObject(); //@3 } @Nullable @Override public Object remove(String name) { return null; } @Override public void registerDestructionCallback(String name, Runnable callback) { } @Nullable @Override public Object resolveContextualObject(String key) { return null; } @Nullable @Override public String getConversationId() { return null; } }
@1: Defines a constant as the value of the scope
@2: This get method is the key. The custom scope will automatically call this get method to create bean objects. A line of log is output here for the convenience of viewing the results later
@3: Get the bean instance return through objectFactory.getObject().
Let's create a class with the scope defined above
package com.javacode2018.lesson002.demo18.test3; import org.springframework.stereotype.Component; import java.util.UUID; @Component @MyScope //@1 public class User { private String username; public User() { System.out.println("---------establish User object" + this); //@2 this.username = UUID.randomUUID().toString(); //@3 } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
@1: The custom scope @ MyScope is used
@2: Output a line of log in the constructor
@3: Assign a value to username and randomly generate one through uuid
Use a spring configuration class to load the components marked @ componentabove
package com.javacode2018.lesson002.demo18.test3; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @ComponentScan @Configuration public class MainConfig3 { }
Here's the focus, test cases
@Test public void test3() throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); //Register the custom scope with the spring container context.getBeanFactory().registerScope(BeanMyScope.SCOPE_MY, new BeanMyScope());//@1 context.register(MainConfig3.class); context.refresh(); System.out.println("Get from container User object"); User user = context.getBean(User.class); //@2 System.out.println("user Object class Is:" + user.getClass()); //@3 System.out.println("Multiple calls user of getUsername Feel the effect\n"); for (int i = 1; i <= 3; i++) { System.out.println(String.format("********\n The first%d Start call getUsername", i)); System.out.println(user.getUsername()); System.out.println(String.format("The first%d Call times getUsername end\n********\n", i)); } }
@1: Register the custom scope with the spring container
@2: Get the bean corresponding to User from the container
@3: Output the class corresponding to this bean. Take a closer look later to see if this type is User type
After the code, three loops are made to call the getUsername method of user, and a line of log is output before and after the method.
It's time to witness the miracle. Run the output
Get from container User object user Object class Is: class com.javacode2018.lesson002.demo18.test3.User$$EnhancerBySpringCGLIB$$80233127 Multiple calls user of getUsername Feel the effect ******** First start call getUsername BeanMyScope >>>>>>>>> get:scopedTarget.user ---------establish User object com.javacode2018.lesson002.demo18.test3.User@6a370f4 7b41aa80-7569-4072-9d40-ec9bfb92f438 First call getUsername end ******** ******** 2nd start call getUsername BeanMyScope >>>>>>>>> get:scopedTarget.user ---------establish User object com.javacode2018.lesson002.demo18.test3.User@1613674b 01d67154-95f6-44bb-93ab-05a34abdf51f Second call getUsername end ******** ******** The 3rd start call getUsername BeanMyScope >>>>>>>>> get:scopedTarget.user ---------establish User object com.javacode2018.lesson002.demo18.test3.User@27ff5d15 76d0e86f-8331-4303-aac7-4acce0b258b8 3rd call getUsername end ********
As can be seen from the first two lines of output:
-
When calling context.getBean(User.class) to get the bean from the container, the User constructor is not called to create the User object
-
The type of output in the second line shows that the user object returned by getBean is a cglib proxy object.
As can be seen from the following log output, each time the user.getUsername method is called, the BeanMyScope#get method and the User constructor are automatically called internally.
As can be seen from the above case, when proxymode = scopedproxymode.target in the customized Scope_ During class, a proxy object will be created for the bean. Any method calling the proxy object will call the get method in the custom Scope implementation class (beanmycope above) to retrieve the bean object again.
Implementation of dynamic refresh @ Value
Then, we can use the features explained above to realize the dynamic refresh of @ Value and a custom Scope. This custom Scope supports the automatic refresh of @ Value annotation. The custom annotation can be marked on the classes that need to use the automatic refresh of @ Value annotation. When the configuration is modified and any method of these beans is called, Let spring restart and initialize the bean. This idea can be realized. Let's write the code below.
First, customize a Scope: RefreshScope
package com.javacode2018.lesson002.demo18.test4; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Scope(BeanRefreshScope.SCOPE_REFRESH) @Documented public @interface RefreshScope { ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS; //@1 }
The class annotated with @ RefreshScope annotation is required to support the configuration of dynamic refresh @ Value
@1: This is the key, using ScopedProxyMode.TARGET_CLASS
The resolution class corresponding to this custom Scope
Several irrelevant methods in the following classes have been removed and can be ignored
package com.javacode2018.lesson002.demo18.test4; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.Scope; import org.springframework.lang.Nullable; import java.util.concurrent.ConcurrentHashMap; public class BeanRefreshScope implements Scope { public static final String SCOPE_REFRESH = "refresh"; private static final BeanRefreshScope INSTANCE = new BeanRefreshScope(); //A map is used to cache bean s private ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(); //@1 private BeanRefreshScope() { } public static BeanRefreshScope getInstance() { return INSTANCE; } /** * Clean up current */ public static void clean() { INSTANCE.beanMap.clear(); } @Override public Object get(String name, ObjectFactory<?> objectFactory) { Object bean = beanMap.get(name); if (bean == null) { bean = objectFactory.getObject(); beanMap.put(name, bean); } return bean; } }
The above get method will first get the bean from the beanMap. If it cannot get the getObject that will call objectFactory, let spring create an instance of the bean, and then throw it into the beanMap
The clean method above is used to clean up all beans currently cached in the beanMap
To a mail configuration class, use the @ Value annotation injection configuration. The scope of this bean is a custom @ RefreshScope
package com.javacode2018.lesson002.demo18.test4; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * Mail configuration information */ @Component @RefreshScope //@1 public class MailConfig { @Value("${mail.username}") //@2 private String username; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String toString() { return "MailConfig{" + "username='" + username + '\'' + '}'; } }
@1: The custom scope @ RefreshScope is used
@2: Inject mail.username's Value for a through @ Value
The toString method is rewritten, and the effect can be seen when testing later.
Another ordinary bean will be injected with MailConfig
package com.javacode2018.lesson002.demo18.test4; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MailService { @Autowired private MailConfig mailConfig; @Override public String toString() { return "MailService{" + "mailConfig=" + mailConfig + '}'; } }
The code is relatively simple. The toString method is rewritten. You can see the effect when testing later.
To get the mail configuration information from db
package com.javacode2018.lesson002.demo18.test4; import java.util.HashMap; import java.util.Map; import java.util.UUID; public class DbUtil { /** * Simulate getting mail configuration information from db * * @return */ public static Map<String, Object> getMailInfoFromDb() { Map<String, Object> result = new HashMap<>(); result.put("mail.username", UUID.randomUUID().toString()); return result; } }
To a spring configuration class, scan and load the above components
package com.javacode2018.lesson002.demo18.test4; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class MainConfig4 { }
A tool class
There are 2 internal methods, as follows:
package com.javacode2018.lesson002.demo18.test4; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.env.MapPropertySource; import java.util.Map; public class RefreshConfigUtil { /** * Simulate changing the configuration information in the database */ public static void updateDbConfig(AbstractApplicationContext context) { //Update mailPropertySource configuration information in context refreshMailPropertySource(context); //Clear the cache of all beans in BeanRefreshScope BeanRefreshScope.getInstance().clean(); } public static void refreshMailPropertySource(AbstractApplicationContext context) { Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb(); //Throw it into MapPropertySource (MapPropertySource class is a class provided by spring and a subclass of PropertySource) MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb); context.getEnvironment().getPropertySources().addFirst(mailPropertySource); } }
The updateDbConfig method simulates the method to be called when modifying the configuration in db. There are two lines of code in the method. The first line of code calls the refreshMailPropertySource method to modify the configuration information of the mail in the container
BeanRefreshScope.getInstance().clean() is used to clear all cached beans in BeanRefreshScope. When calling any method of the bean, it will restart the spring container to create the bean. When the spring container re creates the bean, it will re parse the @ Value information. At this time, the mail configuration information in the container is new, so the @ Value injected information is also new.
Let's have a test case
@Test public void test4() throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.getBeanFactory().registerScope(BeanRefreshScope.SCOPE_REFRESH, BeanRefreshScope.getInstance()); context.register(MainConfig4.class); //Refresh the mail configuration to the Environment RefreshConfigUtil.refreshMailPropertySource(context); context.refresh(); MailService mailService = context.getBean(MailService.class); System.out.println("When the configuration is not updated,Output 3 times"); for (int i = 0; i < 3; i++) { //@1 System.out.println(mailService); TimeUnit.MILLISECONDS.sleep(200); } System.out.println("Simulate the configuration effect of 3 updates"); for (int i = 0; i < 3; i++) { //@2 RefreshConfigUtil.updateDbConfig(context); //@3 System.out.println(mailService); TimeUnit.MILLISECONDS.sleep(200); } }
@1: Loop 3 times and output the information of mailService
@2: Loop for 3 times. Internally, @ 3 is used to simulate and update the configuration information in db, and then mailService information is output
Witness the miracle and see the effect
When the configuration is not updated,Output 3 times MailService{mailConfig=MailConfig{username='df321543-8ca7-4563-993a-bd64cbf50d53'}} MailService{mailConfig=MailConfig{username='df321543-8ca7-4563-993a-bd64cbf50d53'}} MailService{mailConfig=MailConfig{username='df321543-8ca7-4563-993a-bd64cbf50d53'}} Simulate the configuration effect of 3 updates MailService{mailConfig=MailConfig{username='6bab8cea-9f4f-497d-a23a-92f15d0d6e34'}} MailService{mailConfig=MailConfig{username='581bf395-f6b8-4b87-84e6-83d3c7342ca2'}} MailService{mailConfig=MailConfig{username='db337f54-20b0-4726-9e55-328530af6999'}}
The above MailService has output six times. The value of username in the first three times is the same, and the value of username in the next three times is different, indicating that the modified configuration is effective.
Summary
The key to dynamic @ Value implementation is the proxyMode parameter in @ Scope. The Value is ScopedProxyMode.DEFAULT. An agent will be generated to achieve the effect of @ Value dynamic refresh. This place is the key.
If you are interested, you can take a look at the @ RefreshScope annotation source code in springboot, which is similar to our customized @ RefreshScope above, and the implementation principle is similar.
summary
We have solved all the three questions in this interview. I hope you have mastered them. If you have any questions, please leave me a message and communicate