ilistsource 不包含任何数据源。_SpringBoot多数据源问题打破沙锅讲到底(下)

事务管理器自动配置
所有人都说,能不使用分布式事务就不使用分布式事务,因此暂不考虑分布式事务。
数据源配置好后,接下来就需要配置事务管理器了。相关代码内容如下:

f83ed61d085b546ee4177a104ce8eab4.png


可以看到,一共包含了四个条件注解,它们的作用分别是:
@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是比较常用的,相关代码如下:

3810fc3ba2d48f426a66c9aa61abf9bf.png


可以看到,它一共包含三个条件,它们的作用分别是:
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }),要求指定的两个类型必须存在,其含义就是必须引入mybatis的jar包,还有mybatis和spring整合的jar包。
@ConditionalOnSingleCandidate(DataSource.class),要求容器中只有了一个数据源类型的候选Bean。
@AutoConfigureAfter(DataSourceAutoConfiguration.class),要求自动配置的顺序在SpringBoot官方支持的数据源自动配置的后面。毕竟只有数据源配置好了才可以配置MyBatis。
然后使用已注册的数据源Bean再注册一个SqlSessionFactory类型的Bean,代码如下:

15a99d43873204344164965cb0161db8.png


由于标了@ConditionalOnMissingBean注解,所以是在别人没有注册的前提下才会注册的。如果别人已经注册了,那就用别人注册的就可以了,这里就不需再注册了。
我们一般都会定义很多MyBatis的Mapper接口(即标有@Mapper注解的接口),用这些接口来执行SQL语句,而且这些接口可以像普通Bean一样注入到Service层。
可问题是,我们并没有提供这些Mapper接口的实现类啊,我们也没有把它们注册到容器里啊,其实这些事情,都是自动配置代码帮我们做了。
下面请看下实现这部分功能的代码:

dc757347a0ecf40acdfbed827852bcfb.png

4d8d58bbc836b746cec851a77b57c9b8.png

88818f9bab73c09ec7db7961b8927306.png

a68cc90167376cbade8d88e441bc3e32.png

ce18d3ff26366d4ffb7c90429cd4a1a6.png

5ef58edadfcb0bc0346ce148172c4c9a.png

5fc2b849cb881373de7dbe139b28b2ec.png

这些代码做了很多事情,来逐一了解下:
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了。它的内容如下:

6409766197f8034575fc928fa7b75755.png


它包含两个条件,作用如下:
@AutoConfigureAfter(MybatisAutoConfiguration.class),指定自己的自动配置要放在MyBatis自动配置的后面,分页插件要依赖于MyBatis,当然要放在后面了。
@ConditionalOnBean(SqlSessionFactory.class),确保SqlSessionFactory类型的Bean已经被注册,因为分页插件需要用到它,即MyBatis已经自动配置过了。
所以需要将容器中所有的类型为SqlSessionFactory的Bean都收集过来,如下:

fd8195e964adfc88528bd66e2c17d162.png


然后将所有在application.yml中为分页插件配置的属性都收集到,如下:

bbd36ff8e6e5dd8bb69342384d027f80.png


最后再生成一个拦截器,并把这些属性设置给拦截器,然后使用拦截器去拦截所有的SqlSessionFactory,如下:

dd1d4cf53abdb4e5180581f2d81e823d.png


这样在MyBatis执行所有查询SQL语句时都会被拦截,只须在需要的时候自己再执行一个select count(*)语句查询出总记录数即可。
这个“需要的时候”其实默认情况下就是我们传给MyBatis的参数中,包含有名字(或属性名)为pageNum和pageSize这两个参数,并且值不能为NULL。
通过学习这些所有自动配置的源码,我们已经明白了自动配置的原理以及里面的实现细节,也就是上篇文章开头说的,我们已经搞清楚了“现有状况”。
接下来就需要基于现有状况和实际需求提供解决方案就行了。
说明:这里不考虑分布式事务,也就是说多个数据源,只有一个数据源有事务。 多个数据源自动配置
自己写代码实现多个数据源的自动配置,可以参考阿里Druid数据源自动配置的代码,直接复制过来即可。
第一个数据源的自动配置如下:

