SpringBoot --- Principles

1. Automatic configuration

1.1, bean loading method

1.1.1. Method 1: configuration file + <bean/>label

The loading method of the most basic bean can actually directly hit the core idea of ​​spring control bean, which is to provide the class name, and then spring can manage it. So the first way is to give the class name of the bean and load it into a class through the reflection mechanism.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--xml方式声明自己开发的bean-->
    <bean id="cat" class="Cat"/>
    <bean class="Dog"/>

    <!--xml方式声明第三方开发的bean-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/>
    <bean class="com.alibaba.druid.pool.DruidDataSource"/>
    <bean class="com.alibaba.druid.pool.DruidDataSource"/>
</beans>

1.1.2. Method 2: Configuration file scanning + annotation definition bean

​ Because the first method needs to write all the beans controlled by spring in the xml file, it is very unfriendly to programmers, so there is a second method. Which class is to be loaded into a bean under the control of spring, just add an annotation on this class, and you can also give a bean name (id) by the way. The annotations that can be used here are @Component and three derived annotations @Service, @Controller, @Repository.

@Component("tom")
public class Cat {
    
    
}
@Service
public class Mouse {
    
    
}

​ Of course, since we cannot add the above four annotations to the technical source code provided by the third party, when you need to load a bean developed by a third party, you can use the following method to define annotated beans. @Bean is defined above a method, and the return value of the current method can be handed over to spring for control. Remember that the class of this method must be defined in the class modified by @Component.

@Component
public class DbConfig {
    
    
    @Bean
    public DruidDataSource dataSource(){
    
    
        DruidDataSource ds = new DruidDataSource();
        return ds;
    }
}

​ The above is only the declaration of the bean, and spring does not perceive these things. To make spring aware of these classes, spring must be set up to check these classes to see if they are tagged. You can set spring to check which packages through the following xml configuration, and if you find the corresponding annotations, bring the corresponding class into the scope of spring control and declare it as a bean.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
    ">
    <!--指定扫描加载bean的位置-->
    <context:component-scan base-package="com.test.bean,com.test.config"/>
</beans>

​ Method 2 The way of declaring beans is the more common way of declaring beans in enterprises at present, but it also has disadvantages. In the first method, through a configuration file, you can check how many or how many beans are defined in the current spring environment, but in the second method, there is no place to check the overall information. Only when the program is running can you perceive how many beans are loaded. beans.

1.1.3, Method 3: Annotation method to declare configuration class

​ Method 2 has perfectly simplified the bean declaration, and there is no need to write a lot of configuration information in the future. Carefully observe the xml configuration file, and you will find that there is only the sentence "scan package" left in this file, so someone proposed to use java class to replace this fixed-format configuration, so the following format appeared. Strictly speaking, it cannot be regarded as a brand-new method, but since this form of development is the mainstream form in enterprise-level development, it is independently made into a method.

​ Define a class and use @ComponentScan to replace the package scanning action in the original xml configuration. In fact, the functions are basically the same.

@ComponentScan({
    
    "com.test.bean","com.test.config"})
public class SpringConfig3 {
    
    
    @Bean
    public DogFactoryBean dog(){
    
    
        return new DogFactoryBean();
    }
}

1.1.3.1, using the FactroyBean interface

​ Add a little knowledge, spring provides an interface FactoryBean, which can also be used to declare beans, but the object created by the class that implements the FactoryBean interface is not the object of the current class, but the object of the generic type specified by the FactoryBean interface. As listed below, the created bean is not DogFactoryBean, but Dog. what's the function? You can do some things before the object is initialized. The comment position in the following example is to allow you to expand other things to do.

public class DogFactoryBean implements FactoryBean<Dog> {
    
    
    @Override
    public Dog getObject() throws Exception {
    
    
        Dog d = new Dog();
        //.........
        return d;
    }
    @Override
    public Class<?> getObjectType() {
    
    
        return Dog.class;
    }
    @Override
    public boolean isSingleton() {
    
    
        return true;
    }
}

​ Some people say, isn’t it enough to write the code in the comment into the Dog’s construction method? Why go around so hard, write a class, and implement the interface, how troublesome. It's really different. You can understand that Dog is a particularly clean model that is stripped after abstraction, but a series of initialization actions must be performed in actual use. It's just that the initialization action is different depending on the situation. If you write in Dog, perhaps the initialization action A cannot meet your needs at present, and you will have to make a DogB solution at this time. Then, there is no more, you have to make two Dog classes. At that time, using the FactoryBean interface could perfectly solve this problem.

​Usually, classes that implement the FactoryBean interface are loaded in the form of @Bean. Of course, you can also use @Component to declare DogFactoryBean, as long as it is scanned and loaded, but this format loading always feels weird and not very directional clear.

@ComponentScan({
    
    "com.test.bean","com.test.config"})
public class SpringConfig3 {
    
    
    @Bean
    public DogFactoryBean dog(){
    
    
        return new DogFactoryBean();
    }
}

1.1.3.2. Annotation format imports beans configured in XML format

​ Add a little knowledge, because most of the systems developed in the early days use the form of xml to configure beans, and the current enterprise-level development basically does not use this mode. But if you are very lucky and need to carry out secondary development based on the previous system, it will be embarrassing. The newly developed annotation format is the xml format that was previously developed. At this time, it is not for you to choose which mode to use, but to use both at the same time. Spring provides an annotation to solve this problem, @ImportResource, just write the name of the xml configuration file to be integrated directly on the configuration class, which is considered a compatibility solution and has no practical significance.

@Configuration
@ImportResource("applicationContext1.xml")
public class SpringConfig32 {
    
    
}

1.1.3.3, proxyBeanMethods attribute

