[SpringBoot from entry to proficiency] Chapter 3 Springboot starter, container, common annotation analysis

Three, Springboot analysis

3.1 Import method

3.1.1 Inherit parent project

spring-boot-starter-parentIt can be inherited in the project .

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>${springboot.version}</version>
</parent>

spring-boot-starter-parentInside the project also inherits another parent project spring-boot-dependencies.

insert image description here

3.1.2 Dependency management

Introduce Springboot's dependencies through dependency management spring-boot-dependencies.

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${springboot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3.2 Version number

3.2.1 Default version number

Declare the version numbers of almost all dependencies commonly used in development in spring-boot-starter-parentthe parent project of . spring-boot-dependenciesTherefore, there is no need to pay attention to the version number in the Springboot project, and the version matching is automatic.

<properties>
    <activemq.version>5.16.5</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.97</appengine-sdk.version>
    <artemis.version>2.19.1</artemis.version>
    <aspectj.version>1.9.7</aspectj.version>
    <assertj.version>3.21.0</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <awaitility.version>4.1.1</awaitility.version>
    <build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
    <byte-buddy.version>1.11.22</byte-buddy.version>
    <caffeine.version>2.9.3</caffeine.version>
    <cassandra-driver.version>4.13.0</cassandra-driver.version>
    <classmate.version>1.5.1</classmate.version>
    <commons-codec.version>1.15</commons-codec.version>
    <commons-dbcp2.version>2.9.0</commons-dbcp2.version>
    <commons-lang3.version>3.12.0</commons-lang3.version>
    <commons-pool.version>1.6</commons-pool.version>
    <commons-pool2.version>2.11.1</commons-pool2.version>
    <couchbase-client.version>3.2.7</couchbase-client.version>
    <db2-jdbc.version>11.5.7.0</db2-jdbc.version>
    <dependency-management-plugin.version>1.0.11.RELEASE</dependency-management-plugin.version>
    <derby.version>10.14.2.0</derby.version>
    <dropwizard-metrics.version>4.2.10</dropwizard-metrics.version>
    <ehcache.version>2.10.9.2</ehcache.version>
    <ehcache3.version>3.9.9</ehcache3.version>
    <elasticsearch.version>7.15.2</elasticsearch.version>
    <embedded-mongo.version>3.0.0</embedded-mongo.version>
    <flyway.version>8.0.5</flyway.version>
    <freemarker.version>2.3.31</freemarker.version>
    <git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
    <glassfish-el.version>3.0.4</glassfish-el.version>
    <glassfish-jaxb.version>2.3.6</glassfish-jaxb.version>
    <glassfish-jstl.version>1.2.6</glassfish-jstl.version>
    <groovy.version>3.0.11</groovy.version>
    <gson.version>2.8.9</gson.version>
    <h2.version>1.4.200</h2.version>
    <hamcrest.version>2.2</hamcrest.version>
    <hazelcast.version>4.2.5</hazelcast.version>
    <hazelcast-hibernate5.version>2.2.1</hazelcast-hibernate5.version>
    <hibernate.version>5.6.9.Final</hibernate.version>
    <hibernate-validator.version>6.2.3.Final</hibernate-validator.version>
    <hikaricp.version>4.0.3</hikaricp.version>
    <hsqldb.version>2.5.2</hsqldb.version>
    <htmlunit.version>2.54.0</htmlunit.version>
    <httpasyncclient.version>4.1.5</httpasyncclient.version>
    <httpclient.version>4.5.13</httpclient.version>
    <httpclient5.version>5.1.3</httpclient5.version>
    <httpcore.version>4.4.15</httpcore.version>
    <httpcore5.version>5.1.3</httpcore5.version>
    <infinispan.version>12.1.12.Final</infinispan.version>
    <influxdb-java.version>2.22</influxdb-java.version>
    <jackson-bom.version>2.13.3</jackson-bom.version>
    <jakarta-activation.version>1.2.2</jakarta-activation.version>
    <jakarta-annotation.version>1.3.5</jakarta-annotation.version>
    <jakarta-jms.version>2.0.3</jakarta-jms.version>
    <jakarta-json.version>1.1.6</jakarta-json.version>
    <jakarta-json-bind.version>1.0.2</jakarta-json-bind.version>
    <jakarta-mail.version>1.6.7</jakarta-mail.version>
    <jakarta-management.version>1.1.4</jakarta-management.version>
    <jakarta-persistence.version>2.2.3</jakarta-persistence.version>
    <jakarta-servlet.version>4.0.4</jakarta-servlet.version>
    <jakarta-servlet-jsp-jstl.version>1.2.7</jakarta-servlet-jsp-jstl.version>
    <jakarta-transaction.version>1.3.3</jakarta-transaction.version>
    <jakarta-validation.version>2.0.2</jakarta-validation.version>
    <jakarta-websocket.version>1.1.2</jakarta-websocket.version>
    <jakarta-ws-rs.version>2.1.6</jakarta-ws-rs.version>
    <jakarta-xml-bind.version>2.3.3</jakarta-xml-bind.version>
    <jakarta-xml-soap.version>1.4.2</jakarta-xml-soap.version>
    <jakarta-xml-ws.version>2.3.3</jakarta-xml-ws.version>
    <janino.version>3.1.7</janino.version>
    <javax-activation.version>1.2.0</javax-activation.version>
    <javax-annotation.version>1.3.2</javax-annotation.version>
    <javax-cache.version>1.1.1</javax-cache.version>
    <javax-jaxb.version>2.3.1</javax-jaxb.version>
    <javax-jaxws.version>2.3.1</javax-jaxws.version>
    <javax-jms.version>2.0.1</javax-jms.version>
    <javax-json.version>1.1.4</javax-json.version>
    <javax-jsonb.version>1.0</javax-jsonb.version>
    <javax-mail.version>1.6.2</javax-mail.version>
    <javax-money.version>1.1</javax-money.version>
    <javax-persistence.version>2.2</javax-persistence.version>
    <javax-transaction.version>1.3</javax-transaction.version>
    <javax-validation.version>2.0.1.Final</javax-validation.version>
    <javax-websocket.version>1.1</javax-websocket.version>
    <jaxen.version>1.2.0</jaxen.version>
    <jaybird.version>4.0.6.java8</jaybird.version>
    <jboss-logging.version>3.4.3.Final</jboss-logging.version>
    <jdom2.version>2.0.6.1</jdom2.version>
    <jedis.version>3.7.1</jedis.version>
    <jersey.version>2.35</jersey.version>
    <jetty-el.version>9.0.52</jetty-el.version>
    <jetty-jsp.version>2.2.0.v201112011158</jetty-jsp.version>
    <jetty-reactive-httpclient.version>1.1.11</jetty-reactive-httpclient.version>
    <jetty.version>9.4.48.v20220622</jetty.version>
    <jmustache.version>1.15</jmustache.version>
    <johnzon.version>1.2.18</johnzon.version>
    <jolokia.version>1.7.1</jolokia.version>
    <jooq.version>3.14.16</jooq.version>
    <json-path.version>2.6.0</json-path.version>
    <json-smart.version>2.4.8</json-smart.version>
    <jsonassert.version>1.5.0</jsonassert.version>
    <jstl.version>1.2</jstl.version>
    <jtds.version>1.3.1</jtds.version>
    <junit.version>4.13.2</junit.version>
    <junit-jupiter.version>5.8.2</junit-jupiter.version>
    <kafka.version>3.0.1</kafka.version>
    <kotlin.version>1.6.21</kotlin.version>
    <kotlin-coroutines.version>1.5.2</kotlin-coroutines.version>
    <lettuce.version>6.1.8.RELEASE</lettuce.version>
    <liquibase.version>4.5.0</liquibase.version>
    <log4j2.version>2.17.2</log4j2.version>
    <logback.version>1.2.11</logback.version>
    <lombok.version>1.18.24</lombok.version>
    <mariadb.version>2.7.5</mariadb.version>
    <maven-antrun-plugin.version>3.0.0</maven-antrun-plugin.version>
    <maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
    <maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
    <maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
    <maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
    <maven-enforcer-plugin.version>3.0.0</maven-enforcer-plugin.version>
    <maven-failsafe-plugin.version>2.22.2</maven-failsafe-plugin.version>
    <maven-help-plugin.version>3.2.0</maven-help-plugin.version>
    <maven-install-plugin.version>2.5.2</maven-install-plugin.version>
    <maven-invoker-plugin.version>3.2.2</maven-invoker-plugin.version>
    <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
    <maven-javadoc-plugin.version>3.3.2</maven-javadoc-plugin.version>
    <maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
    <maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
    <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
    <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
    <maven-war-plugin.version>3.3.2</maven-war-plugin.version>
    <micrometer.version>1.8.7</micrometer.version>
    <mimepull.version>1.9.15</mimepull.version>
    <mockito.version>4.0.0</mockito.version>
    <mongodb.version>4.4.2</mongodb.version>
    <mssql-jdbc.version>9.4.1.jre8</mssql-jdbc.version>
    <mysql.version>8.0.29</mysql.version>
    <nekohtml.version>1.9.22</nekohtml.version>
    <neo4j-java-driver.version>4.4.6</neo4j-java-driver.version>
    <netty.version>4.1.78.Final</netty.version>
    <netty-tcnative.version>2.0.53.Final</netty-tcnative.version>
    <okhttp3.version>3.14.9</okhttp3.version>
    <oracle-database.version>21.3.0.0</oracle-database.version>
    <pooled-jms.version>1.2.4</pooled-jms.version>
    <postgresql.version>42.3.6</postgresql.version>
    <prometheus-client.version>0.12.0</prometheus-client.version>
    <quartz.version>2.3.2</quartz.version>
    <querydsl.version>5.0.0</querydsl.version>
    <r2dbc-bom.version>Arabba-SR13</r2dbc-bom.version>
    <rabbit-amqp-client.version>5.13.1</rabbit-amqp-client.version>
    <rabbit-stream-client.version>0.4.0</rabbit-stream-client.version>
    <reactive-streams.version>1.0.4</reactive-streams.version>
    <reactor-bom.version>2020.0.20</reactor-bom.version>
    <rest-assured.version>4.4.0</rest-assured.version>
    <rsocket.version>1.1.2</rsocket.version>
    <rxjava.version>1.3.8</rxjava.version>
    <rxjava-adapter.version>1.2.1</rxjava-adapter.version>
    <rxjava2.version>2.2.21</rxjava2.version>
    <saaj-impl.version>1.5.3</saaj-impl.version>
    <selenium.version>3.141.59</selenium.version>
    <selenium-htmlunit.version>2.54.0</selenium-htmlunit.version>
    <sendgrid.version>4.7.6</sendgrid.version>
    <servlet-api.version>4.0.1</servlet-api.version>
    <slf4j.version>1.7.36</slf4j.version>
    <snakeyaml.version>1.29</snakeyaml.version>
    <solr.version>8.8.2</solr.version>
    <spring-amqp.version>2.4.6</spring-amqp.version>
    <spring-batch.version>4.3.6</spring-batch.version>
    <spring-data-bom.version>2021.1.5</spring-data-bom.version>
    <spring-framework.version>5.3.21</spring-framework.version>
    <spring-hateoas.version>1.4.4</spring-hateoas.version>
    <spring-integration.version>5.5.13</spring-integration.version>
    <spring-kafka.version>2.8.7</spring-kafka.version>
    <spring-ldap.version>2.3.8.RELEASE</spring-ldap.version>
    <spring-restdocs.version>2.0.6.RELEASE</spring-restdocs.version>
    <spring-retry.version>1.3.3</spring-retry.version>
    <spring-security.version>5.6.6</spring-security.version>
    <spring-session-bom.version>2021.1.3</spring-session-bom.version>
    <spring-ws.version>3.1.3</spring-ws.version>
    <sqlite-jdbc.version>3.36.0.3</sqlite-jdbc.version>
    <sun-mail.version>1.6.7</sun-mail.version>
    <thymeleaf.version>3.0.15.RELEASE</thymeleaf.version>
    <thymeleaf-extras-data-attribute.version>2.0.1</thymeleaf-extras-data-attribute.version>
    <thymeleaf-extras-java8time.version>3.0.4.RELEASE</thymeleaf-extras-java8time.version>
    <thymeleaf-extras-springsecurity.version>3.0.4.RELEASE</thymeleaf-extras-springsecurity.version>
    <thymeleaf-layout-dialect.version>3.0.0</thymeleaf-layout-dialect.version>
    <tomcat.version>9.0.64</tomcat.version>
    <unboundid-ldapsdk.version>4.0.14</unboundid-ldapsdk.version>
    <undertow.version>2.2.18.Final</undertow.version>
    <versions-maven-plugin.version>2.8.1</versions-maven-plugin.version>
    <webjars-locator-core.version>0.48</webjars-locator-core.version>
    <wsdl4j.version>1.6.3</wsdl4j.version>
    <xml-maven-plugin.version>1.0.2</xml-maven-plugin.version>
    <xmlunit2.version>2.8.4</xmlunit2.version>
</properties>

So it can be concluded from the dependencies of the pom file above:

For the dependencies that exist in dependencies, when we import the project, the dependency does not need to write the version number by default; while the dependenciesdependencies that are not managed in it naturally need to declare the version number.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

insert image description here

3.2.2 Modify the default version number

Introduce the following configuration in the current project to overwrite the configuration in the parent project to modify the version number of mysql.

<properties>
    <mysql.version>5.1.49</mysql.version>
</properties>

insert image description here

3.3 Launcher

What are Starters of the Springboot project?

**Springboot extracts all functional scenarios and makes Starters (starters) one by one. You only need to import all dependencies of these Starters-related scenarios into the project. **Import the launcher of any scene you want to use.

All official starters follow a similar naming convention: spring-boot-starter-*, where *is a specific type of application.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter-web: Help us import the components that the web module depends on for normal operation.

Third-party starter names should not spring-bootstart with , because it is a rule reserved by official Spring Boot components. For example, there is a third-party starter project called thirdpartyproject, which would normally be named thirdpartyproject-spring-boot-starter.

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

The lowest level dependency of all scene enablers spring-boot-starter.

insert image description here

Introduction to starters in official documents

3.4 Automatic configuration

The launcher consists of two cores:

  1. Import module related dependencies;
  2. automatic configuration;

Automatic configuration is one of the core functions of Spring boot, which eliminates or reduces a lot of business-independent configurations that we need when developing spring applications.

And the idea behind it is not new, it uses a principle called convention over configuration (convention over configuration), it makes some reasonable assumptions (that is, conventions) in advance, as long as you follow its conventions, you don't need With additional configuration, the functionality it provides can be used directly, eliminating the need for explicit configuration. For example, maven will assume that the source code directory is the src/main/java directory under the project root directory by default. If you follow this directory structure, you don't need to tell maven what the source code directory is. This principle is often used by various frameworks, because frameworks often make corresponding configurations according to these conventions. If these configurations meet our needs, we don’t have to do anything, just accept and use them readily. Only when When these configurations do not meet our needs, we need to make adjustments.

Next, take spring-boot-starter-web as an example to see what automated configurations have been made.

3.4.1 Tomcat container

  • Introduce built-in Tomcat dependencies;
  • Configure Tomcat;

3.4.2 SpringMVC

  • Introduce a full set of SpringMVC components;
  • Configure SpringMVC common components;
    • Front Controller dispatcherServlet
    • Handler Mapper requestMappingHandlerMapping
    • Handler adapter requestMappingHandlerAdapter
    • view resolver viewResolver
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Springboot02BootApplication {
    
    

    public static void main(String[] args) {
    
    
        //获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
        ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);

        //获取Spring容器中所有Bean的名称
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
    
    
            System.out.println(name);
        }
    }
}

