This is the 12th day of my participation in the More text Challenge. For details, see the link below

1, the preface

In the past, we need to write a lot of configuration files to integrate Spring + MyBatis + SpringMVC, which can be called configuration file hell. We also need to introduce various types of JAR packages in the pem. XML file. Mybatis, SpringMVC, Spring-AOP, Spring-Context and so on.

Since using SpringBoot, a new project requires almost no changes to get it up and running. In the POM file, all we need to do is introduce a spring-boot-starter web. Everything we’ve done before, SpringBoot does for us at the bottom.

XDM who has written SSM will remember dispatcherServlet and characterEncoding. These are the two options that we must configure in web.xml. We also need the configuration file upload parser multipartResolver. You need to configure a druidDataSource and a viewResolver… At the end of the day, you’ll have a mess of configuration files.

When we use SpringBoot, it seems that we have never configured these things. All of a sudden, the configuration files that we could write with our eyes closed seem far away, and we forget the names of filters and interceptors. This is both a benefit and a disadvantage of SpringBoot’s simplified configuration.

In this article, learn how SpringBoot does dependency management and auto-configuration.

2. Dependency management

2.1 Parent project does dependency management

For each SpringBoot project, the pom.xml file defines a parent node for us

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3. RELEASE</version>
    <relativePath/> <! -- lookup parent from repository -->
</parent>
Copy the code

This node specifies the version number, so in the pom.xml file we have many imported jars that do not have a version number, but this is not an error because SpringBoot helps us specify a version number for some of the most common JAR packages.

CTRL + right click to enter the spring-boot-starter-parent jar package, you will find that its parent project is spring-boot-dependencies

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.1.3. RELEASE</version>
    <relativePath>. /.. /spring-boot-dependencies</relativePath>
</parent>
Copy the code

In this jar package, you declare the version number of many commonly used jars in development

So when you import a JAR in your pom.xml file, you don’t need to write it in dependencies if the jar defines the version number in spring-boot-dependencies. If you want to use a different version number, you can also define the version in pom.xml, following the proximity rule. For example, if you want to use a custom version of the MySQL driver, just define it in pom.xml

<properties>
    <mysql.version>5.1.43</mysql.version>
</properties>
Copy the code

2.2 Starter Scenario

In the SpringBoot project, we just need to introduce the spring-boot-starter- Web package to write the interface and access it. Since the starter integrates the Spring AOP, Spring Context, Spring WebMVC and other JAR packages we introduced when we wrote the Spring project, including Tomcat, the SpringBoot project does not need external Tomcat. Simply start the Application class and use the built-in Tomcat server.

In the SpringBoot project, according to the official documentation, there are various scenarios in which spring-boot-starter-* can be used, and as soon as the starter is introduced, all the usual dependencies for this scenario are automatically introduced. (docs. Spring. IO/spring – the boot…

The lowest dependency for all scenario initiators is the Spring-boot-starter, a jar package that is the core startup package and contains auto-configuration support, logging, and YAML. Core Starter, including Auto-Configuration Support, Logging and YAML.

This spring-boot-autoconfigure relates to SpringBoot’s next feature.

3. Container function

Before you learn about SpringBoot’s auto-configuration capabilities, you need to learn about SpringBoot’s container management capabilities. When I learned Spring, I knew that Spring’s IOC and AOP.

The IOC container helps us to store objects and manage them, including their creation, assembly, and destruction, leaving the work of the program to the Spring framework. The core of learning is how to get objects in and out of Spring.

3.1 Default Scan path of SpringBoot packages

In SpringBoot, we do not specify a scan path for any package, but objects that you register in the container can be retrieved. This is because SpringBoot has a default scan path for packages, and the target objects in this path are registered in the container. The default scan path is the Main Application Class directory and subdirectories. You can run the scanBasePackages attribute to change the scan path

@SpringBootApplication(scanBasePackages = "xxx.xxx.xxx")
Copy the code

This property is actually bound to the @ComponentScan basePackages property, so you can use @ComponentScan to achieve the same effect.

The default scanning path in the code in the parse method ComponentScanAnnotationParser classes, in the corresponding line breakpoints, start the main class for debugging

And when you debug it, you’ll see that this declaringClass is actually the launch class for your project, and then the package that the launch class is in is going to be added to your basePackages.

3.2 Adding Components

(1) @Configuration and @Bean

The @Configuration annotation indicates that the class is a Configuration class, and the @Bean annotation registers an instance in the container.

import com.codeliu.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

    @Bean
    public User user(a) {
        User user = new User("The bald guy".20);
        returnuser; }}Copy the code