​ The @Configuration annotation was used in the previous example. When we use AnnotationConfigApplicationContext to load the configuration class, the configuration class does not need to add this annotation. But this annotation has a more powerful function, which can guarantee the uniqueness of the bean created by the method in the configuration class. Just set the proxyBeanMethods attribute value to true for the @Configuration annotation. Since the default value of this attribute is true, it is rarely seen explicitly written, unless you want to give up this function.

@Configuration(proxyBeanMethods = true)
public class SpringConfig33 {
    
    
    @Bean
    public Cat cat(){
    
    
        return new Cat();
    }
}

​ When the above cat method is called through the container, the same object is obtained. Note that you must use the spring container object to call this method to maintain the uniqueness of the bean. This feature is used in many low-level source codes.

public class App33 {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig33.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
    
    
            System.out.println(name);
        }
        System.out.println("-------------------------");
        SpringConfig33 springConfig33 = ctx.getBean("springConfig33", SpringConfig33.class);
        System.out.println(springConfig33.cat());
        System.out.println(springConfig33.cat());
        System.out.println(springConfig33.cat());
    }
}

1.1.4, method 4: use @Import annotation to inject beans

​ Loading beans by scanning is a common bean loading method in enterprise-level development, but because scanning can not only load what you want, but also load all kinds of messy things, in case there is no Good control outweighs the gain.

Some people will wonder, what's the problem? For example, you scan the com.test.service package, and later scan the com.test.dao package because of business needs. You find that there are only two packages under the com.test package, service and dao. .test will do. But I never expected that ten days later, you added an external dependency package, which also contained the com.

​ So we need a precision-guided loading method, and using the @Import annotation can solve your problem. It can load everything, just write the .class corresponding to the loaded class in the parameter of the annotation. Some people think that it is more troublesome to write by hand, and it is better to scan. Yes, but he can specify the loading. A good naming convention and @ComponentScan can solve many problems, but the @Import annotation has its important application scenarios. Have you ever thought about what if the bean you want to load is not decorated with @Component? There is no solution now, and @Import does not need to consider this issue.

@Import({
    
    Dog.class,DbConfig.class})
public class SpringConfig4 {
    
    
}

Inject the configuration class using the @Import annotation

In addition to loading beans, configuration classes can also be loaded using the @Import annotation. In fact, they are essentially the same.

@Import(DogFactoryBean.class)
public class SpringConfig4 {
    
    
}

1.1.5. Method 5: Register beans in programming form

​ The methods of loading beans described above are all bean loading during the container startup phase. The following method is more special, and the beans can be manually loaded after the container initialization is completed. In this way, programmatic control of bean loading can be achieved.

public class App5 {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        //上下文容器对象已经初始化完毕后,手工加载bean
        ctx.register(Mouse.class);
    }
}

​ In fact, there are quite a lot of pitfalls in this method. For example, there are already certain types of beans in the container, will they be overwritten when they are loaded again? These are all questions to think about and pay attention to.

public class App5 {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        //上下文容器对象已经初始化完毕后,手工加载bean
        ctx.registerBean("tom", Cat.class,0);
        ctx.registerBean("tom", Cat.class,1);
        ctx.registerBean("tom", Cat.class,2);
        System.out.println(ctx.getBean(Cat.class));
    }
}

1.1.6. Method 6: Import a class that implements the ImportSelector interface

In the five ways, we feel that the loading of beans can be controlled programmatically, and the loading control of beans can be realized by adding an if statement. But after all, the bean loading control is realized after the container is initialized, so can it be controlled during the container initialization process? The answer is must. The class that implements the ImportSelector interface can set the full path class name of the loaded bean. Remember, as long as you can program, you can judge. Being able to judge means that you can control the running direction of the program, and then control everything.

​ Now there is another way to control bean loading, or the way to select beans.

public class MyImportSelector implements ImportSelector {
    
    
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
    
    
        //各种条件的判定,判定完毕后,决定是否装载指定的bean
        boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Configuration");
        if(flag){
    
    
            return new String[]{
    
    "com.test.bean.Dog"};
        }
        return new String[]{
    
    "com.test.bean.Cat"};
    }
}

1.1.7. Method 7: Import the class that implements the ImportBeanDefinitionRegistrar interface

​ Method 6 provides the form of controlling bean loading by giving the full path and class name of the given class. If you are familiar with the bean loading principle of spring, you know that bean loading is not a simple object. Spring defines A thing called BeanDefinition, it is the core that controls bean initialization loading. Several methods are given in the BeanDefinition interface to control the relevant properties of the bean. To put it simply, whether the created object is a singleton or a non-singleton, you can control this by defining the scope attribute in the BeanDefinition. If you feel that Method 6 does not give you enough control over beans, then Method 7 is worth having. We can define a bean by defining a class and then implementing the ImportBeanDefinitionRegistrar interface, and it also allows you to have more fine-grained control over the initialization of the bean.

public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    
    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    
    
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}

1.1.8. Method 8: Import the class that implements the BeanDefinitionRegistryPostProcessor interface

​ The above seven methods all load or declare beans during container initialization, but there is a bug here. With so many ways, what if there is a conflict between them? Who can have the final say? This is a good question. When a certain type of bean is loaded in various ways one after another, before you fully understand the loading order of all the loading methods, you really don’t know who has the final say.

BeanDefinitionRegistryPostProcessor in spring, look at the name, BeanDefinition means bean definition, Registry registration, Post post, Processor processor, full name bean definition post processor, what do you do? After all bean registrations have been tossed, it closes the last step. To put it bluntly, it has the final say, and it is the last one to run.

public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
    
    
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    
    
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl4.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}

Summarize

  1. The bean definition gradually evolved from the previous xml configuration to the annotation configuration. The essence is the same, and the object is created after loading the class name through the reflection mechanism. The object is the bean controlled by spring
  2. The @Import annotation can specify to load a certain class as a bean controlled by spring. If the loaded class also has @Bean-related definitions, it will be loaded together
  3. Spring has opened up several programmable control bean initialization methods. Through branch statements, the fixed loading bean can be converted into a choice of whether the bean is loaded or which bean is selected to be loaded.