3.4.3 Web function

  • Introduce common Web functions;
  • Configure web features:
    • upload multipartResolver
    • JSON processing mappingJackson2HttpMessageConverter
    • Exception handling handlerExceptionResolver
    • Background data verification mvcValidator
    • Character encoding filter characterEncodingFilter

3.4.4 Component scanning

There is no need to configure component scanning. The default component scanning package is the package where the startup class is located, which means that the components in the package where the startup class is located and all subpackages below it will be scanned in by default.

If you need to change the scan package, just add the scanBasePackages configuration item in the @SpringBootApplication annotation of the startup class, or specify the scan path through the @ComponentScan annotation.

@SpringBootApplication(scanBasePackages = "com.newcapec")

3.4.5 Configure default values

The default configuration is eventually mapped to a certain class, such as: DataSourceProperties.

3.4.6 Load automatic configuration items on demand

There are many starters in Springboot, each with automatic configuration items, and all automatic configuration functions are in the spring-boot-autoconfigure package.

insert image description here

Note: Springboot does not load all automatic configuration items when it starts, but opens the corresponding automatic configuration items according to the introduced starter.

3.5 Springboot container

3.5.1 @Configuration+@Bean

Bean configuration is performed through @Configuration and @Bean annotations in the Springboot project.

3.5.1.1 Bean class

