glmapper

SpringBoot 系列-自动配置及 starter 机制解析

字数统计: 1.9k阅读时长: 8 min
2020/01/05 Share

一家之言,如有任何错误,请批评指出,不胜感激

本篇主要来讨论研究两个问题:1、什么自动配置,2、如何编写自动配置

在使用 Spring 作为项目开发框架的过程中,当需要集成某个组件时,通常需要大量的 xml 配置才可以让项目工程 run 起来,下面先以 mybatis 为例,来看下如何使用 mybatis-Spring 模块,需要哪些必不可少的依赖和配置。

使用 mybatis-spring

任何组件的集成都绕不过两个问题:依赖和配置,关于配置在这篇文章中介绍了配置的一些点,有兴趣的可以看下。

依赖

从 mybatis 的官方文当可以了解到,要使用 MyBatis-Spring 模块,需要在类路径下包含 mybatis-spring.jar 文件和相关依赖(如:mysql-connector-java)即可。如果使用 Maven 作为构建工具,则在 pom.xml 中加入以下代码即可:

1
2
3
4
5
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${latest.version}</version>
</dependency>

bean 配置

Spirng 集成 mybatis 通常需要以下 bean 配置:

1、dataSource

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

2、sqlSessionFactory

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

3、其他:包扫描和事务配置

1
2
3
4
5
6
7
8
9
10
11
<!-- 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。

1
2
3
4
5
<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 两个自动配置类之后刷新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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 自动配置的配置如下:

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

其处理逻辑在 SpringApplication 类中,具体解析方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 配置:

1
2
3
4
<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

  • 自动配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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 依赖中
1
2
3
4
5
6
7
8
<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 文件,配置如下:
1
2
3
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.glmapper.bridge.boot.autoconfigure.GlmpperAutoConfiguration

编写 starter

starter 里面没有代码,只做依赖管控

1
2
3
4
5
6
7
8
9
10
11
<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 最佳实践。

原文作者:GuoLei Song

原文链接:http://www.glmapper.com/2020/01/05/springboot-series-auto-configure/

发表日期:January 5th 2020, 5:50:13 pm

更新日期:January 10th 2020, 10:12:29 am

版权声明:转载请注明出处

CATALOG
  1. 1. 使用 mybatis-spring
    1. 1.1. 依赖
  2. 2. bean 配置
  3. 3. SpringBoot 中如何集成 mybatis 的
  4. 4. mybatis starter 是如何规避 bean 配置的
    1. 4.1. MybatisAutoConfiguration 自动配置类
  5. 5. mybatis 自动配置的 bean 是如何生效的
  6. 6. 如何编写自己的 starter
    1. 6.1. 编写 autoconfigure
    2. 6.2. 编写 starter
  7. 7. 小结