1.2, bean loading control

It is impossible to perform saturated loading of beans in the spring container in enterprise-level development. What is saturated loading, that is, whether it is used or not, all are loaded. For example, if there are 20,000 classes in jdk, it is obviously unreasonable to load 20,000 beans, because you will not use most of them at all. What is the reasonable loading method? It must be loaded by necessity, that is, what is loaded with. Continuing to think, which beans are loaded are usually affected by what? The easiest thing to think about is what technology you want to use, just load the corresponding bean. What technology means? It is to load the class of the corresponding technology. So in the spring container, it is a common operation to control the loading of certain beans by determining whether a certain class is loaded. The following example gives the corresponding code implementation. In fact, the idea is very simple. First, judge whether the full path name of a class can be loaded successfully. If the loading is successful, it means that there is such a class. Then do a specific job, otherwise, do other jobs. .

public class MyImportSelector implements ImportSelector {
    
    
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    
    
        try {
    
    
            Class<?> clazz = Class.forName("com.test.bean.Mouse");
            if(clazz != null) {
    
    
                return new String[]{
    
    "com.test.bean.Cat"};
            }
        } catch (ClassNotFoundException e) {
    
    
			//e.printStackTrace();
            return new String[0];
        }
        return null;
    }
}

​ Through the above analysis, we can see that such operations will become common operations in enterprise-level development, so springboot will encapsulate these common operations for us once.

​ The following example uses the @ConditionalOnClass annotation to load the corresponding bean when the com.test.bean.Wolf class is loaded in the virtual machine. Compare the code above with the code below, do you feel refreshed? In fact, there are many such annotations.

@Bean
@ConditionalOnClass(name = "com.test.bean.Wolf")
public Cat tom(){
    
    
    return new Cat();
}

​ The @ConditionalOnMissingClass annotation controls that the specified class is not loaded in the virtual machine before loading the corresponding bean.

@Bean
@ConditionalOnMissingClass("com.test.bean.Dog")
public Cat tom(){
    
    
    return new Cat();
}

​ This kind of condition can also be done in a logical relationship. Writing 2 means that both conditions are true, and writing more than one means that multiple conditions are true.


@Bean
@ConditionalOnClass(name = "com.test.bean.Wolf")
@ConditionalOnMissingClass("com.test.bean.Mouse")
public Cat tom(){
    
    
    return new Cat();
}

​ In addition to judging whether to load a class, you can also judge the current container type. The following example is to judge whether the current container environment is a web environment.

@Bean
@ConditionalOnWebApplication
public Cat tom(){
    
    
    return new Cat();
}

​ The following is to determine whether the container environment is a non-web environment.

@Bean
@ConditionalOnNotWebApplication
public Cat tom(){
    
    
    return new Cat();
}

​ Of course, you can also determine whether a bean with a specified name is loaded. For example, the bean corresponding to jdbcTemplate is already provided in the current container. Do you need to load a new bean of jdbcTemplate? It's not necessary anymore. Spring said, if you write it yourself, I won't worry about it for you. If you haven't written it, I will provide it to you. If you don't have it, I will provide it to you, if you have it, use your own,

@Bean
@ConditionalOnBean(name="jerry")
public Cat tom(){
    
    
    return new Cat();
}

​ The following is to determine whether the mysql driver class is currently loaded. If it is loaded, I will create a Druid data source object for you, perfect!

public class SpringConfig {
    
    
    @Bean
    @ConditionalOnClass(name="com.mysql.jdbc.Driver")
    public DruidDataSource dataSource(){
    
    
        return new DruidDataSource();
    }
}

​ Among them, there are many bean loading control annotations of springboot, which will not be listed here. The most commonly used judgment condition is to control according to whether the class is loaded.

Summarize

  1. springboot defines several conditional setting annotations to control bean loading, changing from spring fixed loading of beans to selective loading of beans according to the situation

1.3, bean dependency property configuration

When the bean is running, the developer may need to provide some setting values ​​when implementing the corresponding business logic, which are attributes. If the construction method is used to fix the parameters and the flexibility is insufficient, at this time, the knowledge related to the bean attribute configuration learned earlier can be used for flexible configuration. First, through the yml configuration file, set the configuration information needed to run the bean.

cartoon:
  cat:
    name: "图多盖洛"
    age: 5
  mouse:
    name: "泰菲"
    age: 1

​ Then define a dedicated class that encapsulates attributes, load configuration attributes, and read attribute values ​​​​related to the corresponding prefix.

@ConfigurationProperties(prefix = "cartoon")
@Data
public class CartoonProperties {
    
    
    private Cat cat;
    private Mouse mouse;
}

​ Finally, inject the corresponding configuration at the location of use.

@EnableConfigurationProperties(CartoonProperties.class)
public class CartoonCatAndMouse{
    
    
    @Autowired
    private CartoonProperties cartoonProperties;
}

​ It is recommended to use @EnableConfigurationProperties to declare beans on business classes, so that when this class is not used, the dedicated property configuration class CartoonProperties will not be loaded for no reason, reducing the number of resources controlled by spring.

Summarize

  1. If the operation of the bean requires external setting values, it is recommended to encapsulate the setting values ​​into a dedicated attribute class * * * * Properties
  2. Set the attribute class to load the configuration information of the specified prefix
  3. Load beans by annotating @EnableConfigurationProperties where attribute classes need to be used, instead of defining beans directly on attribute configuration classes, reducing the number of resource loading, and do not saturate loading because of the need to load.

1.4. Principle of automatic configuration

​ What is automatic configuration? Simply put, springboot guesses what you are going to do based on the behavior of our developers, and then prepares all the beans you want to use. Sound amazing? In fact, it is very simple. How did springboot do it? Just look at what class you imported, and you will know what you want to do. Then load all the beans you may want to use (note that it is possible), and you can use them directly. Springboot has done all the work needed.