Customers class:

public class Customers {
    
    

    private String name;
    private Integer age;

    public Customers() {
    
    
    }

    public Customers(String name, Integer age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "Customers{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }
}

Orders class:

public class Orders {
    
    

    private String name;
    private Customers customers;

    public Orders() {
    
    
    }

    public Orders(String name) {
    
    
        this.name = name;
    }

    public Orders(String name, Customers customers) {
    
    
        this.name = name;
        this.customers = customers;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Customers getCustomers() {
    
    
        return customers;
    }

    public void setCustomers(Customers customers) {
    
    
        this.customers = customers;
    }

    @Override
    public String toString() {
    
    
        return "Orders{" +
            "name='" + name + '\'' +
            ", customers=" + customers +
            '}';
    }
}

3.5.1.2 Configuration class

@Configuration
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        //在@Configuration注解的配置类中,如果直接调用@Bean标注的方法,相当于从IOC容器中获取该bean并依赖注入
        return new Orders("Tom's Order", getCustomers());
    }
}

3.5.1.3 Testing

public static void main(String[] args) {
    
    
    //获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
    ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);

    //获取Spring中加载的Bean
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
    
    
        System.out.println(name);
    }

    Customers customers = context.getBean(Customers.class);
    System.out.println(customers);

    Orders orders = context.getBean(Orders.class);
    System.out.println(orders);
    
    System.out.println("是否单例:" + (customers == orders.getCustomers()));
}