Then test in the startup class, you can find that the instances in the container are singletons, that is, multiple times to get the same object.

@SpringBootApplication
public class DockerTestApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(DockerTestApplication.class, args);
        User user1 = run.getBean("user", User.class);
        User user2 = run.getBean(User.class);
        // trueSystem.out.println(user1 == user2); }}Copy the code

The proxyBeanMethods property in the @Configuration annotation, which is the method of the proxy bean, determines whether it is a singleton pattern, defaults to true. Full mode (proxyBeanMethods = true) and Lite mode (proxyBeanMethods = false). Full mode guarantees that each @bean method is called as many times as it returns a single instance. In Lite mode, how many times each @bean method is called returns a newly created component. Component dependencies must be in Full mode by default, and other dependencies must be in Lite mode by default

@Component, @controller, @service, @repository

Four French Kings, using annotations on the POJO, mapper, Service, and Controller classes.

(3) @ Import

The annotation is defined as follows: There is only one value attribute, and you can pass in an array of classes, which will be automatically registered into the container for you during startup.

@Configuration
@Import({User.class, DBHelper.class})
public class MyConfig {}Copy the code
@SpringBootApplication
public class DockerTestApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(DockerTestApplication.class, args);
        User user1 = run.getBean(User.class);
        User user2 = run.getBean(User.class);
        // true
        System.out.println(user1 == user2);

        User user = run.getBean(User.class);
        // com.codeliu.entity.User@63411512
        System.out.println(user);

        DBHelper dbHelper = run.getBean(DBHelper.class);
        // ch.qos.logback.core.db.DBHelper@35cd68d4System.out.println(dbHelper); }}Copy the code

As you can see, the default component name is the full class name.

(4) @conditional assembly

This means that components are injected only when the conditions specified in @conditionals are met.

import com.codeliu.entity.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
// If there is no bean named test, the annotation can be placed on the type or method, with different scope
@ConditionalOnMissingBean(name = "test")
public class MyConfig {
    @Bean
    public User user(a) {
        User user = new User("The bald guy".20);
        returnuser; }}Copy the code

Other annotations are similar. When SpringBoot does auto-configuration, the underlying use of a lot of conditional assembly for on-demand loading purposes.

3.3 Importing native Configuration Files

The @importResource annotation can import the Spring configuration file and make the contents of the configuration file effective. Because some project beans are defined in XML files, you must know the path to the XML file so that Spring loads the configuration file when the project starts. So for the SpringBoot project, where all the beans are configured in Java, is XML useless?

@Configuration is used with @ImportResource to load XML configurations.

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource("classpath:beans.xml")
public class MyConfig {}Copy the code

3.4 Binding Configuration

Many times we need to read properties from the Properties file and encapsulate them into the corresponding Java bean. We can read it from code

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();// Get the name of the configuration file
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             // Encapsulate the javabeans.}}}Copy the code

This is extremely inconvenient when there are many properties in the configuration file.

(1) @Component and @ConfigurationProperties

Using these two annotations on a Java bean can be associated with a property in a configuration file, but it is important to note that the Java bean must have a setter/getter method, otherwise the value cannot be assigned, and that the property in the configuration file must not have uppercase letters, otherwise the startup error will be reported.

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "my-user")
public class User {
    private String names;
    private intage; . }Copy the code

The getter/setter and toString methods are omitted. The @ConfigurationProperties annotation specifies the prefix of the relevant properties in the configuration file, in the configuration file

my-user.names=CodeTiger
my-user.age=22
Copy the code

Start the test in the class to get the User object, and the output will find that the property has been assigned.

(2) @ ConfigurationProperties and @ EnableConfigurationProperties

@ ConfigurationProperties annotations specified prefix on loading Java beans, and @ EnableConfigurationProperties annotations are added on the configuration class, the annotation has two functions: Enable configuration binding and register the specified Java bean with the container. Since this annotation registers the Java bean into the container, there is no need to add the @Component annotation on the Java bean.

@Configuration
@EnableConfigurationProperties({User.class})
public class MyConfig {}Copy the code

4. Principle of automatic configuration

With that in mind, let’s take a look at how the SpringBoot basics help us automatically configure beans. Start with the @SpringBootApplication annotation added to the startup class.

The above four notes have nothing to do with our study and can be ignored.

(1) @ SpringBootConfiguration

If you look at the definition of the annotation, you see that it is labeled @Configuration and that it has a unique property called proxyBeanMethods. We talked about this property earlier when we talked about the @Configuration annotation, so I’m not going to repeat it here. This indicates that the class decorated by @SpringBootConfiguration is also a configuration class.