​ The significance of automatic configuration is to speed up the development efficiency, and load the beans that developers need to use when using a certain technology in advance according to the situation, so as to realize the effect of automatic configuration. Of course, developers may need to provide necessary parameters. For example, if you want to use mysql technology and import the coordinates of mysql, springboot will know that you need to perform database operations, and a series of beans related to database operations will be declared in advance for you. But you have to tell springboot which database you use, like what IP address, port, if you don't tell springboot, springboot will not be able to help you complete the work related to automatic configuration.

​ And this kind of thinking is actually obtained slowly in the daily development process according to the habits of developers. The whole process is divided into 2 stages:

​Phase 1: Preparation

  1. The developers of springboot first collect a large number of programming habits of Spring developers, organize the list of technologies frequently used by each program in the development process, and form a technology set A

  2. Collect the usage parameters of commonly used technologies ( Technology Set A ), no matter what common settings you use or what common settings I use, collect them all and sort them out, get the common settings of each technology in the development process, and form a setting set corresponding to each technology B

Phase 2: Loading phase

  1. springboot initializes the basic environment of the Spring container, reads the user's configuration information, loads user-defined beans and other imported coordinates, and forms an initialization environment

  2. Springboot loads all the technologies included in technology set A by default when SpringBoot starts. At this time, some of the things that must be loaded are invalid and useless.

  3. Springboot will agree on the conditions corresponding to starting this technology for each technology in technology set A , and set it to be loaded according to the conditions. Since the developer has imported some beans and other coordinates, that is, the initialization environment , it can be initialized according to this time. The environment is compared with the technology set A of springboot, which one matches which one is loaded

  4. Because some technologies cannot work without configuration, springboot starts to set B. It counts the most commonly used settings when developers in various industries in various countries use a certain technology, and then directly sets these settings as default values, and tells developers the current settings. I have already made a set for you. You want It can be used directly, which can reduce the workload of developers to configure parameters

  5. But the default configuration may not be able to solve the problem, so springboot opens the interface for modifying the setting set B , and developers can decide whether to override the default configuration according to their needs.

​ The above is just an idea. When it comes to the code implementation stage, it is necessary to think carefully about how to implement it. Suppose we want to implement the automatic configuration function by ourselves, what work do we have to do?

  • First specify a technology X, we intend to make technology X have the function of automatic configuration, this technology X can be any function, this technology belongs to the technology set A described above
public class CartoonCatAndMouse{
    
    
}
  • Then find out the common configuration Y in the process of using technology X, which belongs to the setting set B described above
cartoon:
  cat:
    name: "图多盖洛"
    age: 5
  mouse:
    name: "泰菲"
    age: 1
  • Design the corresponding yml configuration writing format for the commonly used configuration Y, and then define an attribute class to encapsulate the corresponding configuration attributes. This process is actually the bean dependency attribute management we did in the previous section, exactly the same
@ConfigurationProperties(prefix = "cartoon")
@Data
public class CartoonProperties {
    
    
    private Cat cat;
    private Mouse mouse;
}
  • Finally, make a configuration class. When this class is loaded, the corresponding functional bean can be initialized and loaded into the corresponding configuration.
@EnableConfigurationProperties(CartoonProperties.class)
public class CartoonCatAndMouse implements ApplicationContextAware {
    
    
    private CartoonProperties cartoonProperties;
}
  • Of course, you can also set the activation conditions for the current automatic configuration class, for example, use @CondtionOn* * * * to set the loading conditions for it
@ConditionalOnClass(name="org.springframework.data.redis.core.RedisOperations")
@EnableConfigurationProperties(CartoonProperties.class)
public class CartoonCatAndMouse implements ApplicationContextAware {
    
    
    private CartoonProperties cartoonProperties;
}

​ It has been done here, but I have encountered a brand new problem, how to make springboot load this class when it starts? If it is not loaded, all the conditional judgments we made and the attribute loading we made will be invalid. springboot opens a configuration entry for us, create a META-INF directory in the configuration directory, and create a spring.factories file, add settings in it, and explain which classes need to start automatic configuration.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.test.bean.CartoonCatAndMouse

​ In fact, this file does one thing, and the specified class is loaded through this configuration. Turning around, it is a normal bean loading, which is almost the same as loading beans in xml format at first, the format has just changed. What is the core of automatic configuration? Automatic configuration is actually a small ecology, which can be understood according to the following ideas:

  1. Auto-configuration is fundamentally a bean loading
  2. The control of bean loading conditions gives developers a feeling that automatic configuration is adaptive and can be judged according to the situation, but in fact it is the most common application of branch statements, which is the first veil that blinds our eyes
  3. When using a bean, if you don’t set an attribute, there will be a default value. If you don’t want to use the default value, you can set it yourself, that is, you can modify some or all parameters. It is still necessary to use branch statements to make judgments, which is the second veil that blinds our eyes
  4. The springboot technology prepares the technologies that a large number of developers may use in advance, and the conditions are also written. When you use it, you import a coordinate, and the corresponding technology can be used. In fact, it helps us write the spring.factories file in advance. Well, this is the third veil that blinds our eyes

Summarize

  1. When springboot starts, it first loads the org.springframework.boot.autoconfigure.EnableAutoConfiguration configuration item in the spring.factories file, and loads all the classes configured in it into beans
  2. When loading a bean, the class definition corresponding to the bean is set with a loading condition, so it is possible that the loading is successful, or the condition detection fails and the bean is not loaded
  3. For classes that can be loaded into beans normally, the corresponding configuration property class is usually initialized through the @EnableConfigurationProperties annotation and the corresponding configuration is loaded
  4. The configuration attribute class usually loads the configuration with the specified prefix through @ConfigurationProperties. Of course, these configurations usually have default values. If there is no default value, you are forced to use it after configuration

