SpringBoot啟動(dòng)機(jī)制(starter機(jī)制)核心原理詳解

作者:MyBug

成都創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的墨玉網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

一、前言

使用過springboot的同學(xué)應(yīng)該已經(jīng)知道,springboot通過默認(rèn)配置了很多框架的使用方式幫我們大大簡化了項(xiàng)目初始搭建以及開發(fā)過程。
本文的目的就是一步步分析springboot的啟動(dòng)過程,這次主要是分析springboot特性自動(dòng)裝配。
那么首先帶領(lǐng)大家回顧一下以往我們的web項(xiàng)目是如何搭建的,通常我們要搭建一個(gè)基于Spring的Web應(yīng)用,我們需要做以下一些工作:
pom文件中引入相關(guān)jar包,包括spring、springmvc、redis、mybaits、log4j、MySQL-connector-java 等等相關(guān)jar …
配置web.xml,Listener配置、Filter配置、Servlet配置、log4j配置、error配置 …
配置數(shù)據(jù)庫連接、配置spring事務(wù)
配置視圖解析器
開啟注解、自動(dòng)掃描功能
配置完成后部署tomcat、啟動(dòng)調(diào)試
……
花在搭建一個(gè)初始項(xiàng)目,可能一個(gè)小時(shí)就過去了或者半天救過了,但是用了SpringBoot之后一切都會(huì)變得非常便捷,下面我們首先來分析一下SpringBoot的起步依賴以及自動(dòng)配置。

二、起步依賴

1.在我們的pom文件里面引入以下jar:

 <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 開發(fā)包 -->
       <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包自動(dòng)幫我們引入了web模塊開發(fā)需要的相關(guān)jar包。
mybatis-spring-boot-starter幫我們引入了dao開發(fā)相關(guān)的jar包。
spring-boot-starter-xxx是官方提供的starter,xxx-spring-boot-starter是第三方提供的starter。
截圖看一下我們的mybatis-spring-boot-starter
SpringBoot啟動(dòng)機(jī)制(starter機(jī)制)核心原理詳解

可以看出mybatis-spring-boot-starter并沒有任何源碼,只有一個(gè)pom文件,它的作用就是幫我們引入其它jar。
2.配置數(shù)據(jù)源

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:
     # 最小空閑連接數(shù)量
     minimum-idle: 5
     # 連接池最大連接數(shù),默認(rèn)是10
     maximum-pool-size: 60
     # 此屬性控制從池返回的連接的默認(rèn)自動(dòng)提交行為,默認(rèn)值:true
     auto-commit: true
     # 一個(gè)連接idle狀態(tài)的最大時(shí)長(毫秒),超時(shí)則被釋放(retired),缺省:10分鐘
     idle-timeout: 600000
     # 此屬性控制池中連接的最長生命周期,值0表示無限生命周期,默認(rèn)1800000即30分鐘
     max-lifetime: 1800000
     # 數(shù)據(jù)庫連接超時(shí)時(shí)間,默認(rèn)30秒,即30000
     connection-timeout: 60000

stater機(jī)制幫我們完成了項(xiàng)目起步所需要的的相關(guān)jar包。那問題又來了,傳統(tǒng)的spring應(yīng)用中不是要在application.xml中配置很多bean的嗎,比如dataSource的配置,transactionManager的配置 … springboot是如何幫我們完成這些bean的配置的?
下面我們來分析這個(gè)過程

三、自動(dòng)配置

1.基于java代碼的bean配置
以mybatis為例,在上面的截圖中,我們發(fā)現(xiàn)mybatis-spring-boot-starter這個(gè)包幫我們引入了mybatis-spring-boot-autoconfigure這個(gè)包,如下圖:
SpringBoot啟動(dòng)機(jī)制(starter機(jī)制)核心原理詳解

里面有MybatisAutoConfiguration這個(gè)類,打開這個(gè)類看看有些什么東西。
SpringBoot啟動(dòng)機(jī)制(starter機(jī)制)核心原理詳解

