SpringBoot start mechanism (starter mechanism) Detailed core principles

Author: MyBug

I. Introduction

Used springboot students should already know, springboot help us greatly simplifies the development process and the project to build the initial default configuration through a lot of the framework of use.
The purpose of this paper is a step by step analysis process springboot start, this time is to analyze the characteristics springboot automatic assembly.
Well first of all lead us to recall the past, our web project is how to build, usually we have to build a Spring-based Web applications, we need to do some of the following:
the relevant jar package pom file introduced, including spring, springmvc, redis, mybaits , log4j, mysql-connector-java-related jar ... and so
configured web.xml, Listener configuration, Filter configuration, Servlet configuration, log4j configuration, error configuration ...
configure a database connection configuration spring Affairs
configuration view resolver
open annotation, auto scan function
after the completion of the deployment configuration tomcat, start debugging
......
spent to build an initial project, may or half an hour later saved, but everything will be followed by a SpringBoot become very easy, let's first analyze SpringBoot start dependent and automatic configuration.

Second, start dependence

1. The introduction of the jar in our pom files inside:

 <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.0.4.RELEASE</version>
       <relativePath /> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.example</groupId>
   <artifactId>demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>demo</name>
   <description>Demo project for Spring Boot</description>

   <properties>
       <java.version>1.8</java.version>
   </properties>

   <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>

       <!--mybatis 开发包 -->
       <dependency>
           <groupId>org.mybatis.spring.boot</groupId>
           <artifactId>mybatis-spring-boot-starter</artifactId>
           <version>1.3.2</version>
       </dependency>
       <!--springboot web模块支持 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <scope>runtime</scope>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
       </plugins>
   </build>

spring-boot-starter-web package automatically help us to introduce the relevant jar package web module development needs.
mybatis-spring-boot-starter introduced dao help us develop relevant jar package.
spring-boot-starter-xxx is the official starter, xxx-spring-boot-starter is a starter provided by third parties.
Screenshot look at our mybatis-spring-boot-starter
SpringBoot start mechanism (starter mechanism) Detailed core principles

It can be seen mybatis-spring-boot-starter does not have any source code, only a pom file, its role is to help us to introduce other jar.
Configuring Data Source

spring:
 datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    hikari:
     # 最小空闲连接数量
     minimum-idle: 5
     # 连接池最大连接数,默认是10
     maximum-pool-size: 60
     # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
     auto-commit: true
     # 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟
     idle-timeout: 600000
     # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
     max-lifetime: 1800000
     # 数据库连接超时时间,默认30秒,即30000
     connection-timeout: 60000

stater mechanism to help us complete the relevant jar package needed to start the project. That problem again, the traditional spring application configuration application.xml not to do in a lot of bean, such as dataSource configuration, transactionManager configuration ... how springboot is to help us accomplish these bean configuration?
Let's analyze this process

Third, automatic configuration

1. Based on java bean configuration code
to mybatis for example, in the screenshot above, we found mybatis-spring-boot-starter introduced into the envelope to help us mybatis-spring-boot-autoconfigure the envelope, as shown below:
SpringBoot start mechanism (starter mechanism) Detailed core principles

There are MybatisAutoConfiguration this class, open class and see some what.
SpringBoot start mechanism (starter mechanism) Detailed core principles

Familiar @Configuration &, @ Bean bean two students probably already know. Annotations using these two together can create a java class code based on the configuration, it can be used to replace the corresponding xml configuration file.
@Configuration annotation class can be seen as capable of producing so that the Spring IoC container management Bean instance factory. br /> @ Bean annotation tells Spring, with a @Bean annotation method returns an object that should be registered in the spring container.
So the above MybatisAutoConfiguration this class, automatically help us generate significant examples of these Mybatis SqlSessionFactory and container management to the spring, thus completing the automatic registration bean.