1.5. Change automatic configuration

Knowing the execution process of automatic configuration, you can do some advanced customization according to this automatic configuration process. For example, the system will load more than 100 automatic configuration technologies by default. If we manually intervene in this project first, is it feasible to disable automatic configuration? The answer must be yes. There are quite a few ways:

Method 1: Exclude specified automatic configuration classes through yaml configuration settings

spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration

Method 2: Exclude automatic configuration classes through annotation parameters

@EnableAutoConfiguration(excludeName = "",exclude = {
    
    })

Method 3: Exclude coordinates (the application area is narrow)

If the current automatic configuration contains more automatic configuration functions, it is a nesting doll effect. At this time, whether the automatic configuration is started can be managed through the control of the detection condition. For example, when the web program starts, the tomcat server will be automatically started, and the condition for loading the tomcat server can be invalidated by excluding the coordinates. But it needs to be reminded that if you exclude tomcat, remember to add another server that can run.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!--web起步依赖环境中,排除Tomcat起步依赖,匹配自动配置条件-->
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--添加Jetty起步依赖,匹配自动配置条件-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
</dependencies>

Summarize

  1. The automatic configuration of springboot is not necessarily running, you can intervene in the form of configuration whether to enable the corresponding automatic configuration function

Two, custom starter

2.1. Case: Record the number of independent IP visits by system visitors

The function of this case is to count the number of independent IP visits to the website, and continuously output the visit information in the background. The overall function is to output monitoring information (format: IP+number of visits) every 10 seconds in the background, and to collect statistics on the user's visit behavior when the user visits the website.

​ For example: Zhang San visited the website function 15 times, IP address: 192.168.0.135, Li Si visited the website function 20 times, IP address: 61.129.65.248. Then the following monitoring information is output in the background of the website, and this information is refreshed every 10 seconds.

         IP访问监控
+-----ip-address-----+--num--+
|     192.168.0.135  |   15  |
|     61.129.65.248  |   20  |
+--------------------+-------+

​ Before making specific production, make a specific analysis of the function

  1. Where is the data recorded

    The final recorded data is a string (IP address) corresponding to a number (number of visits). The data storage model that can be selected here can use the map model provided by java, that is, the key-value key-value pair model, or with key -value The storage technology of the key-value pair model, such as redis technology. In this case, map is used as the implementation solution, and interested partners can use redis as the solution.

  2. The running position of the statistics function, because every web request needs to be counted, so using an interceptor will be a better solution. In this case, an interceptor is used to achieve. However, in the early stage of production, first use the form of calling to test, and then change to the implementation plan of the interceptor after the function is completed.

  3. In order to improve the flexibility of statistical data display, configuration items are added for statistical functions. Output frequency, output data format, and statistical data display mode can all be adjusted through configuration.

    • Output frequency, default 10 seconds
    • Data characteristics: cumulative data/stage data, default cumulative data
    • Output format: verbose mode / minimalist mode

​ In the following production, it is divided into several steps. First complete the production of the most basic statistical functions, then develop statistical reports, then set up all the configurations, and finally implement the interceptor function, and the overall function is completed.

2.2, IP counting business function development (custom starter)

The ultimate effect of this function is to import a starter into an existing project, and the corresponding function will be added. If the corresponding starter is deleted, the function will disappear. It is required that the function should be completely decoupled from the original project. Therefore, it is necessary to develop an independent module and make corresponding functions.

Step 1: Create a new module and define business function classes

​ The production of functional classes is not complicated. Define a business class and declare a Map object to record the number of ip visits. The key is the ip address, and the value is the number of visits.

public class IpCountService {
    
    
    private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();
}

​Some friends may have doubts, if it is not set to static, how to share data every time it is requested? Remember, after the current class is loaded into a bean, it is a singleton object. All objects are singletons. There is no problem of multiple objects sharing variables.

Step 2: Make statistics function

​ Make the method corresponding to the statistical operation, and the number of records corresponding to the ip +1 after each visit. It needs to be handled according to the situation. If there is no data corresponding to the ip, add a new data, otherwise just modify the value of the corresponding key + 1

public class IpCountService {
    
    
    private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();
    public void count(){
    
    
        //每次调用当前操作,就记录当前访问的IP,然后累加访问次数
        //1.获取当前操作的IP地址
        String ip = null;
        //2.根据IP地址从Map取值,并递增
        Integer count = ipCountMap.get(ip);
        if(count == null){
    
    
            ipCountMap.put(ip,1);
        }else{
    
    
            ipCountMap.put(ip,count + 1);
        }
    }
}

​ Because the current function is finally imported into other projects, and the project that imports the current function is a web project, which can directly obtain the request object from the container, so the operation of obtaining the IP address can obtain the request object through automatic assembly, and then obtain the corresponding Access IP address.

public class IpCountService {
    
    
    private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();
    @Autowired
    //当前的request对象的注入工作由使用当前starter的工程提供自动装配
    private HttpServletRequest httpServletRequest;
    public void count(){
    
    
        //每次调用当前操作,就记录当前访问的IP,然后累加访问次数
        //1.获取当前操作的IP地址
        String ip = httpServletRequest.getRemoteAddr();
        //2.根据IP地址从Map取值,并递增
        Integer count = ipCountMap.get(ip);
        if(count == null){
    
    
            ipCountMap.put(ip,1);
        }else{
    
    
            ipCountMap.put(ip,count + 1);
        }
    }
}

Step 3: Define the auto-configuration class

​ The effect we need to achieve is to enable this function by importing the current module. Therefore, to use automatic configuration to realize automatic loading of functions, it is necessary to develop an automatic configuration class to load the current function when starting the project.

public class IpAutoConfiguration {
    
    
    @Bean
    public IpCountService ipCountService(){
    
    
        return new IpCountService();
    }
}

​ The automatic configuration class needs to be configured in the spring.factories file to run automatically.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.test.autoconfig.IpAutoConfiguration

