天天看點

SpringBoot 系列-自動配置及 starter 機制解析

本篇主要來讨論研究兩個問題:1、什麼自動配置,2、如何編寫自動配置

在使用 Spring 作為項目開發架構的過程中,當需要內建某個元件時,通常需要大量的 xml 配置才可以讓項目工程 run 起來,下面先以 mybatis 為例,來看下如何使用 mybatis-Spring 子產品,需要哪些必不可少的依賴和配置。

使用 mybatis-spring

任何元件的內建都繞不過兩個問題:依賴和配置,關于配置在[]()這篇文章中介紹了配置的一些點,有興趣的可以看下。

依賴

從 mybatis 的官方文當可以了解到,要使用 MyBatis-Spring 子產品,需要在類路徑下包含 mybatis-spring.jar 檔案和相關依賴(如:mysql-connector-java)即可。如果使用 Maven 作為建構工具,則在 pom.xml 中加入以下代碼即可:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>${latest.version}</version>
</dependency>           

複制

bean 配置

Spirng 內建 mybatis 通常需要以下 bean 配置:

1、dataSource

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
    init-method="init" destroy-method="close">
    // 省略其他配置
</bean>           

複制

2、sqlSessionFactory

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>           

複制

3、其他:包掃描事務配置

<!-- DAO 接口所在包名,Spring 會自動查找其下的類,并将其定義為一個 Spring Bean -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.glmapper.bridge.boot.dao" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>

<!-- (事務管理)transaction manager -->
<bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>           

複制

這些個 bean 是在 Spring 中使用 mybatis 架構時基本必不可少的配置。那麼在 SpringBoot 中呢?

SpringBoot 中如何內建 mybatis 的

SpringBoot 內建 mybatis 非常簡單,加一下下面的 starter ,再在 application.properties 配置下資料庫連接配接配置即可;不需要配置 datasource,sqlSessionFactory 等這些 bean。

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

複制

官方文檔:https://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/

mybatis starter 是如何規避 bean 配置的

引用

mybatis-spring-boot-starter

既然可以不用在 xml 中配置 bean ,那肯定是這些 bean 是在

mybatis-spring-boot-starter

中通過某種方式被建立了。

在 SpringBoot 官方文檔的描述中,starter 隻是用來管理依賴的,一般不會有代碼,自動配置的代碼一般在

xxxx-autoconfigure

中。mybatis 的自動配置相關代碼是在

mybatis-spring-boot-autoconfigure

中。

mybatis-spring-boot-autoconfigure

這依賴中隻有簡單的幾個類,其中最核心的就是 MybatisAutoConfiguration 這個配置類。另外一個 MybatisProperties 是 mybatis spring boot 的屬性配置類,就是常見的 mybatis.xxxx。

MybatisAutoConfiguration 自動配置類