insert image description here

  1. The method name of the @Bean annotation is the default bean name, and the value configuration item of @Bean can modify the bean name .
  2. The @Bean annotation is used in the configuration class to register components for the container on the method, which is also a single instance by default .
  3. When the proxyBeanMethods configuration item of the @Configuration annotation is false, the Bean is not a singleton mode, and the default value is true .

3.5.2 @Import

  • The @Import annotation implements adding instances to Spring's IOC container through fast import.
  • @Import can be used to import third-party packages, and of course @Bean annotations can also be used, but the way @Import annotations are quickly imported is more convenient.
  • The three usages of @Import mainly include:
    1. Class array method;
    2. ImportSelector method;
    3. ImportBeanDefinitionRegistrar method;

3.5.2.1 Class array

Fill in the class array directly, and the Class array can contain 0 or more.

Bean class:

public class Dog {
    
    
}

public class Cat {
    
    
}

Import in configuration class:

@Configuration
@Import({
    
    Dog.class, Cat.class})
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        return new Orders("Tom's Order", getCustomers());
    }
}

insert image description here

The corresponding imported beans will be added to the spring container, and the bean names in the container are the full class names of the class .

3.5.2.2 ImportSelector

The implementation class of the ImportSelector interface is imported through the @Import annotation, and the Bean object that needs to be registered is defined in its abstract method selectImports.

