[TOC] # db-spring-boot-starter 前面项目中,咱们使用了 [01.db-core模块](01.db-core%E6%A8%A1%E5%9D%97.md)为整个项目提供通用的数据库处理,现在我们将采用springboot 标准starter的做法,重构项目基础组件,利用org.springframework.boot.autoconfigure,完成对象的基本装配。同时他具有以下功能: * 集成druid数据源 * 集成mybatis-plus * 动态数据源切换 * pagehelper分页处理 * Guava ## db-spring-boot-starter代码分析 * 工具类 ![](https://img.kancloud.cn/66/39/663991e94fed078fe0728f7a522e2a0a_1283x581.png) * AOP切换数据源类 ![](https://img.kancloud.cn/c5/f3/c5f3ac49fb355d8406a42407111f708b_1530x611.png) * 动态数据源定义core log ![](https://img.kancloud.cn/77/7e/777ede181c9cf389d069dc5636086b1b_1626x640.png) * 多数据源自动装配定义 ![](https://img.kancloud.cn/07/87/078711f35a963869ee0b8eeb4816b632_1834x645.png) ### 动态数据源详解 * [15.动态数据源配置](18.%E5%8A%A8%E6%80%81%E6%95%B0%E6%8D%AE%E6%BA%90%E9%85%8D%E7%BD%AE.md) ## db-spring-boot-starter 如何使用 > user-center代码 * user-center pom文件使用 ![](https://img.kancloud.cn/3f/6f/3f6f3f642f446d680a5af9300efc405e_1530x400.png) * user-center application.yml ![](https://img.kancloud.cn/40/a5/40a57e25f8c3cd6521c05798abf79b77_1846x617.png) * 编写dao xml代码 ![](https://img.kancloud.cn/50/2c/502c2953cf4547194c91a530b38cd903_1850x738.png) ## mybatis-plus 介绍 * MyBatis 是一款优秀的持久层框架,其目的是想当做互联网的篱笆墙,围绕着数据库提供持久化服务的一个框架,支持自定义 SQL、存储过程及高级映射。 * MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作,还可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Ordinary Java Object,普通 Java 对象)为数据库中的记录。 * [MyBatis-Plus](https://github.com/baomidou/mybatis-plus)(简称 MP)是一个[MyBatis](http://www.mybatis.org/mybatis-3/)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 ### 使用mybatis编写Dao ![](https://img.kancloud.cn/50/2c/502c2953cf4547194c91a530b38cd903_1850x738.png) ## db-spring-boot-starter自动装配原理解析 咱们想想,在不同项目中,咱们的项目是如何装配这些对象的吗?下面咱们需要揭密。 * db-spring-boot-starter 中定义了spring.factories文件 ![](https://img.kancloud.cn/7b/65/7b65c4f503da46d6cf96d56d516f9684_1554x443.png) * DataSourceAutoConfig 中@Import(DataSourceAOP.class) ![](https://img.kancloud.cn/c4/88/c48802a1d13011b1baf97f7c7b698fb7_1055x298.png) 那么这些文件是如何完成加载到spring容器的呢? 此时,咱们必须回到user-center,阅读源码 * @SpringBootApplication ![](https://img.kancloud.cn/9b/cf/9bcf14c2f60672903bd0f633c4f868a2_1545x621.png) * @EnableAutoConfiguration ![](https://img.kancloud.cn/3a/27/3a2745529f91f89fccc11b06c74879b4_1088x677.png) * AutoConfigurationImportSelector ![](https://img.kancloud.cn/08/b7/08b727562ad33adfd71fdf4bed7810f8_1007x485.png) 阅读到这里,我们了解到,user-center在启动时,由于@SpringBootApplication是复合注解,包含@EnableAutoConfiguration,这个类中@import了核心处理类AutoConfigurationImportSelector,这个类的核心就是将classpath中搜索所有META-INF/spring.factories配置文件,并且将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration key对应的配置项加载到spring容器 ## SPI机制的使用 ``` SPI的全名为Service Provider Interface,简单的总结下java spi机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。 java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制 ``` ### JDK SPI 在 JDBC 中的应用 JDK 中只定义了一个 java.sql.Driver 接口,具体的实现是由不同数据库厂商来提供的。这里以 MySQL 提供的 JDBC 实现包为例进行分析。 在 mysql-connector-java-*.jar 包中的 META-INF/services 目录下,有一个 java.sql.Driver 文件中只有一行内容,如下所示: ``` com.mysql.cj.jdbc.Driver ``` 在使用 mysql-connector-java-*.jar 包连接 MySQL 数据库的时候,我们会用到如下语句创建数据库连接: ``` String url = "jdbc:mysql://59.110.164.254:3306/user-center?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false "; Connection conn = DriverManager.getConnection(url, username, pwd); ``` DriverManager 是 JDK 提供的数据库驱动管理器,其中的代码片段,如下所示: ``` static {     loadInitialDrivers();     println("JDBC DriverManager initialized"); } ``` 在调用 getConnection() 方法的时候,DriverManager 类会被 Java 虚拟机加载、解析并触发 static 代码块的执行,在 loadInitialDrivers() 方法中通过 JDK SPI 扫描 Classpath 下  java.sql.Driver 接口实现类并实例化,核心实现如下所示: ``` private static void loadInitialDrivers() {     String drivers = System.getProperty("jdbc.drivers")     // 使用 JDK SPI机制加载所有 java.sql.Driver实现类     ServiceLoader<Driver> loadedDrivers =             ServiceLoader.load(Driver.class);     Iterator<Driver> driversIterator = loadedDrivers.iterator();     while(driversIterator.hasNext()) {         driversIterator.next();     }     String[] driversList = drivers.split(":");     for (String aDriver : driversList) { // 初始化Driver实现类         Class.forName(aDriver, true,             ClassLoader.getSystemClassLoader());     } } ``` 在 MySQL 提供的 com.mysql.cj.jdbc.Driver 实现类中,同样有一段 static 静态代码块,这段代码会创建一个 com.mysql.cj.jdbc.Driver 对象并注册到 DriverManager.registeredDrivers 集合中( CopyOnWriteArrayList 类型),如下所示: ``` static {    java.sql.DriverManager.registerDriver(new Driver()); } ``` 在 getConnection() 方法中,DriverManager 从该 registeredDrivers 集合中获取对应的 Driver 对象创建 Connection,核心实现如下所示: ``` private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {     // 省略 try/catch代码块以及权限处理逻辑     for(DriverInfo aDriver : registeredDrivers) {         Connection con = aDriver.driver.connect(url, info);         return con;     } } ``` ### SpringBoot中的类SPI扩展机制 在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。 ## Guava Guava 还提供了很多实用工具,如 Lists、Maps、Sets,接下来我们分别来看下这些常用工具的使用和原理。 * List<泛型> list = Lists.newArrayList(); * Map<String,String> hashMap = Maps.newHashMap(); 这种写法其实就是一种简单的工厂模式 ``` // 可以预估 list 的大小为 20 List<String> list = Lists.newArrayListWithCapacity(20); List<String> list = Lists.newArrayListWithExpectedSize(20); Map<String,String> hashMap = Maps.newHashMap(); Map<String,String> linkedHashMap = Maps.newLinkedHashMap(); Map<String,String> withExpectedSizeHashMap = Maps.newHashMapWithExpectedSize(20); ``` Guava 还提供了提供了一些异常处理的静态方法 ``` Throwables.throwIfUnchecked(new RuntimeException("模拟业务出错")); ``` ## 总结回顾 db-spring-boot-starter构建原理 * 1.ImportSelector 该接口的方法的返回值都会被纳入到spring容器管理中 * 2.SpringFactoriesLoader 该类可以从classpath中搜索所有META-INF/spring.factories配置文件,并读取配置 db-spring-boot-starter如何使用 * 1.使用mybatis构建dao文件 * 2.配置数据源 * 3.配置xml路径