In-depth study of Spring Boot automatic assembly

Mode annotation

Stereotype Annotation mode known as annotations, Spring notes have in common mode @Service, @Repository, @Controllerand so on, they are "derived" from @Componentcomments. We all know that all @Componentannotated classes will be scanned by Spring and included in the IOC container, and @Componentthe classes annotated by the derived annotations will also be scanned into the IOC container. Below we mainly come to understand @Componentthe "derivation" and "layering" through custom mode annotations .

@Component "Derivative"

Create a new Spring Boot project, the Spring Boot version is 2.1.0.RELEASE, artifactIdautoconfig, and spring-boot-starter-webdependencies are introduced . The project structure is as follows:

In com.example.democase the new annotationpackage, and then create a FirstLevelServicecomment:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface FirstLevelService {
    String value() default "";
}

This annotation is defined by @Serviceannotations, @Serviceand you will find that it is @Componentannotated when you view the source code , so their hierarchical relationship is:

└─@Component
     └─@Service
          └─@FirstLevelService

That @FirstLevelServiceis @Componentderived from the annotation mode, we have to test whether it marked a class can be scanned into the IOC container:

In com.example.demoNew Under servicethe package, and then create a TestServiceclass:

@SecondLevelService
public class TestService {
}

In com.example.demothe new bootstrappackage, and then create a ServiceBootStrapclass for the test register TestServiceand obtain it from the IOC vessel:

@ComponentScan("com.example.demo.service")
public class ServiceBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(ServiceBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        TestService testService = context.getBean("testService", TestService.class);
        System.out.println("TestService Bean: " + testService);
        context.close();
    }
}

Run the main method of this class, the console output is as follows:

@Component "hierarchical"

We com.example.demo.annotationcreate another SecondLevelServiceannotation definition under the path , the annotation is @FirstLevelServicemarked by the above :

At this time, the hierarchical relationship is:

└─@Component
     └─@Service
           └─@FirstLevelService
                 └─@SecondLevelService

We will replace the TestServiceabove annotation with the main method, @SecondLevelServiceand then run it again ServiceBootStrap, the output is as follows:

It can be seen that the result is also successful.

One thing to note here is that the @Componentannotation only contains one value attribute definition, so its "derived" annotation can only contain one value attribute definition.

@Enable module driver

@EnableModule drivers are supported after Spring Framework 3.1. In general, the module here is a collection of components to achieve a certain function. Through the @Enablemodule driver, we can turn on the corresponding module function.

@EnableModule drive can be divided into two implementation modes: "annotation drive" and "interface programming". The following demonstrates one by one:

Annotation Driven

In Spring, an annotation-driven example can be viewed from the @EnableWebMvcsource code:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

This annotation is done by @Importimporting a configuration class DelegatingWebMvcConfiguration:

This configuration class is inherited from it WebMvcConfigurationSupport, which defines some Bean declarations.

Therefore, the annotation-driven @Enablemodule driver actually @Importimports a configuration class to realize the component registration of the corresponding module. When these components are registered in the IOC container, the corresponding function of this module can be used.

Let's define a @Enablemodule driver based on annotation- driven.

In com.example.demoNew Under configurationthe package, and then create a HelloWorldConfigurationconfiguration class:

@Configuration
public class HelloWorldConfiguration {

    @Bean
    public String hello() {
        return "hello world";
    }
}

A named helloBean is defined in this configuration class with content hello world.

In com.example.demo.annotationcreating a next EnableHelloWorldannotation definition:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
}

We @Importimported the configuration class we just created on this annotation class.

Then in com.example.demo.bootstrapcreating a next TestEnableBootstapstartup class to test @EnableHelloWorldannotation is in effect:

@EnableHelloWorld
public class TestEnableBootstap {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(TestEnableBootstap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        String hello = context.getBean("hello", String.class);
        System.out.println("hello Bean: " + hello);
        context.close();
    }
}

Run the main method of this class, the console output is as follows:

Explain that our custom annotation-driven @EnableHelloWorldis feasible.

Interface programming

