写在前面: 在沸点列了个小目标 - 2020 立个 Flag,掘金等级到 6 级,原创文章数 120+ 篇幅!:sunglasses: - #掘金沸点# ,明年打不打脸,就看各位的老板的了。小手抖一抖,关注、点赞走一走~
在使用 Spring 作为项目开发框架的过程中,当需要集成某个组件时,通常需要大量的 xml 配置才可以让项目工程 run 起来,下面先以 mybatis 为例,来看下如何使用 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> 复制代码
Spirng 集成 mybatis 通常需要以下 bean 配置:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> // 省略其他配置 </bean> 复制代码
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean> 复制代码
<!-- 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 非常简单,加一下下面的 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-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 的定义及其生效条件:
@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 和解析配置。
上面分析到 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,让你的项目和 xml bean 配置说再见。
原来的 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 作为公共组件提供给其他项目工程用,从框架角度来看,最佳实践是:
@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(); } } 复制代码
<dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version> <scope>provided</scope> </dependency> </dependencies> 复制代码
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=/ com.glmapper.bridge.boot.autoconfigure.GlmpperAutoConfiguration 复制代码
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 最佳实践。
一家之言,如有任何错误,请批评指出,不胜感激 !也欢迎大家关注我的个人微信公众号,目前已经整理了 SpringBoot/SpringSession/SpringCloud/基础算法/JAVA 等系列文章,同时也会定期的送些小礼物(书或者我自己的书法联系手稿,欢迎一起交流~