2. autoconfiguration condition depends on
the use of this class from MybatisAutoConfiguration annotation can be seen that, to complete the automatic configuration is dependent condition.

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {

 private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);

 private final MybatisProperties properties;

 private final Interceptor[] interceptors;

 private final ResourceLoader resourceLoader;

 private final DatabaseIdProvider databaseIdProvider;

 private final List<ConfigurationCustomizer> configurationCustomizers;
 ......

First prep about Springboot is a common condition depends Annotations are: br /> @ ConditionalOnBean, when there is only a single bean in the current context, will instantiate Bean.
@ConditionalOnClass, a class on the classpath, will instantiate Bean.
br /> @ ConditionalOnExpression, when the expression is true, when will instantiate Bean.
@ConditionalOnMissingBean, when only one bean does not exist in the current context, it will instantiate Bean.
br /> @ ConditionalOnMissingClass, a class on the class path does not exist, when will instantiate Bean.
@ConditionalOnNotWebApplication, not a web Bean will instantiate this application.
br /> @ AutoConfigureAfter, a bean in the bean after the instantiation of the auto-configuration.
@AutoConfigureBefore, in an instance of this bean bean completed before the automatic configuration.
To complete the automatic configuration Mybatis therefore, it is necessary in the presence SqlSessionFactory.class classpath, SqlSessionFactoryBean.class these two classes, requires the presence of the bean and the bean DataSource done automatically registered.
DataSourceAutoConfiguration enter this class, you can see that this class belongs to the package:
org.springframework.boot.autoconfigure.jdbc
This package also belongs to spring-boot-autoconfigure-2.0.4.RELEASE.jar this package, automatically configure this package to help the introduction of jdbc, kafka, logging, mail, mongo packets. We need a lot of pack after the introduction of the corresponding jar command will take effect automatically.
SpringBoot start mechanism (starter mechanism) Detailed core principles

3.Bean parameters to obtain
this we already know the configuration process the bean, but have yet to see how springboot read properites or property yml configuration file to create a data source?
In DataSourceAutoConfiguration class which, we note that the use of the EnableConfigurationProperties this comment.

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
        DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

    @Configuration
    @Conditional(EmbeddedDatabaseCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import(EmbeddedDataSourceConfiguration.class)
    protected static class EmbeddedDatabaseConfiguration {

    }
......

DataSourceProperties encapsulated in each attribute data sources, and using the prefix specified annotation ConfigurationProperties profile.

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {

    private ClassLoader classLoader;

    /**
     * Name of the datasource. Default to "testdb" when using an embedded database.
     */
    private String name;

    /**
     * Whether to generate a random datasource name.
     */
    private boolean generateUniqueName;

    /**
     * Fully qualified name of the connection pool implementation to use. By default, it
     * is auto-detected from the classpath.
     */
    private Class<? extends DataSource> type;

    /**
     * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
     */
    private String driverClassName;

    /**
     * JDBC URL of the database.
     */
    private String url;

    /**
     * Login username of the database.
     */
    private String username;

    /**
     * Login password of the database.
     */
    private String password;

    /**
     * JNDI location of the datasource. Class, url, username & password are ignored when
     * set.
     */
    private String jndiName;

    /**
     * Initialize the datasource with available DDL and DML scripts.
     */
    private DataSourceInitializationMode initializationMode = DataSourceInitializationMode.EMBEDDED;

    /**
     * Platform to use in the DDL or DML scripts (such as schema-${platform}.sql or
     * data-${platform}.sql).
     */
    private String platform = "all";

    /**
     * Schema (DDL) script resource references.
     */
    private List<String> schema;

    /**
     * Username of the database to execute DDL scripts (if different).
     */
    private String schemaUsername;

    /**
     * Password of the database to execute DDL scripts (if different).
     */
    private String schemaPassword;

    /**
     * Data (DML) script resource references.
     */
    private List<String> data;

    ......

Through the above analysis, we can see that: br /> annotation @ Configuration Properties is the role of yml or properties configuration file into bean.
@EnableConfigurationProperties annotation role is to make @ConfigurationProperties annotations take effect. If only @ConfigurationProperties notes, the container is in the spring or properties acquired less than yml configuration files into the bean.
In this way, the yml configuration parameters or properties into a bean, bean and how these were found and loaded?

4.Bean found
springboot default for all classes and subclasses of the main components of the package under the category where the scanning start, but did not include dependencies in the class, then the dependencies of how the bean is found and loaded?
We usually add @SpringBootApplication this annotation startup class, the point went to see

@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 {

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
     ......

实际上重要的只有三个Annotation:br/>@Configuration(@SpringBootConfiguration里面还是应用了@Configuration)
@EnableAutoConfiguration
br/>@ComponentScan
@Configuration的作用上面我们已经知道了,被注解的类将成为一个bean配置类。
br/>@ComponentScan的作用就是自动扫描并加载符合条件的组件,比如@Component和@Repository等,最终将这些bean定义加载到spring容器中。
@EnableAutoConfiguration 这个注解的功能很重要,借助@Import的支持,收集和注册依赖包中相关的bean定义。

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

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    String[] excludeName() default {};

}

如上源码,@EnableAutoConfiguration注解引入了@AutoConfigurationPackage和@Import这两个注解。@AutoConfigurationPackage的作用就是自动配置的包,@Import导入需要自动配置的组件。

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

}

~

/**
 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
 * configuration.
 */
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        register(registry, new PackageImport(metadata).getPackageName());
    }

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

}

