Interviewer: Have you used @Value in Spring before?
Me: @Value can be labeled on fields and data from external profiles, such as some configuration information from databases, can be placed in profiles and then injected into some fields of bean s by @Value
Interviewer: That means the @Value data comes from the profile?
Me: Well, the most common use of our projects is to reference configurations in the Properties file through @Value
Interviewer: @Value data source is there any other way?
Me: I'm so happy at this point that I've studied all of them just to ask. I said: Of course, configuration information can be placed in db or other storage media. When the container starts, it can be loaded into Environment. The value applied in @Value is ultimately resolved by Environment, so it can be achieved by simply extending Environment.
Interviewer: That's good. It seems that you can do some research on spring. Do you like to study spring sources?
Me: Say with a smile, well, I really like to play with the source when I am free. I feel I know spring well, I can't do it properly, I'm a semi-proficient.
Interviewer: Looking at my smile, can the @Value value be dynamically refreshed?
Me: Yes, I remember that a @RefreshScope comment in springboot would do what you said
Interviewer: Can you tell us how @RefreshScope was implemented? Can you give us a general overview?
Me: Hmm.. I've read a little before, but I don't understand it
Interviewer: That's okay. You can go back and study again. What salary do you expect?
Me: 30,000
Interviewer: Today's interview is okay, but it would be better if @RefreshScope could answer. This is a plus, but it's a bit difficult. How about 25,000?
Me: (Think to myself: 25,000, is a question not answered properly, cut 5000, a bit ruthless, I want to go back and research, 30,000 is certainly OK). I said: the minimum 29,000
Interviewer: Thank you. That's where the interview is today. Go out and turn right. No!
I have a good habit that every time I go back for an interview, I will make a revert. I must find a way to solve the problem that I have not solved so that it will be worthwhile.
The interview questions are as follows
-
Usage of @Value
-
@Value data source
-
@Value dynamic refresh problem
Usage of @Value
The system requires a connection to db, which has a lot of configuration information.
Mail needs to be sent in the system, and information about configuring the mail server is needed to send mail.
There are other configuration information.
We can put these configuration information in a single configuration file, which will be modified by operations and maintenance when going online.
So how do you use this configuration information in your system? The @Value annotation is provided in spring to solve this problem.
Usually we store configuration information in the properties configuration file as key=value.
Reference the value corresponding to the specified key through @Value("${key}" in the configuration file).
@Value usage steps
Step 1: Use the @PropertySource annotation to import the configuration file
Place @PropertySource above the class as follows
@PropertySource({"Profile Path 1","Profile Path 2"...})
The @PropertySource annotation has a value property, a string array type, that can be used to specify the paths to multiple profiles.
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 values in the configuration file above with @Value:
grammar
@Value("${In the configuration file key:Default value}")
@Value("${In the configuration file key}")
For example:
@Value("${password:123}")
If password does not exist above, use 123 as the value
@Value("${password}")
If password does not exist above, the value is ${password}
Suppose the configuration file is as follows
jdbc.url=jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8
jdbc.username=javacode
jdbc.password=javacode
Use it 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 below
case
Come to the configuration file db.properties
jdbc.url=jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8
jdbc.username=javacode
jdbc.password=javacode
Come to a configuration class and 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 {
}
Come a class that uses @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 focus above is on the @Value comment, note that the @Value comment
Come to 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'}
It's easy to use, and many people who have used it will understand it at a glance. This is also the first question. Most people are ok. Here's how @Value data comes from, in addition to the configuration file.
@Value data source
Usually our @Value data comes from a configuration file, but there are other ways, such as putting the contents of the configuration file in a database, to make it easier to modify.
We need to first understand where the data in @Value comes from spring.
There is a class in spring
org.springframework.core.env.PropertySource
You can think of it as a configuration source that contains configuration information for key->value, and you can get the corresponding value information for key by using the methods provided in this class
There is a way inside:
public abstract Object getProperty(String name);
Get the corresponding configuration information by name.
The system has a more important interface
org.springframework.core.env.Environment
Used to represent environment configuration information, this interface has several methods that are important
String resolvePlaceholders(String text);
MutablePropertySources getPropertySources();
resolvePlaceholders are used to parse ${text}, and the @Value comment is the last one to call this method to parse.
getPropertySources returns the MutablePropertySources object to see this class
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
}
Inside contains a list of propertySourceList s.
There will be an Environment object in the spring container, and the resolvePlaceholders method for this object will be called to resolve @Value.
You can take a look at the process that ultimately resolves @Value:
1. take@ValueAnnotated value Parameter value as Environment.resolvePlaceholders Method parameters are parsed
2. Environment Internal access MutablePropertySources To parse
3. MutablePropertySources There are multiple inside PropertySource,This will traverse PropertySource List, call PropertySource.getProperty Method parsing key Corresponding values
Through the above process, if we want to change the source of @Value data, we just need to wrap the configuration information as a PropertySource object and drop it inside MutablePropertySources in Environment.
Let's follow this line of thought.
To a mail configuration information class, inject mail configuration information internally using @Value
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, DbUtil, the getMailInfoFromDb method simulates getting 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;
}
}
Come to 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*/
//Simulate getting configuration information from db
Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();
//Drop it in MapPropertySource (MapPropertySource class is a class provided by spring and a subclass of PropertySource)
MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb);
//Drop mailPropertySource first in the PropertySource list in Environment to give 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);
}
Comments are more detailed and are not explained in detail.
Run directly to see the effect
MailConfig{host='smtp.qq.com', username='Passerby', password='123'}
Whether it feels good or not, you can modify DbUtil.getMailInfoFromDb at will, using data from db, redis or other media for everyone to use.
The main point is the code below, which you need to understand
/*The following paragraph is the key start*/
//Simulate getting configuration information from db
Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();
//Drop it in MapPropertySource (MapPropertySource class is a class provided by spring and a subclass of PropertySource)
MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb);
//Drop mailPropertySource first in the PropertySource list in Environment to give 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 might modify it through an interface, then save it, and we want the values to take effect immediately in the spring container without restarting it.
Problems with @Value dynamic refresh, implemented in springboot using @RefreshScope.
Implement @Value dynamic refresh
Learn a point first
This section needs to start with a point of knowledge, not too many are used, so many people do not know it, but a very important point, let's take a look.
This point of knowledge is custom bean scopes. To learn more about this, take a look at this article: bean Scope Details
There is a place in the bean scope that is not mentioned. Look at the source code for the @Scope comment. There is one parameter:
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
The value of this parameter is an enumeration of type ScopedProxyMode l with a value of 4 below
public enum ScopedProxyMode {
DEFAULT,
NO,
INTERFACES,
TARGET_CLASS;
}
Let's not talk about the first three, but what the last value is.
When the proxyMode in @Scope is TARGET_ When CLASS is used, a proxy object is generated for the currently created beans through cglib to access the target bean object.
It's more obscure to understand, but let's look at the code to make it easier to understand, a custom Cope case.
Customize annotations for 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: Using the @Scope annotation, value refers to a constant with a value of my, which you will see below.
@2: Notice here that the parameter name is also proxyMode and the type is ScopedProxyMode, and the @Scope annotation has the same type of parameter as this one. When the spring container parses, it assigns the value of this parameter to the @Scope annotation proxyMode parameter above the @MyScope annotation, so here we set the proxyMode value. The final effect is to directly change the value of the proxyModel parameter in @Scope. The default value here is ScopedProxyMode.TARGET_CLASS
The Scope implementation for the @MyScope annotation is 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: A constant is defined as the value of the scope
@2: This get method is critical, and the custom scope automatically calls this get method to create the bean object, which outputs a line of logs for easy viewing
@3: Get the bean instance return through objectFactory.getObject().
Let's create a class with a 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: Used a custom scope @MyScope
@2: Output a line of logs in the constructor
@3: assign username a value, randomly generated by uuid
Come to the spring configuration class and load the components labeled @Compontent above
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 custom scopes in 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's class For:" + 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 No.%d Start Call getUsername", i));
System.out.println(user.getUsername());
System.out.println(String.format("No.%d Secondary call getUsername End\n********\n", i));
}
}
@1: Register custom scopes in the spring container
@2: Get the bean for User from the container
@3: Output the class corresponding to this bean and take a closer look to see if this type is of User type
There are three more loops behind the code, calling the user's getUsername method, and the method outputs a line of logs before and after.
It's time to witness the miracle, run the output
Get from Container User object
user Object'sclassFor:class com.javacode2018.lesson002.demo18.test3.User$$EnhancerBySpringCGLIB$$80233127
Multiple callsuserOfgetUsernameFeel the effect
********
First Start CallgetUsername
BeanMyScope >>>>>>>>> get:scopedTarget.user
---------EstablishUserobjectcom.javacode2018.lesson002.demo18.test3.User@6a370f4
7b41aa80-7569-4072-9d40-ec9bfb92f438
First invocationgetUsernameEnd
********
********
2nd Start CallgetUsername
BeanMyScope >>>>>>>>> get:scopedTarget.user
---------EstablishUserobjectcom.javacode2018.lesson002.demo18.test3.User@1613674b
01d67154-95f6-44bb-93ab-05a34abdf51f
Second callgetUsernameEnd
********
********
3rd Start CallgetUsername
BeanMyScope >>>>>>>>> get:scopedTarget.user
---------EstablishUserobjectcom.javacode2018.lesson002.demo18.test3.User@27ff5d15
76d0e86f-8331-4303-aac7-4acce0b258b8
Third callgetUsernameEnd
********
From the first two lines of the output, you can see that:
-
When context.getBean(User.class) was called to retrieve bean s from the container, the User constructor was 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.
The subsequent log output shows that the BeanMyScope#get method and User constructor are automatically called internally each time the user.getUsername method is called.
From the example above, you can see that when proxyMode=ScopedProxyMode.TARGET_is in a custom cope CLASS creates a proxy object for this bean, and any method that calls the proxy object will call the get method in the custom Scope implementation class (BeanMyScope above) to retrieve the bean object.
Dynamic Refresh @Value Implementation
We can then use this feature described above to dynamically refresh @Value, which enables a custom cope that supports @Value annotation auto-refreshing. Classes requiring @Value annotation auto-refreshing can annotate this custom annotation when the configuration is modified and any method of these beans is called. Let spring restart the initialization of this bean, this idea can be achieved, 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
}
Classes labeled with the @RefreshScope annotation are required to support dynamic refresh of the @Value configuration
@1: This is the key place, using ScopedProxyMode.TARGET_CLASS
Resolution class corresponding to this custom Scope
Several unrelated methods have been removed from the following classes 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();
//Come a map to cache bean s
private ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(); //@1
private BeanRefreshScope() {
}
public static BeanRefreshScope getInstance() {
return INSTANCE;
}
/**
* Clean up the 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 get method above gets from the beanMap first, gets no getObject that calls the objectFactory and lets spring create an instance of the bean, then throws it into the beanMap
The clean method above cleans up all currently cached beans in the beanMap
Come to the mail configuration class and use the @Value annotation to inject the configuration, which has a custom @RefreshScope scope
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: Used a custom scope @RefreshScope
@2: inject a value of mail.username to one through @Value
Override the toString method to see what happens when you test it.
Another normal bean, MailConfig injected inside
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 simple, rewriting the toString method to see what happens when you test it.
Come a class to get 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;
}
}
Come up with a spring configuration class and scan to load the components above
package com.javacode2018.lesson002.demo18.test4;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class MainConfig4 {
}
To a tool class
There are two 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 changes to all configuration information in the database
*/
public static void updateDbConfig(AbstractApplicationContext context) {
//Update mailPropertySource configuration information in context
refreshMailPropertySource(context);
//Empty the cache of all bean s in the BeanRefreshScope
BeanRefreshScope.getInstance().clean();
}
public static void refreshMailPropertySource(AbstractApplicationContext context) {
Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();
//Drop it in 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 a method that needs to be called when modifying a configuration in db, with two lines of code in the method and the first line calling the refreshMailPropertySource method to modify the configuration information of messages in the container
BeanRefreshScope.getInstance().clean() is used to clear all cached beans in BeanRefreshScope. When any method of calling a bean is called, the spring container is restarted to create the beans. When the spring container recreates the beans, the @Value information is resolved, and the mail configuration information in the container is new, so the @Value injected information is also new.
Come to 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 mail Configuration to Environment
RefreshConfigUtil.refreshMailPropertySource(context);
context.refresh();
MailService mailService = context.getBean(MailService.class);
System.out.println("Configuration 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 configuration effects for 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 to output mailService information
@2: Loop 3 times, first simulate updating configuration information in db through @3 internally, then output mailService information
See the effect when you witness a miracle
Configuration not updated,output3second
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'}}
simulation3Secondary update configuration effect
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 MailService output above is 6 times, the first 3 times the username value is the same, and the last 3 times the username value is different, indicating that the configuration modification has worked.
Summary
The key to the dynamic @Value implementation is the proxyMode parameter in @Scope, with a value of ScopedProxyMode.DEFAULT, which generates a proxy through which to implement the @Value dynamic refresh effect, which is the key.
Interesting to see is the @RefreshScope annotation source in springboot, which is similar to our custom @RefreshScope implementation above.
summary
We have all worked out three questions in this interview. Hope you have also mastered them. Welcome to leave a message and exchange with me if you have any questions!
Case Source
https://gitee.com/javacode2018/spring-series
All the case code for Pedestrian java will be put on this one in the future. watch for everyone to keep an eye on the dynamics.
Source: https://mp.weixin.qq.com/s?__ Biz=MzA5MTkxMDQ4MQ==&mid=2648934401&idx=1&sn=98e726ec9adda6d40663f624705ba2e4&chksm=8862103fbf159981183abef03b4774ab1dfd990a203a183efb8d118455ee4b477dc6cba50d&token=636643900&lang=zh_ CN&scene=21#wechat_ Redirect