所有人都说,能不使用分布式事务就不使用分布式事务,因此暂不考虑分布式事务。
数据源配置好后,接下来就需要配置事务管理器了。相关代码内容如下:
可以看到,一共包含了四个条件注解,它们的作用分别是:
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class }),这个条件要求指定的两个类型必须存在于类路径中,也就是相关依赖jar包必须导入。
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE),指定自动配置的顺序为最低,也就是在同类中放到最后面,相当于把自己的优先级降到了最低。
@ConditionalOnSingleCandidate(DataSource.class),指定容器中必须只有一个DataSource类型的候选Bean,因为是本地事务管理器,而非分布式的,所以必须只能对应一个数据源才行。
@ConditionalOnMissingBean(PlatformTransactionManager.class),指定此时容器中还没有注册过事务管理器类型的Bean,如果别人已经注册过了,这里就不会再注册了。
如果上面四个条件都满足的话,这里就会注册一个基于数据源的事务管理器(DataSourceTransactionManager),这个数据源就是上篇文章中注册的那个。
至此和底层数据库已经接上头了,而且事务也有了,接下来就是在上面使用JDBC来“唱戏”了,但是纯粹的JDBC确实不太好用,所以该ORM框架上场了。 MyBatis自动配置
MyBatis是比较常用的,相关代码如下:
可以看到,它一共包含三个条件,它们的作用分别是:
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }),要求指定的两个类型必须存在,其含义就是必须引入mybatis的jar包,还有mybatis和spring整合的jar包。
@ConditionalOnSingleCandidate(DataSource.class),要求容器中只有了一个数据源类型的候选Bean。
@AutoConfigureAfter(DataSourceAutoConfiguration.class),要求自动配置的顺序在SpringBoot官方支持的数据源自动配置的后面。毕竟只有数据源配置好了才可以配置MyBatis。
然后使用已注册的数据源Bean再注册一个SqlSessionFactory类型的Bean,代码如下:
由于标了@ConditionalOnMissingBean注解,所以是在别人没有注册的前提下才会注册的。如果别人已经注册了,那就用别人注册的就可以了,这里就不需再注册了。
我们一般都会定义很多MyBatis的Mapper接口(即标有@Mapper注解的接口),用这些接口来执行SQL语句,而且这些接口可以像普通Bean一样注入到Service层。
可问题是,我们并没有提供这些Mapper接口的实现类啊,我们也没有把它们注册到容器里啊,其实这些事情,都是自动配置代码帮我们做了。
下面请看下实现这部分功能的代码:
1、获取到当前SpringBoot项目的根目录。
2、使用一个类路径扫描器去扫描根目录下所有标有@Mapper注解的接口,得到的是所有接口的Bean定义。
3、把接口Bean定义的类型设置为MapperFactoryBean类,这个类实现了FactoryBean接口,并把Mapper接口设置为该类构造函数的泛型参数值。
4、并没有把刚刚注册的SqlSessionFactory类型的Bean直接作为属性值给它,而是采用了按类型的自动装配。
5、有一点可能和大多数人的预期不一样,这里为Mapper接口注册的Bean其实并没有实现这个Mapper接口,而是实现了FactoryBean接口,泛型T就是Mapper接口的类型。
6、就是表明它可以返回一个Mapper接口类型的实例,但它自己却并不是Mapper接口类型的,其实这个实例是MyBatis自己生成的,和Spring无关,只不过是以Spring的方式来返回的。
有一点需要注意,由于SqlSessionFactory这个Bean也是按类型进行装配的,所以也需要它在容器中只有一个候选Bean。如果有多个候选Bean的话,Spring不知道该选哪个,会报错的。 分页插件自动配置
MyBatis的分页插件应该大都用的是PageHelper了。它的内容如下:
它包含两个条件,作用如下:
@AutoConfigureAfter(MybatisAutoConfiguration.class),指定自己的自动配置要放在MyBatis自动配置的后面,分页插件要依赖于MyBatis,当然要放在后面了。
@ConditionalOnBean(SqlSessionFactory.class),确保SqlSessionFactory类型的Bean已经被注册,因为分页插件需要用到它,即MyBatis已经自动配置过了。
所以需要将容器中所有的类型为SqlSessionFactory的Bean都收集过来,如下:
然后将所有在application.yml中为分页插件配置的属性都收集到,如下:
最后再生成一个拦截器,并把这些属性设置给拦截器,然后使用拦截器去拦截所有的SqlSessionFactory,如下:
这样在MyBatis执行所有查询SQL语句时都会被拦截,只须在需要的时候自己再执行一个select count(*)语句查询出总记录数即可。
这个“需要的时候”其实默认情况下就是我们传给MyBatis的参数中,包含有名字(或属性名)为pageNum和pageSize这两个参数,并且值不能为NULL。
通过学习这些所有自动配置的源码,我们已经明白了自动配置的原理以及里面的实现细节,也就是上篇文章开头说的,我们已经搞清楚了“现有状况”。
接下来就需要基于现有状况和实际需求提供解决方案就行了。
说明:这里不考虑分布式事务,也就是说多个数据源,只有一个数据源有事务。 多个数据源自动配置
自己写代码实现多个数据源的自动配置,可以参考阿里Druid数据源自动配置的代码,直接复制过来即可。
第一个数据源的自动配置如下:
这样两个数据源都自动配置好了,这里面有两个需要说明:
1、使用@AutoConfigureBefore注解协调数据源之间的自动配置顺序。具体顺序是,第一个数据源,条件符合,可以注册。第二个数据源,条件符合,可以注册。阿里Druid默认数据源,条件不符合,不注册。SpringBoot官方支持的数据源,条件不符合,不注册。
2、在需要开启事务的数据源注册时,标上@Primary注解,让它成为数据源类型的唯一候选Bean,这样在自动配置事务管理器时不会报错,于是就可以不写事务管理器自动配置的代码了,使用官方的自动配置代码。 事务管理器自动配置
由于只有一个数据源启用事务,而且该数据源在注册时已经标上了@Primary注解,所以这部分不用额外处理即可。 多个MyBatis自动配置
MyBatis的自动配置,也是参看MyBatis官方的代码,直接复制过来即可。
第一个数据源对应的MyBatis自动配置如下:
1、使用@AutoConfigureAfter和@AutoConfigureBefore来协调相关顺序,要保证MyBatis的自动配置要在数据源的自动配置后面,然后还要协调MyBatis之间的互相顺序。具体顺序是,第一个数据源MyBatis,条件符合,可以注册。第二个数据源MyBatis,条件符合,可以注册。MyBatis官方的自动配置,条件不符合,不可以注册。
2、在注册SqlSessionFactory时,使用@Qualifier注解限制使用哪个数据源,而且在@Bean注解里指定Bean名称,保证多个SqlSessionFactory类型的Bean不重名。
3、在扫描器扫描根包的时候,使用setAnnotationClass()指定要扫描的注解,以及使用setSqlSessionFactoryBeanName()指定要自动装配的SqlSessionFactory的Bean名称。
4、所以还要再定义一个@XxxMapper注解,直接把MyBatis的@Mapper复制过来改下名字即可。
其实原理也很简单,就是不同的MyBatis使用不同的@Mapper注解,对应不同的数据源,然后连接到不同的数据库。
所以只要我们在定义Mapper接口时,使用不同的@Mapper注解就可以了。保证@Mapper注解和数据库之间有一一映射的关系即可。 分页插件自动配置
如果多个数据源对应的数据库都是一种的话,比如都是MySQL,那分页插件的自动配置不用变。
因为分页插件的拦截器已经注入到了所有的SqlSessionFactory类型的Bean里,同种数据库分页SQL的规则都是一样的,所以不用变。
如果多个数据源对应的数据库不是一种的话,比如是MySQL和Oracle,此时也简单,只需把“运行时检测方言”改成true即可。
在MySQL和Oracle的情况下,我测试过是可以的,其它情况应该也都可以,只要PageHelper支持这种数据库就行。
如果想配置多个PageHelper也是可以的,这个可以按照上面的思路走,同样有以下注意点:
1、自动配置的顺序问题,所有的PageHelper要放到MyBatis自动配置的后面,其次再协调好PageHelper之间的自动配置顺序即可。
2、PageHelper默认的代码没有使用条件注解,所以我们没有办法通过自己注册了而使默认的不注册,这一点可能是它没有考虑周全,所以我们就不能引入PageHelper自动配置的jar包了。
3、要让PageHelper的自动配置和注册的SqlSessionFactory类型的Bean关联起来,建立起合理的映射关系,确保PageHelper配置的数据库和拦截的数据库是一种,这样就可以了。 让自动配置被SpringBoot识别
我们自定义的自动配置,默认情况下SpringBoot是不会识别的,也就是不起作用。
要想起作用,必须做以下两件事情:
1、在类路径里创建META-INF目录,并在其下创建spring.factories文件。
2、在这个文件里填入一对key/value,key为固定的,就是org.springframework.boot.autoconfigure.EnableAutoConfiguration,value就是所有自定义的自动配置类。
最后注意一点,自动配置类的名称都要以AutoConfiguration结尾,这样见名知义,长远来讲,方便你我他。
( 下 篇 完 )
作者现任架构师,工作11年,Java技术栈,计算机基础,用心写文章,喜欢研究技术,崇尚简单快乐。追求以通俗易懂的语言解说技术,希望所有的读者都能看懂并记住。
>>> 热门文章集锦 <<<
毕业10年,我有话说
我是一个协程
我是一个跳表
线程池开门营业招聘开发人员的一天
递归 —— 你值得拥有
迄今为止最好理解的ZooKeeper入门文章
基于角色的访问控制(RBAC)
11年码农的肺腑之言,如何成为一个优秀的程序员,送给渴望优秀的人
非著名架构师告诉你,代码该如何写,才能自己写的容易别人看的也不痛苦
迄今为止最硬核的「Java8时间系统」设计原理与使用方法
任何人都需要知道的「世界时间系统」构成原理,尤其开发人员
彻彻底底给你讲明白啥是SpringMvc异步处理
【面试】我是如何面试别人List相关知识的,深度有点长文
我是如何在毕业不久只用1年就升为开发组长的
爸爸又给Spring MVC生了个弟弟叫Spring WebFlux
【面试】我是如何在面试别人Spring事务时“套路”对方的
【面试】Spring事务面试考点吐血整理(建议珍藏)
【面试】吃透了这些Redis知识点,面试官一定觉得你很NB(干货 | 建议珍藏)
【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看(建议珍藏)
【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)
【面试】一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生(深度好文,建议珍藏)
【面试】如果把线程当作一个人来对待,所有问题都瞬间明白了
Java多线程通关———基础知识挑战
品Spring:帝国的基石