本文转载自微信公众号《编程新谈》,转载请联系编程新谈公众号。解决问题“两步走”的第一步是认清现状。第二步,根据实际情况和现有情况给出方案。可能有人会觉得第二步比较难,其实不然,第一步才是最难的。我就不解释了,不懂的人慢慢懂。问题抽象出来后,有第一类两种。看似不复杂,却很难解决。第二类看似复杂,其实比较容易解决。很多和SpringBoot相关的问题大概都属于第二类。SpringBoot的核心思想SpringBoot是一个高度集成的框架,由其背后的autoconfigure实现。对于这种自动配置,它引入了条件判断(Condition)机制。这些条件判断大致分为三类:第一类:检测application.yml配置文件中的配置属性,有怎么办,没有怎么办。第二类是检测类路径中引入的class类,有怎么办,没有怎么办。第三类是检测容器中注册的bean,有怎么办,没有怎么办。其实相当于很多if/else相互嵌套,相互交织。SpringBoot在启动的时候,会把所有的条件都一一计算,最后从中“杀出一条血路”。常用的数据库访问方案是基于SpringBoot最常用的方案,从下到上分为:底层,数据库,比如MySQL的倒数第二层,数据源,也就是第三层-最后一层DataSource,还有事务管理器,也就是TransactionManager的倒数第四层该层是ORM框架,比如MyBatis的倒数第二层,就是分页组件,比如PageHelper,如果有的话只有一个数据库,那么数据源就是单一数据源,事务自然是本地事务。如果有多个数据库,数据源就变成了多个数据源,事务自然就变成了分布式事务。按照微服务的理论,同样的代码不会直接访问其他数据源,而是应该通过接口访问其他数据源中的数据。但是在实际情况中,当然,只要不出问题,越简单越好,只要明白自己在做什么就可以了。SpringBoot官方支持的数据源想了解一些,最好的资料就是官方文档。要想深入了解,恐怕只能看源码了。SpringBoot的数据源自动配置类为:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration默认支持两种数据源配置:嵌入式数据库(EmbeddedDatabaseConfiguration)和池化数据源(PooledDataSourceConfiguration)。选择这两个数据源中的哪一个取决于各自条件的计算结果以及满足谁的条件。我们注意到每个类上都有四个注解,我们来看看它们的作用:@Configuration,表示这个类会被Spring框架处理。@Conditional,这是一个条件,需要指定一个条件类,这个条件类需要计算。@ConditionalOnMissingBean,这个是用来检测指定Bean是否注册的条件,没有注册时满足条件。@Import用于引入其他类,引入的类会交由Spring框架处理。你可以看到有两个条件。下面我们来看看这两种数据源配置的具体情况。池化数据源的条件一:@Conditional(PooledDataSourceCondition.class)可以看到指定的条件类是PooledDataSourceCondition,这个类的内容如下:可以看到它继承自AnyNestedCondition类,也就是说这个类的条件依赖于它内部嵌套类的条件,所以它定义了两个内部嵌套类,每个嵌套类上都有条件注解。内层嵌套类1的条件是:@ConditionalOnProperty(prefix="spring.datasource",name="type")这个是关于application.yml配置文件中属性的检测,如果属性spring.datasource.type已配置,条件满足,否则不满足。这个条件表示是否显式指定数据源的类型。这个在日常开发中一般不会指定,所以一般不会满足这个条件。内部嵌套类2的条件是:@Conditional(PooledDataSourceAvailableCondition.class)指定另一个条件类PooledDataSourceAvailableCondition。该类相关内容如下:其核心思想是通过类加载器Class分别加载以下三个数据源:com.zaxxer.hikari.HikariDataSourceorg.apache.tomcat.jdbc.pool.DataSourceorg.apache.commons。dbcp2.BasicDataSource如果加载成功,则满足此条件。一般情况下,我们不会使用这三个数据源,所以一般来说,这个条件是不满足的。一般情况下,这两个嵌套类的条件都不满足,那么它们的外层类的条件一般也不满足。池化数据源的第二个条件:@ConditionalOnMissingBean({DataSource.class,XADataSource.class})这个条件是检测Spring容器中是否注册了DataSource或者XADataSource类型的Bean。如果未注册,则符合规定。这个要看实际情况了。@Import引入的类:@Import({DataSourceConfiguration.Hikari.class,DataSourceConfiguration.Tomcat.class,DataSourceConfiguration.Dbcp2.class,DataSourceConfiguration.Generic.class,DataSourceJmxConfiguration.class})可以看到这些导入的类是每类数据源配置或注册类也是如此。这里一共介绍了五类。它们也是有条件的,将按顺序计算。最多只有一个会匹配,或者一个都不匹配。我们来看一个SpringBoot官方推荐的数据源,Hikari的配置。其内容如下:包含三个条件:@ConditionalOnClass(HikariDataSource.class),表示HikariDataSource类必须存在,也就是必须引入Hikari。jar包。@ConditionalOnMissingBean(DataSource.class),表示DataSource类型的Bean不存在,即目前还没有注册过数据源。@ConditionalOnProperty(name="spring.datasource.type",havingValue="com.zaxxer.hikari.HikariDataSource",matchIfMissing=true),表示指定数据源类型为Hikari,不指定则认为是在合规。如果这三个条件都满足,一个HikariDataSource类型的数据源bean将被注册到容器中。@ConfigurationProperties(prefix="spring.datasource.hikari")的作用是在实例化数据源Bean时,根据setter的规则设置application.yml配置文件中spring.datasource.hikari开头的配置属性给这个数据源Bean实例。其他类型数据源的注册细节和这个Hikari是完全一样的,所以会计算上面介绍的5个数据源配置类的条件,但是最多只有一个配置类满足条件。因此,从某种意义上说,SpringBoot的条件并不具备某些情况下“短路”的特性。池化数据源的部分已经完成。让我们再看看嵌入式数据源。嵌入数据源条件1:@Conditional(EmbeddedDatabaseCondition.class)这里指定的条件类为EmbeddedDatabaseCondition,其相关内容如下:其核心思想是先判断池化数据源的条件是否满足。如果池化数据源一致,那么内嵌数据源一定不一致,所以池化数据源的优先级高。然后分别加载以下三个嵌入式数据源类:org.h2.Driverorg.apache.derby.jdbc.EmbeddedDriverorg.hsqldb.jdbcDriver只要其中一个加载成功,就认为是合规的。在实践中,很少使用嵌入式数据源,所以一般不满足这个条件。内嵌数据源条件2:@ConditionalOnMissingBean({DataSource.class,XADataSource.class})想必已经知道是什么意思了,即如果此时数据源类型的bean还没有注册到容器中,就会见面。@Import引入的类:@Import(EmbeddedDataSourceConfiguration.class)由于一般开发很少用到内嵌数据源,所以就不看了。其实一般情况下,SpringBoot官方支持的三种池化数据源和三种嵌入式数据源是不会满足这些条件的。因为一般情况下,我们都是用阿里的Druid数据源。阿里的Druid数据源Druid数据源的自动配置内容如下:有两个条件:@ConditionalOnClass(DruidDataSource.class),说明DruidDataSource类需要存在,即Druid数据源的jar包已介绍。@ConditionalOnMissingBean表示容器中没有注册过DataSource类型的Bean。自动配置除了和条件有关之外,还和顺序有很大的关系,因为顺序最靠前的条件是先计算的。一旦满足条件,bean将被注册到容器中。一旦注册了特定类型的bean,后者可能就没有机会了。挂号的。自动配置顺序:@AutoConfigureBefore(DataSourceAutoConfiguration.class)表示Druid数据源的自动配置在SpringBoot官方数据源自动配置之前进行,所以Druid数据源在容器中注册了一个DataSource类型的Bean。所以SpringBoot官方的数据源自动配置是没有机会注册数据源bean的。这样,我们使用的是Druid数据源。