熟悉@Configuration&、@Bean這兩個(gè)bean的同學(xué)或許已經(jīng)知道了。這兩個(gè)注解一起使用就可以創(chuàng)建一個(gè)基于java代碼的配置類,可以用來替代相應(yīng)的xml配置文件。
@Configuration注解的類可以看作是能生產(chǎn)讓Spring IoC容器管理的Bean實(shí)例的工廠。@Bean注解告訴Spring,一個(gè)帶有@Bean的注解方法將返回一個(gè)對(duì)象,該對(duì)象應(yīng)該被注冊(cè)到spring容器中。
br/>@Bean注解告訴Spring,一個(gè)帶有@Bean的注解方法將返回一個(gè)對(duì)象,該對(duì)象應(yīng)該被注冊(cè)到spring容器中。

2.自動(dòng)配置條件依賴
從MybatisAutoConfiguration這個(gè)類中使用的注解可以看出,要完成自動(dòng)配置是有依賴條件的。

@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;
 ......

首先預(yù)習(xí)一下Springboot是常用的條件依賴注解有:@ConditionalOnBean,僅在當(dāng)前上下文中存在某個(gè)bean時(shí),才會(huì)實(shí)例化這個(gè)Bean。
br/>@ConditionalOnBean,僅在當(dāng)前上下文中存在某個(gè)bean時(shí),才會(huì)實(shí)例化這個(gè)Bean。
br/>@ConditionalOnExpression,當(dāng)表達(dá)式為true的時(shí)候,才會(huì)實(shí)例化這個(gè)Bean。
br/>@ConditionalOnMissingClass,某個(gè)class在類路徑上不存在的時(shí)候,才會(huì)實(shí)例化這個(gè)Bean。
br/>@AutoConfigureAfter,在某個(gè)bean完成自動(dòng)配置后實(shí)例化這個(gè)bean。
所以要完成Mybatis的自動(dòng)配置,需要在類路徑中存在SqlSessionFactory.class、SqlSessionFactoryBean.class這兩個(gè)類,需要存在DataSource這個(gè)bean且這個(gè)bean完成自動(dòng)注冊(cè)。
進(jìn)入DataSourceAutoConfiguration這個(gè)類,可以看到這個(gè)類屬于這個(gè)包:
org.springframework.boot.autoconfigure.jdbc
這個(gè)包又屬于spring-boot-autoconfigure-2.0.4.RELEASE.jar這個(gè)包,自動(dòng)配置這個(gè)包幫們引入了jdbc、kafka、logging、mail、mongo等包。很多包需要我們引入相應(yīng)jar后自動(dòng)配置才生效。
SpringBoot啟動(dòng)機(jī)制(starter機(jī)制)核心原理詳解

3.Bean參數(shù)的獲取
到此我們已經(jīng)知道了bean的配置過程,但是還沒有看到springboot是如何讀取yml或者properites配置文件的的屬性來創(chuàng)建數(shù)據(jù)源的?
在DataSourceAutoConfiguration類里面,我們注意到使用了EnableConfigurationProperties這個(gè)注解。

@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中封裝了數(shù)據(jù)源的各個(gè)屬性,且使用了注解ConfigurationProperties指定了配置文件的前綴。

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

    ......

通過以上分析,我們可以得知:@ConfigurationProperties注解的作用是把yml或者properties配置文件轉(zhuǎn)化為bean。
br/>@ConfigurationProperties注解的作用是把yml或者properties配置文件轉(zhuǎn)化為bean。
通過這種方式,把yml或者properties配置參數(shù)轉(zhuǎn)化為bean,這些bean又是如何被發(fā)現(xiàn)與加載的?

4.Bean的發(fā)現(xiàn)
springboot默認(rèn)掃描啟動(dòng)類所在的包下的主類與子類的所有組件,但并沒有包括依賴包的中的類,那么依賴包中的bean是如何被發(fā)現(xiàn)和加載的?
我們通常在啟動(dòng)類中加@SpringBootApplication這個(gè)注解,點(diǎn)進(jìn)去看