cc5ccc76bab451c9be94c0bd0e479879.png

27b0232f197b3bad8591cc12fa5ad108.png

第二个数据源的自动配置如下:

ac7c6f026c9415ace61e5bc82f18df6c.png

40755f8de25c2fea9ab03c329322e7d7.png


这样两个数据源都自动配置好了,这里面有两个需要说明:
1、使用@AutoConfigureBefore注解协调数据源之间的自动配置顺序。具体顺序是,第一个数据源,条件符合,可以注册。第二个数据源,条件符合,可以注册。阿里Druid默认数据源,条件不符合,不注册。SpringBoot官方支持的数据源,条件不符合,不注册。
2、在需要开启事务的数据源注册时,标上@Primary注解,让它成为数据源类型的唯一候选Bean,这样在自动配置事务管理器时不会报错,于是就可以不写事务管理器自动配置的代码了,使用官方的自动配置代码。 事务管理器自动配置
由于只有一个数据源启用事务,而且该数据源在注册时已经标上了@Primary注解,所以这部分不用额外处理即可。 多个MyBatis自动配置
MyBatis的自动配置,也是参看MyBatis官方的代码,直接复制过来即可。
第一个数据源对应的MyBatis自动配置如下:

e18049ea0f5f726cd703d31c87b5a2ce.png

f753c4c53fb4e08c1f8095dc9621fc3d.png

6b2582e5820670e1d9a2a4086a1062d8.png

第二个数据源对应的MyBatis自动配置如下:

eec0164687f7ed3bf0ce45b4b7d5b6a4.png

9b0a8dabe51453d2ca85beb990425e33.png

92b19a6fd62c2161802d5ee1eafd6dc7.png

e7f7af51b3830f57667eb655945e3056.png

这样两个数据源对应的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即可。

4ee71f23990b8ea12ba675c22a091026.png


在MySQL和Oracle的情况下,我测试过是可以的,其它情况应该也都可以,只要PageHelper支持这种数据库就行。
如果想配置多个PageHelper也是可以的,这个可以按照上面的思路走,同样有以下注意点:
1、自动配置的顺序问题,所有的PageHelper要放到MyBatis自动配置的后面,其次再协调好PageHelper之间的自动配置顺序即可。
2、PageHelper默认的代码没有使用条件注解,所以我们没有办法通过自己注册了而使默认的不注册,这一点可能是它没有考虑周全,所以我们就不能引入PageHelper自动配置的jar包了。
3、要让PageHelper的自动配置和注册的SqlSessionFactory类型的Bean关联起来,建立起合理的映射关系,确保PageHelper配置的数据库和拦截的数据库是一种,这样就可以了。 让自动配置被SpringBoot识别
我们自定义的自动配置,默认情况下SpringBoot是不会识别的,也就是不起作用。
要想起作用,必须做以下两件事情:
1、在类路径里创建META-INF目录,并在其下创建spring.factories文件。

0f4032c02e694508fa1feb817afb9e35.png


2、在这个文件里填入一对key/value,key为固定的,就是org.springframework.boot.autoconfigure.EnableAutoConfiguration,value就是所有自定义的自动配置类。

2e258f3f4ee8d833a80aa36a17db18de.png


最后注意一点,自动配置类的名称都要以AutoConfiguration结尾,这样见名知义,长远来讲,方便你我他。

( 下 篇 完 )

作者现任架构师,工作11年,Java技术栈,计算机基础,用心写文章,喜欢研究技术,崇尚简单快乐。追求以通俗易懂的语言解说技术,希望所有的读者都能看懂并记住。

033344b2364e9f72f32301fcdb52afb4.png      

>>> 热门文章集锦 <<<

毕业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:帝国的基石

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值