preface

When using SpringBoot for project development, no configuration files, no complex POM coordinates, as long as a xxxApplication (boot class) on the run, then springBoot is how to do the convention is greater than the configuration?

Custom starter

Why custom starter

In daily development, there are often some independent business of the public module, if multiple projects can reuse the public module, there is no need to manually copy to the project, we will encapsulate the public module into a starter, reuse when the direct introduction of dependence can be, Springboot for us to complete automatic assembly.

Naming rules

Springboot The official starter is named in the spring-boot-starter-xxx format. It is recommended that the name of a customized starter be xxX-spring-boot-starter to distinguish the two types of starter

Custom starter implementation

Create an empty project named hello-spring-boot-starter. Hello-spring-boot-autoconfigure = hello-spring-boot-autoconfigure = hello-spring-boot-autoconfigure = hello-spring-boot-autoconfigure

<! -- Automatic configuration -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<! Support reading XML /properties file configuration -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
Copy the code

4. Create a HelloProperties class

// Configure the attribute prefix
@ConfigurationProperties("my.hello")
public class HelloProperties {
    private String name;
    private Integer age;
    private String hometown;
    // Getter setter toString omitted
}
Copy the code

5. Create a HelloService class

public class HelloService {
    private String name;
    private Integer age;
    private String hometown;
    public HelloService(String name, Integer age, String hometown) {
        this.name = name;
        this.age = age;
        this.hometown = hometown;
    }
    public String sayHello(String name) {
        return "hello..." + name;
    }
    public String helloWorld(a) {
        return String.format("[name=%s, age=%d, hometown=%s]".this.name, this.age, this.hometown); }}Copy the code

6, new HelloServiceAutoConfiguration class

@Configuration
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
    private final HelloProperties helloProperties;
    public HelloServiceAutoConfiguration(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }
    @Bean
    @ConditionalOnMissingBean		// Execute this method if the HelloService class does not exist
    public HelloService helloService(a) {
        return new HelloService(this.helloProperties.getName(), this.helloProperties.getAge(), this.helloProperties.getHometown()); }}Copy the code

Create a meta-INF /spring.factories factory under the classpath

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.springboot.HelloServiceAutoConfiguration
Copy the code

Modules MVN clean Install, package the child modules to the local repository

9. Introduce child modules dependencies in parent project

<dependencies>
    <dependency>
        <groupId>com.springboot</groupId>
        <artifactId>hello-spring-boot-autoconfigure</artifactId>
        <version>0.0.1 - the SNAPSHOT</version>
    </dependency>
</dependencies>
Copy the code

Create a new module to introduce the starter test. 11. Introduce dependencies in spring-boot-test

<! -- Custom starter-->
<dependency>
   <groupId>com.springboot</groupId>
   <artifactId>hello-spring-boot-autoconfigure</artifactId>
   <version>0.0.1 - the SNAPSHOT</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
Copy the code

12. Create the HelloController class

@RestController
@RequestMapping("/hello")
public class HelloController {
    @Resource
    private HelloService helloService;
    @GetMapping("/{name}")
    public String hello(@PathVariable String name) {
        return helloService.sayHello(name);
    }
	@GetMapping
    public String helloWorld(a) {
        returnhelloService.helloWorld(); }}Copy the code

Application. Properties Add configuration information

my.hello.name=feiyangyang
my.hello.age=19
my.hello.hometown=bj
Copy the code

14. Start the project and test it



Principle of Automatic configuration

The SpringBoot core annotation @SpringBootApplication is a composite annotation that includes three important annotations

@SpringBootConfiguration

As you can see, the @SpringBootConfiguration annotation inherits from @Configuration and is used to declare that the current class is a Configuration class. One or more instances of the current class declaration marked with the @Bean annotation are injected into the IOC container.

Also, Spring’s configuration classes are components of the IOC container.


@EnableAutoConfiguration

The @enableAutoConfiguration annotation is the core annotation for automatic springBoot configuration. It is used to inject beans required by Spring applications into the container.



Look at the @autoConfigurationPackage annotation, which automatically configures a package, essentially using @import to Import a component into the Spring container, The imported here is AutoConfigurationPackages. The Registrar. The class