Bean class:

public class Student {
    
    
}

public class Teacher {
    
    
}

The implementation class of the ImportSelector interface:

public class MyImportSelector implements ImportSelector {
    
    
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    
    
        return new String[]{
    
    "com.newcapec.bean.Student", "com.newcapec.bean.Teacher"};
    }
}

The selectImports method of the ImportSelector interface:

  • Return value String[]: a string array representing the full class name of the component registered in the container .
  • Parameter AnnotationMetadata: Indicates all annotation information currently annotated by @Import.

Import in configuration class:

@Configuration
@Import({
    
    Dog.class, Cat.class, MyImportSelector.class})
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        return new Orders("Tom's Order", getCustomers());
    }
}

insert image description here

It should be noted that the selectImports method can return an empty array but cannot return null, otherwise a null pointer exception will be reported.

3.5.2.3 ImportBeanDefinitionRegistrar

The implementation class of the ImportBeanDefinitionRegistrar interface is imported through the @Import annotation, and the Bean objects that need to be registered are defined in its abstract method registerBeanDefinitions. Similar to the usage of ImportSelector, except that this usage can customize the registered Bean.

Bean class:

public class Users {
    
    
}

The implementation class of the ImportBeanDefinitionRegistrar interface:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
    

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    

        //指定Bean定义信息
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Users.class);
        //注册一个Bean指定bean名称
        registry.registerBeanDefinition("users", rootBeanDefinition);
    }
}