Step 4: Simulate the call in the original project and test the function

​ Import the currently developed starter into the original calling project

<dependency>
    <groupId>cn.test</groupId>
    <artifactId>ip_spring_boot_starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

​ It is recommended to choose the function that is convenient to call for testing. It is recommended to use pagination operation. Of course, you can also change other function locations for testing.

@RestController
@RequestMapping("/books")
public class BookController {
    
    

    @Autowired
    private IpCountService ipCountService;
    
    @GetMapping("{currentPage}/{pageSize}")
    public R getPage(@PathVariable int currentPage,@PathVariable int pageSize,Book book){
    
    
        ipCountService.count();
        IPage<Book> page = bookService.getPage(currentPage, pageSize,book);
        if( currentPage > page.getPages()){
    
    
            page = bookService.getPage((int)page.getPages(), pageSize,book);
        }
        return new R(true, page);
    }
}

Kind tips

​ Since the currently produced functions need to import coordinates at the corresponding call positions, it is necessary to ensure that the warehouse has the currently developed functions, so each time the original code is modified, it needs to be recompiled and installed into the warehouse. In order to prevent problems, it is recommended to clean and then install before each installation to ensure that resources are updated. Remember remember! !

current effect

​ After each paging operation is called, the currently accessed IP address can be output on the console. This function can be tested by adding logs or output statements in the count operation.

2.2.1. Scheduled task report development

At present, the access data has been recorded in the business function class, but the monitoring information has not been output to the console. Since monitoring information needs to be output every 10 seconds, a timer function is required. You can choose the third-party technology Quartz to implement, or you can choose Spring's built-in task to complete this function. Here, Spring's task is selected as the implementation solution.

Step 1: Enable the scheduled task function

​ The scheduled task function needs to be set in the general configuration of the current function. Combined with the existing business settings, a more reasonable position is to set it in the automatic configuration class. Loading the automatic configuration class enables the scheduled task function.

@EnableScheduling
public class IpAutoConfiguration {
    
    
    @Bean
    public IpCountService ipCountService(){
    
    
        return new IpCountService();
    }
}

Step 2: Create a function to display statistical data

​ Define the operation print() to display the statistical function, and set up a scheduled task. The current setting is to run the statistical data every 5 seconds.

public class IpCountService {
    
    
    private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();
    @Scheduled(cron = "0/5 * * * * ?")
    public void print(){
    
    
        System.out.println("         IP访问监控");
        System.out.println("+-----ip-address-----+--num--+");
        for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
    
    
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(String.format("|%18s  |%5d  |",key,value));
        }
        System.out.println("+--------------------+-------+");
      }
}

​ Among them, the splicing of display information about statistical reports can be performed in various forms. Here, the formatting string operation in the String class is used. Learners can adjust the implementation plan according to their own preferences.

Kind tips

​ Clean and then install before running the effect every time, remember to remember! !

current effect

​ After each paging operation is called, you can see the statistical data on the console, and the basic functions have been developed so far.

2.2.2. Use attribute configuration to set function parameters

Since the information format displayed in the current report is fixed, in order to improve the flexibility of report information display, it is necessary to set parameters through the yml file to control the display format of the report.

Step 1: Define the parameter format

​ Set 3 attributes, which are used to control the display cycle (cycle), whether the stage data is cleared (cycleReset), and the data display format (model)

tools:
  ip:
    cycle: 10
    cycleReset: false
    model: "detail"

Step 2: Define the attribute class of the encapsulation parameters, and read the configuration parameters

​ In order to prevent too many types of parameters defined by the project team, resulting in conflicts, usually setting attribute prefixes will use at least two levels of attributes as prefixes to distinguish.

​ The log output mode is to select one of several category options. For this kind of classified data, it is recommended to create an enumeration to define the classified data. Of course, strings can also be used.

@ConfigurationProperties(prefix = "tools.ip")
public class IpProperties {
    
    
    /**
     * 日志显示周期
     */
    private Long cycle = 5L;
    /**
     * 是否周期内重置数据
     */
    private Boolean cycleReset = false;
    /**
     * 日志输出模式  detail:详细模式  simple:极简模式
     */
    private String model = LogModel.DETAIL.value;
    public enum LogModel{
    
    
        DETAIL("detail"),
        SIMPLE("simple");
        private String value;
        LogModel(String value) {
    
    
            this.value = value;
        }
        public String getValue() {
    
    
            return value;
        }
    }
}

Step 3: Load the attribute class

@EnableScheduling
@EnableConfigurationProperties(IpProperties.class)
public class IpAutoConfiguration {
    
    
    @Bean
    public IpCountService ipCountService(){
    
    
        return new IpCountService();
    }
}

Step 4: Apply Configuration Properties

​ In the functional class of the application configuration property, use automatic assembly to load the corresponding configuration bean, and then use the configuration information for branch processing.

​ Note: The function of clearing data must be run after output, otherwise the data checked each time will be blank data.

public class IpCountService {
    
    
    private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();
    @Autowired
    private IpProperties ipProperties;
    @Scheduled(cron = "0/5 * * * * ?")
    public void print(){
    
    
        if(ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())){
    
    
            System.out.println("         IP访问监控");
            System.out.println("+-----ip-address-----+--num--+");
            for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
    
    
                String key = entry.getKey();
                Integer value = entry.getValue();
                System.out.println(String.format("|%18s  |%5d  |",key,value));
            }
            System.out.println("+--------------------+-------+");
        }else if(ipProperties.getModel().equals(IpProperties.LogModel.SIMPLE.getValue())){
    
    
            System.out.println("     IP访问监控");
            System.out.println("+-----ip-address-----+");
            for (String key: ipCountMap.keySet()) {
    
    
                System.out.println(String.format("|%18s  |",key));
            }
            System.out.println("+--------------------+");
        }
        //阶段内统计数据归零
        if(ipProperties.getCycleReset()){
    
    
            ipCountMap.clear();
        }
    }
}

