一家之言,如有任何错误,请批评指出,不胜感激
本篇主要来讨论研究两个问题: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 文件,配置如下:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.glmapper.bridge.boot.autoconfigure.GlmpperAutoConfiguration
编写 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 最佳实践。
image
网友评论