The registerBeanDefinitions method of the ImportBeanDefinitionRegistrar interface:

  • Parameter AnnotationMetadata: Indicates all annotation information currently annotated by @Import.
  • Parameter BeanDefinitionRegistry: Indicates that a Bean is defined for registration.

Import in configuration class:

@Configuration
@Import({
    
    Dog.class, Cat.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        return new Orders("Tom's Order", getCustomers());
    }
}

3.5.3 @Conditional

The @Conditional annotation can be used on any type or method. Some conditional judgments can be configured through the @Conditional annotation. When all conditions are met, the target marked by @Conditional will be processed by the spring container.

3.5.3.1 Derived annotations

insert image description here

3.5.3.2 Usage analysis

@Conditional derived annotation Function (both judge whether the specified conditions are met)
@ConditionalOnJava Whether the java version of the system meets the requirements
@ConditionalOnBean There is a specified Bean class
@ConditionalOnMissingBean no bean class specified
@ConditionalOnExpression matches the specified SpEL expression
@ConditionalOnClass has a specified class
@ConditionalOnMissingClass no class specified
@ConditionalOnSingleCandidate The container has only one specified bean, or this bean is the preferred bean
@ConditionalOnProperty The specified property attribute has the specified value
@ConditionalOnResource The specified resource exists under the path
@ConditionalOnWebApplication The system environment is the web environment
@ConditionalOnNotWebApplication The system environment is not a web environment
@ConditionalOnjndi The specified item exists in JNDI

3.5.3.3 Case

Bean class:

public class Color {
    
    
}

public class Red {
    
    
}

public class Green {
    
    
}

Configuration class:

@Configuration
@Import({
    
    Dog.class, Cat.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        return new Orders("Tom's Order", getCustomers());
    }

    @Bean("color")
    public Color getColor(){
    
    
        return new Color();
    }

    @ConditionalOnBean(name = "color")
    @Bean("red")
    public Red getRed(){
    
    
        return new Red();
    }

    @ConditionalOnMissingBean(name = "color")
    @Bean("green")
    public Green getGreen(){
    
    
        return new Green();
    }
}

test:

public static void main(String[] args) {
    
    
    //获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
    ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);

    //获取Spring中加载的Bean
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
    
    
        System.out.println(name);
    }

    Customers customers = context.getBean(Customers.class);
    System.out.println(customers);

    Orders orders = context.getBean(Orders.class);
    System.out.println(orders);

    System.out.println("是否单例:" + (customers == orders.getCustomers()));

    boolean color = context.containsBean("color");
    System.out.println("容器中是否包含color组件:" + color);

    boolean red = context.containsBean("red");
    System.out.println("容器中是否包含red组件:" + red);

    boolean green = context.containsBean("green");
    System.out.println("容器中是否包含green组件:" + green);
}

When the color Bean exists:

容器中是否包含color组件:true
容器中是否包含red组件:true
容器中是否包含green组件:false

When the color Bean does not exist:

容器中是否包含color组件:false
容器中是否包含red组件:false
容器中是否包含green组件:true

3.6 Springboot startup analysis

3.6.1 Main program

