作为公司的技术保障部,一直承担着技术方向的把控,最近公司准备全面转入spring boot的开发。所以我们部门也一直在调研相关的技术知识点;

使用springboot开发应用已经有一段时间了,我们都沉醉于它简洁的配置和平滑的上手曲线。

在springboot的开发中,starter是一个核心的配置,只需要引入对应模块的starter,然后在application.properties中引入对应的配置项,就可以开发业务逻辑了。

这一切都归功于springboot的自动配置的能力。

本文会从一个mybatis与spring boot结合的starter开始学习,该框架是我们提供出来,需要使用mybatis的团队只需要引入这个starter的jar包即可使用

新建原型项目

完整的pom.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <parent>
<groupId>cn.caijiajia</groupId>
<artifactId>springboot-parent</artifactId>
<version>1.0.0.RELEASE</version>
</parent> <groupId>cn.caijiajia</groupId>
<artifactId>mybatis-starter</artifactId>
<version>${project.release.version}</version>
<packaging>jar</packaging> <dependencies>
<dependency>
<groupId>cn.caijiajia</groupId>
<artifactId>framework-starter</artifactId>
<version>${project.release.version}</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency> <dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency> <dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency> <dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies> </project>

开发阶段

1.配置类 DatabaseAutoConfiguration DbProperties