new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()
new AutoConfigurationPackages.PackageImport(metadata)
这两句代码的作用就是加载启动类所在的包下的主类与子类的所有组件注册到spring容器,这就是前文所说的springboot默认扫描启动类所在的包下的主类与子类的所有组件。
那问题又来了,要搜集并注册到spring容器的那些beans来自哪里?
进入 AutoConfigurationImportSelector类,我们可以发现SpringFactoriesLoader.loadFactoryNames方法调用loadSpringFactories方法从所有的jar包中读取META-INF/spring.factories文件信息。
下面是spring-boot-autoconfigure这个jar中spring.factories文件部分内容,其中有一个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值定义了需要自动配置的bean,通过读取这个配置获取一组@Configuration类。

org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

每个xxxAutoConfiguration都是一个基于java的bean配置类。实际上,这些xxxAutoConfiguratio不是所有都会被加载,会根据xxxAutoConfiguration上的@ConditionalOnClass等条件判断是否加载;通过反射机制将spring.factories中@Configuration类实例化为对应的java实列。
到此我们已经知道怎么发现要自动配置的bean了,最后一步就是怎么样将这些bean加载到spring容器。

5.Bean 加载
如果要让一个普通类交给Spring容器管理,通常有以下方法:
使用 @Configuration与@Bean 注解
使用@Controller @Service @Repository @Component 注解标注该类,然后启用@ComponentScan自动扫描
使用@Import 方法
springboot中使用了@Import 方法
br/>@EnableAutoConfiguration注解中使用了@Import({AutoConfigurationImportSelector.class})注解,AutoConfigurationImportSelector实现了DeferredImportSelector接口,
DeferredImportSelector接口继承了ImportSelector接口,ImportSelector接口只有一个selectImports方法。
selectImports方法返回一组bean,@EnableAutoConfiguration注解借助@Import注解将这组bean注入到spring容器中,springboot正式通过这种机制来完成bean的注入的。

四、总结

We can automatically configure the key steps and corresponding comments are summarized as follows: br /> & @ the Configuration and @Bean ------ >>> bean configuration java code-based
@Conditional --------> >>>>> automatic configuration setting conditions depend
br /> @ EnableConfigurationProperties with @ ConfigurationProperties-> reads the configuration file into a bean.
@ EnableAutoConfiguration, @ AutoConfigurationPackage with @ Import-> find and load the bean implementation.

Welcome to share with everyone, like the point of a praise yo remember the article, thanks for the support!

Guess you like

Origin blog.51cto.com/14442094/2427783