(2)@ComponentScan

Specifies which Spring annotations to scan.

(3) @ EnableAutoConfiguration

This is the entry to SpringBoot’s automatic configuration, and the annotation is defined as follows

4.1 Automatically Configuring the basePackage

The @autoconfigurationpackage annotation indicates, as the name implies, the AutoConfigurationPackage.

@ Import annotations, front said, will be a component into the container, so we see AutoConfigurationPackages. The Registrar long what look like. It calls the register method to register the component, so where is the component registered? The second argument is to get the basePackage, so you can guess that the @autoConfigurationPackage annotation automatically registers the default package or the components below the specified package path into the container when the project starts.

Let’s go into the PackageImports class and debug with a breakpoint on the line to see what the values look like when the project starts

As you can see, it takes the package path of the startup class and returns it to the register method as its second argument. This is why at boot time, we don’t need to configure any path, SpringBoot can help us to inject components into the container.

4.2 Automatic Configuration Configuration of imported packages

What does this do? As we said earlier, in Spring we configure the required components such as dispatcherServlet and characterEncoding, but in SpringBoot we do nothing.

Because SpringBoot does it for us at the bottom. Is EnableAutoConfiguration annotations on @ Import (AutoConfigurationImportSelector. Class). Stick it again

So we have to go see AutoConfigurationImportSelector class.

In AutoConfigurationImportSelector classes have a getAutoConfigurationEntry method, the method is to give bulk import some components in the container. Which components to import? In this method, you get a configurations, and then you delete and reduplicate the configurations. So let’s take a look at what’s stored in this variable.

Set a breakpoint on the corresponding line and run

What is this? An array of 130 in length, which is filled with AutoConfiguration, and we see the familiar AopAutoConfiguration. Scroll down and find the familiar DispatcherServlet

So where are these autoconfiguration classes read from? The method in getCandidateConfigurations method

This method calls the loadFactoryNames method, which in turn calls the loadSpringFactories method, using the factory to load all the components in the meta-INF /spring.factories file.

The meta-INF /spring.factories file is in the JAR package we imported. It scans all the meta-INF /spring.factories files in jar packages, and then deduplicates and removes the components that we exclude.

In my test project, the number of components obtained is 130, which is in the spring-boot-Autoconfigure-2.4.4.jar package, which has exactly 130 components.

At this point, the general process is summarized as follows:

(1) using getAutoConfigurationEntry (annotationMetadata); Import components in bulk to the container.

(2) call List configurations = getCandidateConfigurations (annotationMetadata, attributes) access to all you need to import the configuration class in to the container.

(3) Load Map<String, List> loadSpringFactories(@nullable ClassLoader); Get all the components.

(4) Load a file from the meta-INF /spring.factories location. By default, we scan all meta-INF /spring.factories in our current system.

4.3 Enabling automatic configuration items on Demand

SpringBoot loads so many components for us at startup that we can’t possibly use them all, and it’s a waste of resources to register them in a container that doesn’t use them. The underlying Conditional assembly is @conditional, which registers the corresponding component only when we need it.

In my test project, I was able to look at the source code of xxxAutoConfiguration in the spring-boot-Autoconfigure-2.4.4. jar package because the components were loaded at startup. Such as AopAutoConfiguration

In the project, if we had not introduced the AspectJ JAR, there would have been no Advice class, and neither the JDK dynamic proxy nor the CGlib proxy would have taken effect. In this case, the base broker is in effect, which only applies to advisors within the framework. Our custom aspects of the project will not be proxyed by AOP.

Other AutoConfigurations are similar, so I won’t look at them here.

4.4 User Preference

What is user first? The SpringBoot hierarchy automatically loads components for us, but what if we want to use our own? Let’s see HttpEncodingAutoConfiguration

Registration is performed only when the application is a Servlet application and the CharacterEncodingFilter class exists. The class is also bound to the configuration file, where properties can be assigned. When registering the CharacterEncodingFilter, it will be registered only if the bean does not exist in the system to prevent duplicate registrations, and the component value is dynamically assigned. That is, if we do not want to use UTF-8 for encoding, we can change it in the configuration file. We’ll use our custom values.

According to the official document, the following properties can be set.

5, summary

Originally the main analysis of SpringBoot is how to do dependency management and automatic configuration, compared to Spring, a lot of work is in the bottom to help us do. Although we write code may not use these, but know it and know why, the paper comes zhongjue shallow, must know this matter to practice.