In addition to using the above method, we can also implement @Enablemodule drive by way of interface programming . In Spring, there are @EnableCachingannotations based on interface programming , check the source code:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({CachingConfigurationSelector.class})
public @interface EnableCaching {
    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}

EnableCachingAnnotations @Importimport a CachingConfigurationSelectorclass, which indirectly implements the ImportSelectorinterface. In the in-  depth study of Spring component registration  , we have introduced that ImportSelectorcomponent registration can be achieved through .

Therefore @Enable, the essence of implementing module driving through interface programming is @Importto import the interface ImportSelectorimplementation class, which can define the components that need to be registered in the IOC container, so as to realize the registration of the corresponding components of the corresponding module.

Next, we come to realize it again according to this idea:

In com.example.demothe new selectorpacket, then a new path in the HelloWorldImportSelectorimplemented ImportSelectorinterfaces:

public class HelloWorldImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{HelloWorldConfiguration.class.getName()};
    }
}

If you don't understand the meaning of the above code, you can read the article on in-depth learning of Spring component registration .

Then we modify EnableHelloWorld:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldImportSelector.class)
public @interface EnableHelloWorld {
}

The import above is HelloWorldImportSelector, not HelloWorldConfiguration.

Run TestEnableBootstapthe main method again and you will find that the output is the same.

Automatic assembly

The bottom layer of automatic assembly technology in Spring Boot mainly uses the following technologies:

  1. Spring mode annotation assembly

  2. Spring @Enable module assembly

  3. Spring conditional assembly (introduced in the in-depth study of Spring component registration )

  4. Spring factory loading mechanism

The implementation class of Spring factory loading mechanism is SpringFactoriesLoader, check its source code:

The method of this class will read the spring.factories configuration file under the META-INF directory. We check the file under spring-boot-autoconfigure-2.1.0.RELEASE.jar:

When the startup class is @EnableAutoConfigurationmarked, all the classes in the screenshot above will be scanned by Spring to see if they can be included in the IOC container for management.

For example org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration, the source code we viewed :

It can be seen that there are some annotations marked on this category, among which @Configurationare the mode annotations, @EnableConfigurationPropertiesthe module assembly technology, and ConditionalOnClassthe conditional assembly technology. This is consistent with the main underlying technologies of Spring Boot auto-assembly listed above, so we can customize an auto-assembly implementation based on this idea.

Create a new configuration class HelloWorldAutoConfiguration:

@Configuration
@EnableHelloWorld
@ConditionalOnProperty(name = "helloworld", havingValue = "true")
public class HelloWorldAutoConfiguration {
}

Then create a new META-INF directory under the resources directory and create a spring.factories file:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.configuration.HelloWorldAutoConfiguration

Then add helloworld=trueconfiguration in the configuration file application.properties

helloworld=true

Finally create EnableAutoConfigurationBootstrap, test HelloWorldAutoConfigurationwhether it works:

@EnableAutoConfiguration
public class EnableAutoConfigurationBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableAutoConfigurationBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        String hello = context.getBean("hello", String.class);
        System.out.println("hello Bean: " + hello);
        context.close();
    }
}

Run the main method, the console output is as follows:

It shows that our customized automatic assembly has been successful.

Below is a brief analysis of the running logic of the code:

  • Spring's factory loading mechanism will automatically read the content of the spring.factories file in the META-INF directory;

  • We defined in spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.configuration.HelloWorldAutoConfiguration

We use @EnableAutoConfigurationannotations on the test class , then HelloWorldAutoConfigurationit will be scanned by Spring to see if it meets the requirements, and if it meets the requirements, it will be included in the IOC container;

  • HelloWorldAutoConfigurationThe function of the @ConditionalOnPropertyabove annotation is: when the configuration file is configured helloworld=true(we did add this configuration, so it meets the requirements), this class meets the scanning rules; the @EnableHelloWorldannotation is a custom module-driven annotation in our previous example, which introduces the hello Bean, so the hello bean will exist in the IOC container;

  • Through the above steps, we can get the hello bean through the context.

 

Guess you like

Origin blog.csdn.net/u014225733/article/details/100836544