Adding an annotation @SpringBootApplication to an ordinary Java class will automatically identify it as a Springboot application, and the main method in this class is also the entry method of the Springboot program.

@SpringBootApplication
public class MainApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(MainApplication.class, args);
    }
}

3.6.2 @SpringBootApplication

The main program annotation of the SpringBoot program, the current class is also the main configuration class of SpringBoot.

Source code of @SpringBootApplication annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    
     @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    
    

}

There are three annotations @SpringBootConfiguration, @ComponentScan and @EnableAutoConfiguration involved here.

3.6.3 @SpringBootConfiguration

@SpringBootConfiguration is the configuration class annotation of the Springboot program.

Source code of @SpringBootConfiguration annotation:

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    
    

}

Source code of @Configuration annotation:

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    
    

}

analyze:

  • The @Configuration annotation is an annotation provided by the Spring framework to identify a class as a configuration class.
  • @SpringBootConfiguration is an annotation provided by Springboot, indicating that this is a Springboot configuration class.
  • The configuration class is also a component in the container, which is the role of the @Component annotation.

3.6.4 @ComponentScan

Source code of @ComponentScan annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    
    

}

analyze:

  • Specifies to scan the package, and scan the classes under the package and its subpackages.
  • @Repeatable(ComponentScans.class): Indicates that in annotations without @Repeatable annotations, an error will be reported when using the same annotations in the same place. With annotations marked with this meta-annotation, you can use the same annotations in the same place. The @ComponentScan annotation can be used multiple times within the @ComponentScans annotation.

3.6.5 @EnableAutoConfiguration

Turn on the automatic configuration function.

In the previous Spring era, the xml content needed to be configured; now Springboot helps us configure it automatically, and @EnableAutoConfiguration tells SpringBoot to enable the automatic configuration function.

Source code of @EnableAutoConfiguration annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
    

}

Two annotations @AutoConfigurationPackage and @Import(AutoConfigurationImportSelector.class) are involved here.

3.6.6 @AutoConfigurationPackage

Automatic configuration package, specifying the default package rules.

Source code of @AutoConfigurationPackage annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    
    

}

Here again the @Import(AutoConfigurationPackages.Registrar.class) annotation is involved.

3.6.7 @Import(AutoConfigurationPackages.Registrar.class)

Use the @Import annotation to import an AutoConfigurationPackages.Registrar class to register a series of components in the container.

Source code of AutoConfigurationPackages.Registrar:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
    

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    
    
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
    
    
        return Collections.singleton(new PackageImports(metadata));
    }
}

Using the breakpoint debugging function, you can know the package where the class marked with the @SpringBootApplication annotation is located and all the classes under the sub-package, and automatically scan and register it in the Spring container. In the xml file used when learning Spring before, the function of the component scanning base package is the same.

insert image description here

3.6.8 @Import(AutoConfigurationImportSelector.class)

Use the @Import annotation to import an AutoConfigurationImportSelector class to register a series of components in the container.

Part of the source code of AutoConfigurationImportSelector:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    
    
    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    
    
        if (!isEnabled(annotationMetadata)) {
    
    
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
            
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    
    
        if (!isEnabled(annotationMetadata)) {
    
    
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = getConfigurationClassFilter().filter(configurations);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    
    
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
}

3.6.8.1 selectImports method

In line 99 of the source code, getAutoConfigurationEntry(annotationMetadata);some components are imported into the container in batches.

3.6.8.2 getAutoConfigurationEntry method

On line 123 of the source code, the call List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);gets all the components that are ready to be imported into the container. These components are automatic configuration classes, which are to import all the components required by a certain scene (Starter) into the container and configure these components. With the automatic configuration class, it saves us the work of manually writing configuration injection function components and so on.

insert image description here

3.6.8.3 Factory Loader

In line 178 of the source code, SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());all components are obtained by loading the factory.

3.6.8.4 spring.factories file