Kind tips

​ Clean and then install before running the effect every time, remember to remember! !

current effect

​ On the web terminal, the format of statistical information can be controlled by controlling the configuration parameters in the yml file. But the data show that the cycle has not been controlled.

2.2.3. Use attribute configuration to set timer parameters

​ When using the display cycle data in the property configuration, I encountered some problems. Since the configuration data cannot be directly used on the @Scheduled annotation, the function corresponding to the @EnableConfigurationProperties annotation is abandoned and changed to the most original bean definition format.

Step 1: @Scheduled annotation uses #{} to read bean property value

​ Here read the cycle attribute value of the bean whose bean name is ipProperties

@Scheduled(cron = "0/#{ipProperties.cycle} * * * * ?")
public void print(){
    
    
}

Step 2: The attribute class defines the bean and specifies the access name of the bean

​ If the access name of the bean is not set here, spring will use its own naming generator to generate the long name of the bean, and the property cannot be read

@Component("ipProperties")
@ConfigurationProperties(prefix = "tools.ip")
public class IpProperties {
    
    
}

Step 3: Abandon the function corresponding to the @EnableConfigurationProperties annotation, and load the configuration property class in the form of importing beans instead

@EnableScheduling
//@EnableConfigurationProperties(IpProperties.class)
@Import(IpProperties.class)
public class IpAutoConfiguration {
    
    
    @Bean
    public IpCountService ipCountService(){
    
    
        return new IpCountService();
    }
}

Kind tips

​ Clean and then install before running the effect every time, remember to remember! !

current effect

​ On the web terminal, you can control the display cycle of statistical information by controlling the configuration parameters in the yml file

2.2.4. Interceptor development

​ The basic functions have basically been produced, and the development of the interceptor will be carried out below. When developing, first make it in the web project, and then move all the functions into the starter module

Step 1: Develop an interceptor

​ Use automatic assembly to load the business class of the statistical function, and call the corresponding function in the interceptor

public class IpCountInterceptor implements HandlerInterceptor {
    
    

    @Autowired
    private IpCountService ipCountService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        ipCountService.count();
        return true;
    }
}

Step 2: Configure the interceptor

​ Configure the mvc interceptor and set the request path corresponding to the interception. All requests are intercepted here, and users can set the requests to be intercepted according to their needs. Even the properties in IpCountProperties can be loaded here, and the requests intercepted by the interceptor can be set through the configuration.

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
    
    
   
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(ipCountInterceptor()).addPathPatterns("/**");
    }
    
    @Bean
    public IpCountInterceptor ipCountInterceptor(){
    
    
        return new IpCountInterceptor();
    }
}

Kind tips

​ Clean and then install before running the effect every time, remember to remember! !

current effect

​ After importing the corresponding starter on the web terminal, the function is enabled, and the function disappears after removing the coordinates, realizing the effect of customizing the starter.

2.3. Complete functionality——Enable the yml prompt function

When we use the configuration properties of springboot, we can see the prompts, especially after importing the corresponding starter, there will also be corresponding prompts. But now our starter does not have a corresponding prompt function, and this setting is very unfriendly. This section solves the problem of how to enable the configuration prompt for the custom starter function.

​ springboot provides a dedicated tool to achieve this function, only need to import the following coordinates.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

​ After the program is compiled, a corresponding prompt file will be generated in the META-INF directory, and then copy the generated file to the self-developed META-INF directory and edit it. Open the generated file, you can see the following information. The groups attribute defines the overall description of the prompt information of the current configuration, which attribute encapsulation class the current configuration belongs to, and the properties attribute describes the specific settings of each attribute in the current configuration, including name, type, description, default value and other information. The hints attribute is blank by default and is not set. The hints attribute can refer to the production in the springboot source code to set the special prompt information for the current property encapsulation class. In the following example, two optional prompt information are set for the log output mode attribute model.

{
    
    
  "groups": [
    {
    
    
      "name": "tools.ip",
      "type": "cn.angyan.properties.IpProperties",
      "sourceType": "cn.angyan.properties.IpProperties"
    }
  ],
  "properties": [
    {
    
    
      "name": "tools.ip.cycle",
      "type": "java.lang.Long",
      "description": "日志显示周期",
      "sourceType": "cn.angyan.properties.IpProperties",
      "defaultValue": 5
    },
    {
    
    
      "name": "tools.ip.cycle-reset",
      "type": "java.lang.Boolean",
      "description": "是否周期内重置数据",
      "sourceType": "cn.angyan.properties.IpProperties",
      "defaultValue": false
    },
    {
    
    
      "name": "tools.ip.model",
      "type": "java.lang.String",
      "description": "日志输出模式  detail:详细模式  simple:极简模式",
      "sourceType": "cn.angyan.properties.IpProperties"
    }
  ],
  "hints": [
    {
    
    
      "name": "tools.ip.model",
      "values": [
        {
    
    
          "value": "detail",
          "description": "详细模式."
        },
        {
    
    
          "value": "simple",
          "description": "极简模式."
        }
      ]
    }
  ]
}

Summarize

  1. A custom starter is actually an independent functional module. The core technology is to use the effect of automatic configuration to load the corresponding function after loading the module.
  2. Usually, enough conditional control is added to the automatic configuration function of the custom starter, and it will not be 100% loaded to the effect of the function
  3. In this example, map is used to save data. If the redis scheme is used instead, the starter corresponding to redis must be imported in the starter development module.
  4. For configuration properties, the prompt function must be enabled, otherwise users will not be able to perceive how the configuration should be written

3. Core principles

3.1, SpringBoot startup process

  • Initialize various properties and load them into objects

    • Read environment properties (Environment)
    • System configuration (spring.factories)
    • Parameters (Arguments, application.properties)
  • Create a Spring container object ApplicationContext and load various configurations

  • Before the container is created, use the listener mechanism to respond to the needs of loading and updating data at different stages

  • Various functions are added during container initialization, such as statistical time, output logs, etc.