@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
     */
     ......

實(shí)際上重要的只有三個(gè)Annotation:@Configuration(@SpringBootConfiguration里面還是應(yīng)用了@Configuration)
br/>@Configuration(@SpringBootConfiguration里面還是應(yīng)用了@Configuration)
br/>@ComponentScan
br/>@ComponentScan的作用就是自動(dòng)掃描并加載符合條件的組件,比如@Component和@Repository等,最終將這些bean定義加載到spring容器中。

@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這兩個(gè)注解。@AutoConfigurationPackage的作用就是自動(dòng)配置的包,@Import導(dǎo)入需要自動(dòng)配置的組件。

@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)
這兩句代碼的作用就是加載啟動(dòng)類所在的包下的主類與子類的所有組件注冊(cè)到spring容器,這就是前文所說的springboot默認(rèn)掃描啟動(dòng)類所在的包下的主類與子類的所有組件。
那問題又來了,要搜集并注冊(cè)到spring容器的那些beans來自哪里?
進(jìn)入 AutoConfigurationImportSelector類,我們可以發(fā)現(xiàn)SpringFactoriesLoader.loadFactoryNames方法調(diào)用loadSpringFactories方法從所有的jar包中讀取META-INF/spring.factories文件信息。
下面是spring-boot-autoconfigure這個(gè)jar中spring.factories文件部分內(nèi)容,其中有一個(gè)key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的值定義了需要自動(dòng)配置的bean,通過讀取這個(gè)配置獲取一組@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,\

每個(gè)xxxAutoConfiguration都是一個(gè)基于java的bean配置類。實(shí)際上,這些xxxAutoConfiguratio不是所有都會(huì)被加載,會(huì)根據(jù)xxxAutoConfiguration上的@ConditionalOnClass等條件判斷是否加載;通過反射機(jī)制將spring.factories中@Configuration類實(shí)例化為對(duì)應(yīng)的java實(shí)列。
到此我們已經(jīng)知道怎么發(fā)現(xiàn)要自動(dòng)配置的bean了,最后一步就是怎么樣將這些bean加載到spring容器。

5.Bean 加載
如果要讓一個(gè)普通類交給Spring容器管理,通常有以下方法:
使用 @Configuration與@Bean 注解
使用@Controller @Service @Repository @Component 注解標(biāo)注該類,然后啟用@ComponentScan自動(dòng)掃描
使用@Import 方法
springboot中使用了@Import 方法@EnableAutoConfiguration注解中使用了@Import({AutoConfigurationImportSelector.class})注解,AutoConfigurationImportSelector實(shí)現(xiàn)了DeferredImportSelector接口,
br/>@EnableAutoConfiguration注解中使用了@Import({AutoConfigurationImportSelector.class})注解,AutoConfigurationImportSelector實(shí)現(xiàn)了DeferredImportSelector接口,
selectImports方法返回一組bean,@EnableAutoConfiguration注解借助@Import注解將這組bean注入到spring容器中,springboot正式通過這種機(jī)制來完成bean的注入的。

四、總結(jié)

我們可以將自動(dòng)配置的關(guān)鍵幾步以及相應(yīng)的注解總結(jié)如下:@Configuration&與@Bean------>>>基于java代碼的bean配置
br/>@Configuration&與@Bean------>>>基于java代碼的bean配置
br/>@EnableConfigurationProperties與@ConfigurationProperties->讀取配置文件轉(zhuǎn)換為bean。

歡迎大家一起交流,喜歡文章記得點(diǎn)個(gè)贊喲,感謝支持!

網(wǎng)站名稱:SpringBoot啟動(dòng)機(jī)制(starter機(jī)制)核心原理詳解
本文網(wǎng)址:http://muchs.cn/article44/ishdee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、網(wǎng)站營銷靜態(tài)網(wǎng)站外貿(mào)建站、網(wǎng)頁設(shè)計(jì)公司、網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化