It's too cruel. During the interview during the epidemic, one question cut me 5000

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

  1. @Usage of Value

  2. @Value data source

  3. @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:

  1. When calling context.getBean(User.class) to get the bean from the container, the User constructor is not called to create the User object

  2. 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

Keywords: Java Spring Back-end

Added by mediabob on Wed, 17 Nov 2021 12:39:35 +0200