The source code of the loadFactoryNames() method in the SpringFactoriesLoader class:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    
    
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
    
    
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    
    
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
    
    
        return result;
    } else {
    
    
        HashMap result = new HashMap();

        try {
    
    
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");

            while(urls.hasMoreElements()) {
    
    
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
    
    
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
    
    
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
    
    
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }

            result.replaceAll((factoryType, implementations) -> {
    
    
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
    
    
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

Check the source code to know that Springboot loads the META-INF/spring.factories file under the class path by default.

insert image description here

Find the META-INF/spring.factories file in the spring-boot-autoconfigure-2.6.9.jar package.

From line 25 to line 158, there are a total of 133 components to be imported, which is consistent with the number displayed in the debugging.

insert image description here

3.6.8.5 Enable automatic configuration on demand

Although 130 automatic configurations are loaded by default, Springboot will still assemble the rules @Conditional according to the conditions, and finally configure them on demand. When the code is executed to line 129, we can see that there are only 24 auto-configuration classes that need to be imported.

insert image description here

3.6.8.6 Modify the default configuration

@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
    
    
    // Detect if the user has created a MultipartResolver but named it incorrectly
    return resolver;
}
  • Springboot will configure all the components at the bottom by default, but if the user configures it himself, the user's priority will be given.
  • The formal parameters of the @Bean annotated method can look up the component in the container.

3.6.8.5 Example 1

Just look for one, here is RedisAutoConfiguration, see how redis is configured

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({
    
     LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    
    

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    
    
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
    
    
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

analyze:

  • @Configuration: Indicates that this class is a configuration class.
  • @ConditionalOnClass(RedisOperations.class): This configuration is enabled if the RedisOperations class exists.
  • @EnableConfigurationProperties(RedisProperties.class): Some configuration parameters of redis are initialized in the RedisProperties class. The configuration written in yml or properties is the corresponding logic of this class.
  • @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) : Indicates the configuration of importing redis connection.

3.6.8.6 Example 2

Here is DispatcherServletAutoConfiguration to see how DispatcherServlet is configured

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
    
    

	/**
	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/".
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

	/**
	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

	@Configuration(proxyBeanMethods = false)
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	protected static class DispatcherServletConfiguration {
    
    

	}

	@Configuration(proxyBeanMethods = false)
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {
    
    

	}

	@Order(Ordered.LOWEST_PRECEDENCE - 10)
	private static class DefaultDispatcherServletCondition extends SpringBootCondition {
    
    

	}

	@Order(Ordered.LOWEST_PRECEDENCE - 10)
	private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
    
    

	}

}

analyze:

  • @ConfigurationThe configuration class is also a Bean, but for the configuration class, the execution order in some scenarios is necessary and needs to be guaranteed. Compared with Spring, the management of automatic configuration under Spring Boot is a black box. It will dynamically judge whether the automatic configuration class is loaded or not, and the order of loading according to the current situation in the container, so it can be said: Spring Boot’s Automatically configuring it has a strong requirement on the order. Driven by demand, Spring Boot provides us with @AutoConfigureBefore, @AutoConfigureAfter, @AutoConfigureOrder (these three annotations are collectively referred to as "three annotations") to help us solve this demand.
  • @AutoConfigureOrderAdded in version 1.3.0, it indicates absolute order (the smaller the number, the higher the priority).
  • @AutoConfigureBefore, @AutoConfigureAfterto control the order of configuration , indicating that it is loaded after or before a certain configuration; DispatcherServletAutoConfigurationthe premise of being loaded is: ServletWebServerFactoryAutoConfigurationinitialization has been completed.
  • Four internal classes are defined in DispatcherServletAutoConfiguration. Only when the configuration of DispatcherServletAutoConfiguration takes effect can the internal class configuration conditions be judged.

Summarize:

  • Springboot first loads all automatic configuration classes xxxxxAutoConfiguration.
  • Each automatic configuration class takes effect according to conditions. By default, the value specified by the configuration file will be bound, obtained from the xxxxProperties class, and xxxProperties is bound to the configuration file.
  • The effective configuration class will assemble many components in the container, as long as there are these components in the container, it is equivalent to these functions.
  • Customized configuration:
    • Users directly replace the underlying components with @Bean.
    • When the user sees the value of the configuration file obtained by this component, he modifies it.

Guess you like

Origin blog.csdn.net/ligonglanyuan/article/details/126125992