The following describes the specific things to do in each link when the springboot program starts through the code flow.

Springboot30StartupApplication10->SpringApplication.run(Springboot30StartupApplication.class, args);
    SpringApplication1332->return run(new Class<?>[] {
    
     primarySource }, args);
        SpringApplication1343->return new SpringApplication(primarySources).run(args);
            SpringApplication1343->SpringApplication(primarySources)
            # 加载各种配置信息,初始化各种配置对象
                SpringApplication266->this(null, primarySources);
                    SpringApplication280->public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
						# 初始化资源加载器
                        SpringApplication281->this.resourceLoader = resourceLoader;
						# 初始化配置类的类名信息(格式转换)
                        SpringApplication283->this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
                        # 确认当前容器加载的类型
                        SpringApplication284->this.webApplicationType = WebApplicationType.deduceFromClasspath();
                        # 获取系统配置引导信息
                        SpringApplication285->this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
                        # 获取ApplicationContextInitializer.class对应的实例
                        SpringApplication286->setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
                        # 初始化监听器,对初始化过程及运行过程进行干预
                        SpringApplication287->setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
                        # 初始化了引导类类名信息,备用
                        SpringApplication288->this.mainApplicationClass = deduceMainApplicationClass();                       
            # 初始化容器,得到ApplicationContext对象
			SpringApplication1343->new SpringApplication(primarySources).run(args)
				 # 设置计时器
                SpringApplication323->StopWatch stopWatch = new StopWatch();
				 # 计时开始
                SpringApplication324->stopWatch.start();
				# 系统引导信息对应的上下文对象
                SpringApplication325->DefaultBootstrapContext bootstrapContext = createBootstrapContext();
                # 模拟输入输出信号,避免出现因缺少外设导致的信号传输失败,进而引发错误(模拟显示器,键盘,鼠标...SpringApplication327->configureHeadlessProperty();
                    java.awt.headless=true
				# 获取当前注册的所有监听器	
                SpringApplication328->SpringApplicationRunListeners listeners = getRunListeners(args);
                # 监听器执行了对应的操作步骤
                SpringApplication329->listeners.starting(bootstrapContext, this.mainApplicationClass);
				# 获取参数
                SpringApplication331->ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                # 将前期读取的数据加载成了一个环境对象,用来描述信息
                SpringApplication333->ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
                # 做了一个配置,备用
                SpringApplication333->configureIgnoreBeanInfo(environment);
                # 初始化logo
                SpringApplication334->Banner printedBanner = printBanner(environment);
                # 创建容器对象,根据前期配置的容器类型进行判定并创建
                SpringApplication335->context = createApplicationContext();
                # 设置启动模式
                SpringApplication363->context.setApplicationStartup(this.applicationStartup);
                # 对容器进行设置,参数来源于前期的设定
                SpringApplication337->prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
                 # 刷新容器环境
                SpringApplication338->refreshContext(context);
               # 刷新完毕后做后处理
                SpringApplication339->afterRefresh(context, applicationArguments);
                # 计时结束
                SpringApplication340->stopWatch.stop();
                # 判定是否记录启动时间的日志
                SpringApplication341->if (this.logStartupInfo) {
    
    
                # 创建日志对应的对象,输出日志信息,包含启动时间
                SpringApplication342->    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                # 监听器执行了对应的操作步骤
                SpringApplication344->listeners.started(context);
                # 调用运行器
                SpringApplication345->callRunners(context, applicationArguments);
                # 监听器执行了对应的操作步骤
                SpringApplication353->listeners.running(context);

3.2. Listener

Since there are a large number of process stages in the springboot startup process, if you design an interface, you need to design more than ten standard interfaces, which is not friendly to developers. At the same time, the overall process management is scattered, and more than ten processes are independent. The management is difficult and the process is too loose. So how does springboot solve this problem? It uses one of the most primitive design patterns to solve this problem, which is the listener mode, and uses listeners to solve this problem.

​ springboot compares its own startup process to a big event, which is composed of several small events. For example:

  • org.springframework.boot.context.event.ApplicationStartingEvent
    • Application starting event, when the application is running but not doing any processing, the ApplicationStartingEvent will be sent
  • org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
    • Environment preparation event, when Environment is used and before the context is created, ApplicationEnvironmentPreparedEvent will be sent
  • org.springframework.boot.context.event.ApplicationContextInitializedEvent
    • context initialization event
  • org.springframework.boot.context.event.ApplicationPreparedEvent
    • Application Prepared Event, ApplicationPreparedEvent is sent after the bean definition is loaded before starting to refresh
  • org.springframework.context.event.ContextRefreshedEvent
    • context refresh event
  • org.springframework.boot.context.event.ApplicationStartedEvent
    • Application start completed event, ApplicationStartedEvent is sent after the context is refreshed and before all application and command-line runners are invoked
  • org.springframework.boot.context.event.ApplicationReadyEvent
    • Application ready event, after the application and the command-line runner are invoked, ApplicationReadyEvent will be issued to notify the application that it is ready to process the request
  • org.springframework.context.event.ContextClosedEvent (context closing event, corresponding to container closing)

​ The above list is only a part of the events. When the application starts and reaches a certain process point, the listener listens to a certain event trigger and executes the corresponding event. In addition to the system's built-in event processing, users can also customize and develop other actions to be done when the current event is triggered as needed.

//设定监听器,在应用启动开始事件时进行功能追加
public class MyListener implements ApplicationListener<ApplicationStartingEvent> {
    
    
    public void onApplicationEvent(ApplicationStartingEvent event) {
    
    
		//自定义事件处理逻辑
    }
}

Guess you like

Origin blog.csdn.net/shuai_h/article/details/130044247