@EnableConfigurationProperties({DbProperties.class})
public class DatabaseAutoConfiguration
implements BeanDefinitionRegistryPostProcessor, EnvironmentAware { private static Logger log = LoggerFactory.getLogger(DatabaseAutoConfiguration.class); private Environment environment; @Override
public void setEnvironment(Environment environment) {
this.environment = environment;
} @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
} @Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
DbProperties dbProperties = ConfigUtil.getDbConfig(environment, DbProperties.PROPERTY_PREFIX,
DbProperties.class);
// TODO 检查参数
if (dbProperties.getDatabase() == null && dbProperties.getDatabases() != null && dbProperties
.getDatabases().size() == 1) {
dbProperties.setDatabase(dbProperties.getDatabases().get(0));
}
initAllDataAccessBean(dbProperties, registry);
} protected void initAllDataAccessBean(DbProperties dbProperties, BeanDefinitionRegistry registry) {
// 单数据源
if (dbProperties.getDatabase() != null) {
initDataAccessBean(dbProperties.getDatabase(), null, registry);
}
// 多数据源
else if (dbProperties.getDatabases() != null && dbProperties.getDatabases().size() > 0) {
for (int i = 0; i < dbProperties.getDatabases().size(); i++) {
initDataAccessBean(dbProperties.getDatabases().get(i), i, registry);
}
}
} /**
* 注册一个datasource的所有bean 信息
*
* @param databaseConfig
* @param index
* @param registry
*/
protected void initDataAccessBean(DatabaseConfig databaseConfig, Integer index, BeanDefinitionRegistry registry) {
DataSourceConfig dataSourceConfig = databaseConfig.getDataSource();
TransactionConfig transactionConfig = databaseConfig.getTransaction();
MyBatisConfig myBatisConfig = databaseConfig.getMybatis();
LogConfig logConfig = databaseConfig.getLog(); // 给与默认 dataSource bean id: dataSource${index}
if (StringUtils.isEmpty(dataSourceConfig.getName())) {
if (index == null) {
dataSourceConfig.setName("dataSource");
} else {
dataSourceConfig.setName("dataSource" + index);
}
} // 注册datasource
registry.registerBeanDefinition(dataSourceConfig
.getName(), getTomcatJdbcDataSourceBeanDefinition(dataSourceConfig, logConfig)); // 注册事务
if (transactionConfig != null && transactionConfig.isEnable()) {
if (index == null) {
registry.registerBeanDefinition("transactionManager", getTransactionManagerBeanDefinition(dataSourceConfig));
} else {
registry.registerBeanDefinition("transactionManager" + index, getTransactionManagerBeanDefinition(dataSourceConfig));
}
} // 注册mybatis
if (myBatisConfig != null) {
final String sqlSessionFactoryBeanName = "sqlSessionFactory" + dataSourceConfig.getName();
registry.registerBeanDefinition(sqlSessionFactoryBeanName, getSqlSessionFactoryBeanDefinition(myBatisConfig, dataSourceConfig));
registry.registerBeanDefinition("mapperScannerConfigurer" + dataSourceConfig
.getName(), getMapperScannerConfigurerBeanDefinition(myBatisConfig, sqlSessionFactoryBeanName)); // 给与默认 dataSource bean id: sqlSession${index}
if (StringUtils.isEmpty(myBatisConfig.getSqlSessionBeanName())) {
if (index == null) {
myBatisConfig.setSqlSessionBeanName("sqlSession");
} else {
myBatisConfig.setSqlSessionBeanName("sqlSession" + index);
}
}
registry.registerBeanDefinition(myBatisConfig
.getSqlSessionBeanName(), getSqlSessionBeanDefinition(sqlSessionFactoryBeanName));
}
} /**
* 获取 tomcat jdbc datasource bean 定义
*
* @param dataSourceConfig
* @return
*/
protected AbstractBeanDefinition getTomcatJdbcDataSourceBeanDefinition(DataSourceConfig dataSourceConfig, LogConfig logConfig) {
if (dataSourceConfig.getMinIdle() == null) {
dataSourceConfig.setMinIdle(dataSourceConfig.getInitialSize());
}
if (dataSourceConfig.getMaxIdle() == null) {
dataSourceConfig.setMaxIdle(dataSourceConfig.getMaxActive());
} BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition("org.apache.tomcat.jdbc.pool.DataSource");
definitionBuilder.setDestroyMethodName("close"); // add filter
definitionBuilder
.addPropertyValue("jdbcInterceptors", "cn.caijiajia.framework.db.log.TomcatJdbcLogFilter(slowLogOn=" + logConfig
.isSlowSqlOn() + ",slowSqlThreshold=" + logConfig.getSlowSqlThreshold() + ")"); // if not set, use default value in jdbc driver
if (dataSourceConfig.getDefaultAutoCommit() != null) {
definitionBuilder
.addPropertyValue("defaultAutoCommit", dataSourceConfig.getDefaultAutoCommit());
}
if (dataSourceConfig.getDefaultReadOnly() != null) {
definitionBuilder
.addPropertyValue("defaultReadOnly", dataSourceConfig.getDefaultReadOnly());
}
if (dataSourceConfig.getDefaultTransactionIsolation() != null) {
definitionBuilder
.addPropertyValue("defaultTransactionIsolation", dataSourceConfig.getDefaultTransactionIsolation());
} definitionBuilder.addPropertyValue("url", dataSourceConfig.getUrl());
definitionBuilder.addPropertyValue("username", dataSourceConfig.getUsername());
definitionBuilder.addPropertyValue("password", dataSourceConfig.getPassword());
definitionBuilder.addPropertyValue("driverClassName", dataSourceConfig.getDriverClassName()); // 配置初始化大小、最小、最大
definitionBuilder.addPropertyValue("initialSize", dataSourceConfig.getInitialSize());
definitionBuilder.addPropertyValue("minIdle", dataSourceConfig.getMinIdle());
definitionBuilder.addPropertyValue("maxActive", dataSourceConfig.getMaxActive());
definitionBuilder.addPropertyValue("maxIdle", dataSourceConfig.getMaxIdle()); // 配置获取连接等待超时的时间
definitionBuilder.addPropertyValue("maxWait", dataSourceConfig.getMaxWait());
// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫
definitionBuilder
.addPropertyValue("timeBetweenEvictionRunsMillis", dataSourceConfig.getTimeBetweenEvictionRunsMillis());
// 配置一个连接在池中最小生存的时间,单位是毫秒
definitionBuilder
.addPropertyValue("minEvictableIdleTimeMillis", dataSourceConfig.getMinEvictableIdleTimeMillis()); definitionBuilder.addPropertyValue("validationQuery", "select 1");
definitionBuilder.addPropertyValue("testOnConnect", dataSourceConfig.isTestOnConnect());
definitionBuilder.addPropertyValue("testWhileIdle", dataSourceConfig.isTestWhileIdle());
definitionBuilder.addPropertyValue("testOnBorrow", dataSourceConfig.isTestOnBorrow());
definitionBuilder.addPropertyValue("testOnReturn", dataSourceConfig.isTestOnReturn()); definitionBuilder.addPropertyValue("removeAbandoned", dataSourceConfig.isRemoveAbandoned());
definitionBuilder.addPropertyValue("removeAbandonedTimeout", dataSourceConfig.getRemoveAbandonedTimeout()); AbstractBeanDefinition bd = definitionBuilder.getRawBeanDefinition();
bd.setScope(BeanDefinition.SCOPE_SINGLETON); return bd;
} /**
* 获取mybaits SqlSessionFactory bean 定义
*
* @param dataSourceConfig
* @return
*/
protected AbstractBeanDefinition getSqlSessionFactoryBeanDefinition(MyBatisConfig myBatisConfig, DataSourceConfig dataSourceConfig) {
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(org.mybatis.spring.SqlSessionFactoryBean.class);
definitionBuilder.addPropertyReference("dataSource", dataSourceConfig.getName()); PageInterceptor pageInterceptor = new PageInterceptor();
if (myBatisConfig.getPage().isSupportMethodsArguments()) {
Properties pageInterceptorProperties = new Properties();
pageInterceptorProperties.setProperty("supportMethodsArguments", "true");
pageInterceptorProperties.setProperty("params", "pageNum=pageNum;pageSize=pageSize");
pageInterceptor.setProperties(pageInterceptorProperties);
} definitionBuilder
.addPropertyValue("plugins", new Interceptor[]{new LogInfoInterceptor(), pageInterceptor}); AbstractBeanDefinition bd = definitionBuilder.getRawBeanDefinition();
bd.setScope(BeanDefinition.SCOPE_SINGLETON); return bd;
} /**
* 获取mybaits MapperScannerConfigurer bean 定义
*
* @param myBatisConfig
* @param sqlSessionFactoryBeanName
* @return
*/
protected AbstractBeanDefinition getMapperScannerConfigurerBeanDefinition(MyBatisConfig myBatisConfig, String sqlSessionFactoryBeanName) {
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(org.mybatis.spring.mapper.MapperScannerConfigurer.class);
definitionBuilder
.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName);
definitionBuilder.addPropertyValue("basePackage", myBatisConfig.getBasePackage()); AbstractBeanDefinition bd = definitionBuilder.getRawBeanDefinition();
bd.setScope(BeanDefinition.SCOPE_SINGLETON); return bd;
} /**
* 获取mybaits SqlSessionTemplate bean 定义
*
* @param sqlSessionFactoryBeanName
* @return
*/
protected AbstractBeanDefinition getSqlSessionBeanDefinition(String sqlSessionFactoryBeanName) {
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(org.mybatis.spring.SqlSessionTemplate.class);
definitionBuilder.addConstructorArgReference(sqlSessionFactoryBeanName);
AbstractBeanDefinition bd = definitionBuilder.getRawBeanDefinition();
bd.setScope(BeanDefinition.SCOPE_SINGLETON); return bd;
} /**
* 获取 DataSourceTransactionManager bean定义
*
* @param dataSourceConfig
* @return
*/
protected AbstractBeanDefinition getTransactionManagerBeanDefinition(DataSourceConfig dataSourceConfig) {
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(DataSourceTransactionManager.class);
definitionBuilder.addConstructorArgReference(dataSourceConfig.getName());
AbstractBeanDefinition bd = definitionBuilder.getRawBeanDefinition();
bd.setScope(BeanDefinition.SCOPE_SINGLETON); return bd;
}
}

  