MybatisAutoConfiguration 的定義及其生效條件:

  • 1.目前 classpath 下必須有 SqlSessionFactory 和 SqlSessionFactoryBean 這兩個類
  • 2.存在 DataSource bean 執行個體
  • 3.有配置類 MybatisProperties 執行個體
  • 4.在 DataSourceAutoConfiguration 和 MybatisLanguageDriverAutoConfiguration 兩個自動配置類之後重新整理

    @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties(MybatisProperties.class) @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) public class MybatisAutoConfiguration implements InitializingBean { // 定義 SqlSessionFactory bean @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { // } // check @Override public void afterPropertiesSet() { checkConfigFileExists(); } // 省略其他code }

從上面的代碼片段大體可以知道 MybatisAutoConfiguration 所做的事情主要包括以下幾點:1、重新整理 SqlSessionFactory 和 SqlSessionFactoryBean 兩個 bean;2、afterPropertiesSet 中做一些準備或者檢驗工作(這裡就是 check 了 mybatis 的配置檔案是否配置了)

關于 DataSource 的 bean ,則是由 DataSourceAutoConfiguration 這個配置類中來定義。

具體代碼有興趣的讀者可以自己查閱相關源碼,這裡就不展開了。

是以整體看來, MybatisAutoConfiguration 及其所依賴的 xxxConfiguration 會幫助使用者定義 bean 和解析配置。

mybatis 自動配置的 bean 是如何生效的

上面分析到 MybatisAutoConfiguration 及其依賴的配置自動類會幫助建立運作時所需要的 bean,那麼這些 bean 是如何被 SpringBoot 架構感覺并加載的呢?

其實一般的項目工程中,如果我們在一個類上打了 @Configuration 注解的話,Spring 會直接能夠加載到的(前提是這個類所在的包在啟動類的子包下)。但是在架構層面,項目的包和所引入的元件包的包路徑肯定是有差異的,是以在一些情況下會刷不到依賴中的 bean。

SpringBoot 中提供了一種類似于 SPI 機制的方式來幫忙加載 EnableAutoConfiguration、ApplicationListner、ApplicationContextInitializer 等類型的 bean。比如 mybatis 自動配置的配置如下:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration           

複制

其處理邏輯在 SpringApplication 類中,具體解析方法如下:

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    for (String name : names) {
        try {
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            // 反射拿到構造函數
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            // 建立 bean
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        }
        catch (Throwable ex) {
            throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}           

複制

如何編寫自己的 starter

本小節将結合上面的描述,自定義一個 starter,讓你的項目和 xml bean 配置說再見。

場景描述:有兩個 bean,一個 parentBean,一個 childBean,parentBean 需要依賴 childBean,parentBean中又要依賴 http 包

原來的 xml 配置:

<bean id="parentBean" class="com.glmapper.bridge.boot.service.impl.ParentBean">
    <property name="childBean" ref="childBean"></property>
</bean>
<bean id="childBean" class="com.glmapper.bridge.boot.service.impl.ChildBean"/>           

複制

下面考慮的是将這些 bean 作為公共元件提供給其他項目工程用,從架構角度來看,最佳實踐是:

  • 提供一個 autoconfigure 子產品用于編寫自動配置類代碼
  • 提供一個 starter,用于提供給外部使用者使用

編寫 autoconfigure

  • 自動配置類

    @Configuration // parentBean 依賴 HttpClient,是以如果沒有 HttpClient 則不會重新整理目前自動配置類 @ConditionalOnClass(HttpClient.class) public class GlmpperAutoConfiguration { // ParentBean bean 定義 @Bean @ConditionalOnMissingBean // 如果目前 Spring 容器中已經存在 parentBean則不會再建立 public ParentBean parentBean(){ return new ParentBean(); } // ChildBean bean 定義 @Bean @ConditionalOnMissingBean public ChildBean childBean(){ return new ChildBean(); } }

  • 依賴 scope 使用 provided,不直接打在 autoconfigure 依賴中
<dependencies>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.6</version>
        <scope>provided</scope>
    </dependency>
</dependencies>           

複制

  • 編寫 spring.factories,在 resources/META-INF/ 建立一個 spring.factories 檔案,配置如下:
SpringBoot 系列-自動配置及 starter 機制解析

編寫 starter

starter 裡面沒有代碼,隻做依賴管控

<dependency>
    <groupId>com.glmapper.bridge.boot</groupId>
    <artifactId>guides-autoconfigure</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.6</version>
</dependency>           

複制

starter 裡面包括了自動配置的依賴和 httpclient 的依賴,是以使用者在引入 starter 之後所有生效條件都滿足了,就會在啟動時直接重新整理。

示例工程: https://github.com/glmapper/springboot-series-guides.git(guides-autoconfigure 子產品和 guides-starter 子產品)

小結

本篇是介于源碼解析和實踐系列之間的一篇,作為源碼解析的終篇和實踐的開篇。

本篇以 mybatis 為例,對 spring 環境和 SpringBoot 環境下的使用方式做了簡單對比;以此為切入點,介紹了 SpringBoot 中的自動配置及 starter 最佳實踐。

溫馨提示

源碼解析課程告以段落,接下來将進入實戰課程了,大家敬請期待!

▐ 文章來源:磊叔授權分享,轉載請先聯系原作者,謝謝!