Let’s look at AutoConfigurationPackages. The Registrar. The class, I noticed it was AutoConfigurationPackages class inner classes. By analyzing AutoConfigurationPackages. The Registrar class registerBeanDefinitions method, found that the parameters of its internal call register method is PackageImports instances of the class, We go to the construction of the PackageImports class and find that this construction is to get all the package paths to scan.



We interrupted debug and found that the package path we got was the package path of our project.



What is AnnotationMetadata, the argument to the PackageImports construct? Springboot will scan the components in the package path to the IOC container.

Let’s go back to @enableAutoConfiguration and see what the class labeled @import does.



We have found a point to AutoConfigurationImportSelector class selectImports method, and this method is to all components into the IOC container, and will return to, the fully qualified class name of those components to verify us.

A getAutoConfigurationEntry selectImports () method (), found getAutoConfigurationEntry () to get a lot of automatic configuration class (xxxAutoConfiguration), Inject these auto-configuration classes into the IOC container. So how does SpringBoot get these xxxAutoConfiguration classes?





Finally the debug goes to the loadSpringFactories() method and finds that the meta-INF /spring.factories file is being scanned.





The meta-inf/spring. Factories the contents of the file with ConcurrentReferenceHashMap stored, This ConcurrentReferenceHashMap is a Map < this Map < String, List > > such a structure, including the class loader, In the meta-inf/spring. Factories file attribute name org. Springframework. Boot. Autoconfigure. EnableAutoConfiguration as key, All attribute values are stored as values.

So just to summarize a little bit, Springboot when starting from the classpath of the meta-inf/spring. Factories file for org. Springframework. Boot. Autoconfigure. EnableAutoConfiguration Import these values into the container as auto-configuration classes, and the auto-configuration classes will take effect and do the auto-configuration work for us, which we used to do ourselves.

The principle of springBoot automatic configuration: Search all meta-INF /spring.factories files from your classpath, And will the org. Springframework. Boot. Autoconfigure. XxxAutoConfiguration EnableAutoConfiguration attribute value added to the container.


@ComponentScan

@ComponentScan automatically scans and loads qualified components (eg: @Component, @Controller…). , and finally load these bean definitions into the IOC container.


The above user-defined starter is used as an example

The starter process specifies the fully qualified name of the autoconfiguration class in meta-INF/Spring. factories. The autoconfiguration class is picked up by SpringBoot and injected into the IOC container, as shown below. It was added to the list of auto-configuration classes that need to be injected.



We take a look at HelloServiceAutoConfiguration class do?

// Declare this class to be a configuration class, which is the same as XML configuration file injection
@Configuration
// Sets up the ConfigurationProperties function for the specified class. In short, bind the value of the configuration file to the HelloProperties property. The properties we set in Yml are achieved by binding the properties in the xxxProperties class
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
    private final HelloProperties helloProperties;
    public HelloServiceAutoConfiguration(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }
    @Bean
    @ConditionalOnMissingBean		// Execute this method when the HelloService class does not exist and create a new Instance of HelloService using the property values configured in YML as construction parameters
    public HelloService helloService(a) {
        return newHelloService(helloProperties.getName(), helloProperties.getAge(), helloProperties.getHometown()); }}Copy the code

Once the configuration class is in effect and meets the specified conditions (@conditionalonmissingBean), the HelloService class is injected into the IOC container. The properties of the HelloService class are obtained from the corresponding Properties class. The values of these properties are bound to the configuration file.

All properties that can be configured in a configuration file are encapsulated in the xxxProperties class, which can be configured in a configuration file by referring to the xxxProperties class for a particular function.

conclusion

Springboot injects a large number of xxxAutoConfiguration classes at startup, obtained by scanning meta-INF/Spring.Factories in the classpath. After these xxxAutoConfiguration classes take effect, they will inject the corresponding business classes of each xxxAutoConfiguration into the IOC container, and the property values of these business classes are encapsulated in the xxxProperties class. The values of the properties in the xxxProperties class are bound to the properties defined in YML in the project that introduced the starter.

Therefore, to complete automatic injection of a class in SpringBoot, the following two classes are required.

  • XxxAutoConfiguration: Automatic configuration class to add components to the container
  • XxxProperties: encapsulates related properties in the configuration file