@ConfigurationProperties(prefix = DbProperties.PROPERTY_PREFIX)
public class DbProperties { public final static String PROPERTY_PREFIX = "caijiajia.db"; private DatabaseConfig database; private List<DatabaseConfig> databases = new ArrayList(); public DatabaseConfig getDatabase() {
return database;
} public void setDatabase(DatabaseConfig database) {
this.database = database;
} public List<DatabaseConfig> getDatabases() {
return databases;
} public void setDatabases(List<DatabaseConfig> databases) {
this.databases = databases;
}
}

  

这是两段核心配置类,其它还有一些pojo类,这里就不写出来了

自定义spring.factories

自定义spring.factories的步骤很简单,只需要在src/main/resource目录下创建META-INF目录,并在该目录下添加文件spring.factories,文件内容为:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.caijiajia.framework.db.autoconfigure.DatabaseAutoConfiguration  

到这里通过spring boot的自动装配原理实现了配置类的自动装配

一个简单的starter创造就完成了

  

spring boot starter开发的更多相关文章

  1. 最详细的自定义Spring Boot Starter开发教程

    1. 前言 随着Spring的日渐臃肿,为了简化配置.开箱即用.快速集成,Spring Boot 横空出世. 目前已经成为 Java 目前最火热的框架了.平常我们用Spring Boot开发web应用 ...

  2. Spring Boot Starter 开发指南

    Spring Boot Starter是什么? 依赖管理是任何复杂项目的关键部分.以手动的方式来实现依赖管理不太现实,你得花更多时间,同时你在项目的其他重要方面能付出的时间就会变得越少. Spring ...

  3. 从零开始开发一个Spring Boot Starter

    一.Spring Boot Starter简介 Starter是Spring Boot中的一个非常重要的概念,Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的Bean根据环境( 条件 ...

  4. Spring Boot Starter 介绍

    http://www.baeldung.com/spring-boot-starters 作者:baeldung 译者:http://oopsguy.com 1.概述 依赖管理是任何复杂项目的关键部分 ...

  5. spring -boot s-tarter 详解

    Starter POMs是可以包含到应用中的一个方便的依赖关系描述符集合.你可以获取所有Spring及相关技术的一站式服务,而不需要翻阅示例代码,拷贝粘贴大量的依赖描述符.例如,如果你想使用Sprin ...

  6. spring boot + vue + element-ui全栈开发入门——spring boot后端开发

    前言 本文讲解作为后端的spring boot项目开发流程,如果您还不会配置spring boot环境,就请点击<玩转spring boot——快速开始>,如果您对spring boot还 ...

  7. SpringBoot 之Spring Boot Starter依赖包及作用

    Spring Boot 之Spring Boot Starter依赖包及作用 spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和YAML. spri ...

  8. Spring Boot Starter列表

    转自:http://blog.sina.com.cn/s/blog_798f713f0102wiy5.html Spring Boot Starter 基本的一共有43种,具体如下: 1)spring ...

  9. 手把手教你定制标准Spring Boot starter,真的很清晰

    写在前面 我们每次构建一个 Spring 应用程序时,我们都不希望从头开始实现具有「横切关注点」的内容:相反,我们希望一次性实现这些功能,并根据需要将它们包含到任何我们要构建的应用程序中 横切关注点 ...

随机推荐

  1. 建站手册-网站构建:万维网联盟(World Wide Web Consortium)

    ylbtech-建站手册-网站构建:万维网联盟(World Wide Web Consortium) 1.返回顶部 1. http://www.w3school.com.cn/site/site_w3 ...

  2. ORM模型类介绍,

    所有的软件开发过程中,都会涉及到对象和关系型数据库,在用户层面和业务逻辑层面,程序员编写代码都是面向对象的,当我们对象的信息发生变化的时候,都需要将对应的信息,传到关系型数据库中.而在此之前,需要我们 ...

  3. python 查看以及更新安装包

    查看 在终端(windows:电脑win+R, linux:ctrl+alt+T)输入: pip list 或者 conda list 更新 在终端(windows:电脑win+R, linux:ct ...

  4. latex 查找缺失的库文件

    app-portage/pfl contains a program to search in an online database for a Gentoo package containing a ...

  5. bash 中的特殊变量

    bash 中的特殊变量 变量 说明 ------------------------------------ $0 当前脚本的文件名 $n 第n个位置参数 $* 传递给脚本或函数的所有参数,$*会将这 ...

  6. Jquery中input:type=radio的监听,获取设置值

    一.html <div id='demo'> <input type='radio' name='sex' value='男' > <input type='radio' ...

  7. f-strings格式化输出

    python3.6后加入标准库的格式化输出新的写法 不区分大小写,f,F都可以 可以加入表达式 s1 = 'haha' s = f'python{s1.upper()}' l1 = ['小明', 18 ...

  8. 使用VS 2019发布.net core程序并部署到IIS的最新教程

    不管你是使用.net core开发的是web api还是网站类的程序,如果你是部署到IIS,那么下面的内容都适合于你,不会将.net core程序部署到IIS的朋友,可以看看这篇手把手教你部署.net ...

  9. webpack4入门到进阶案例实战课程

    愿景:"让编程不在难学,让技术与生活更加有趣" 更多教程请访问xdclass.net 第一章 webpack4前言 第一集 webpack4入门到进阶案例实战课程介绍 简介:讲述w ...

  10. JS面向对象——组合使用构造函数模型与原型模型中的隐患

    组合使用构造函数模型和原型模型中的问题,使用对象字面量重写原型模型会有隐患(涉及到原型的动态性),如下例: <!DOCTYPE html> <html> <head> ...