springboot情操陶冶-web配置(六)
本文则针对数据库的连接配置作下简单的分析,方便笔者理解以及后续的查阅
栗子当先
以我们经常用的mybatis数据库持久框架来操作mysql服务为例
环境依赖
1.JDK v1.8+
2.springboot v2.0.3.RELEASE
3.mybatis v3.4.6
4.mysql v10.2.8-MarialDB
配置类步骤
1.pom.xml
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
2.springboot配置 application-datasource.properties
#datasource config
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/boot?useSSL=false&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimeZone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
#mybatis config
mybatis.check-config-location=false
mybatis.mapper-locations=classpath*:database/mybatis/mapper/*.xml
mybatis.executor-type=reuse
代码栗子步骤
1.实体类User.java
package com.example.demo.database.entity;
/**
* @author nanco
* -------------
* demo-springboot
* -------------
* @create 2018/10/17 16:52
**/
public class User {
private Long id ;
private String name ;
private Integer age ;
private String email ;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
2.mapper配置文件 UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.database.mysql.dao.UserDao">
<insert id="saveUser" parameterType="com.example.demo.database.entity.User">
insert into tbl_user(name,age,email)
values(#{name},#{age},#{email})
</insert>
</mapper>
3.为了使上述配置生效,则须定义扫描入口(@MapperScan
)
package com.example.demo.database.config;
import com.mysql.jdbc.Driver;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
/**
* @author nanco
* -------------
* demo-springboot
* -------------
* @create 2018/10/17 17:09
**/
@Configuration
public class DatabaseConfig {
@Configuration
@ConditionalOnClass(Driver.class)
@MapperScan("com.example.demo.database.mysql.dao")
static class MysqlInterfaceScanner {
}
}
4.来一个测试类
package com.example.demo.database.mysql;
import com.example.demo.database.DatabaseApplication;
import com.example.demo.database.entity.User;
import com.example.demo.database.mysql.dao.UserDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.Assert;
import javax.annotation.Resource;
/**
* @author nanco
* -------------
* demo-springboot
* -------------
* @create 2018/10/17 17:13
**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {DatabaseApplication.class})
public class MysqlDaoTest {
@Resource
private UserDao userDao;
@Test
public void testSave() {
User user = new User();
user.setName("nanco");
user.setAge(18);
user.setEmail("nancoasky@gmail.com");
System.out.println(userDao.saveUser(user));
}
}
运行上述的测试案例便完成了简单的插入功能,其他的功能读者可自行编写
源码层
查阅了spring-boot-autoconfigure包下的spring.factories,发现对于数据源的配置是通过DataSourceAutoConfiguration类来进行的,由此简单的展开下
DataSourceAutoConfiguration
本类的注册是有条件的,其类上的注解是
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class })
其中DataSource类是JDK自带的,EmbeddedDatabaseType类则是依赖spring-jdbc包,本例中引入mybatis则默认带入了上述包。笔者按照@Configuration
的加载顺序来对此类作下简单的分析
1.静态内部类注册解析
数据源检测
@Configuration
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}
具体的代码读者可翻阅相应的文档,这里作下总结
- 用户配置了spring.datasource.type属性或者classpath下存在springboot默认支持的数据源则该配置略过
- com.zaxxer.hikari.HikariDataSource
- org.apache.tomcat.jdbc.pool.DataSource
- org.apache.commons.dbcp2.BasicDataSource
- 如果上述的条件不满足则会在classpath下找寻springboot默认支持的数据库驱动,存在则会创建SimpleDriverDataSource数据源用来创建数据库连接
- H2 Database
- Derby Database
- HSQL Database
本例中引入mybatis-spring-boot-starter便会引入spring-jdbc包,则会采用HikariDataSource数据源来获取数据库连接
数据源创建
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
根据用户配置的spring.datasource.type属性或者springboot默认支持的数据源(见上)来进行数据源对象的创建,具体的读者可自行阅读,本例中则会采取HikariDataSource数据源,并注入至bean工厂中,当然也可以通过配置项给予更多的属性配置
spring.datasource.hikari.max-wait-millis=10000
spring.datasource.hikari.min-idle=5
spring.datasource.hikari.initial-size=5
spring.datasource.hikari.validation-query=SELECT 1
2.导入类解析
数据库连接池状态类初始化
@Configuration
public class DataSourcePoolMetadataProvidersConfiguration {
@Configuration
@ConditionalOnClass(HikariDataSource.class)
static class HikariPoolDataSourceMetadataProviderConfiguration {
@Bean
public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
return (dataSource) -> {
if (dataSource instanceof HikariDataSource) {
return new HikariDataSourcePoolMetadata(
(HikariDataSource) dataSource);
}
return null;
};
}
}
}
以HikariDataSource为例,则会创建HikariDataSourcePoolMetadata对象,主要是用来获取连接池的相关信息,看下DataSourcePoolMetadata接口就行,具体如下
public interface DataSourcePoolMetadata {
/**
* Return the usage of the pool as value between 0 and 1 (or -1 if the pool is not
* limited).
* <ul>
* <li>1 means that the maximum number of connections have been allocated</li>
* <li>0 means that no connection is currently active</li>
* <li>-1 means there is not limit to the number of connections that can be allocated
* </li>
* </ul>
* This may also return {@code null} if the data source does not provide the necessary
* information to compute the poll usage.
* @return the usage value or {@code null}
*/
Float getUsage();
/**
* Return the current number of active connections that have been allocated from the
* data source or {@code null} if that information is not available.
* @return the number of active connections or {@code null}
*/
Integer getActive();
/**
* Return the maximum number of active connections that can be allocated at the same
* time or {@code -1} if there is no limit. Can also return {@code null} if that
* information is not available.
* @return the maximum number of active connections or {@code null}
*/
Integer getMax();
/**
* Return the minimum number of idle connections in the pool or {@code null} if that
* information is not available.
* @return the minimum number of active connections or {@code null}
*/
Integer getMin();
/**
* Return the query to use to validate that a connection is valid or {@code null} if
* that information is not available.
* @return the validation query or {@code null}
*/
String getValidationQuery();
/**
* The default auto-commit state of connections created by this pool. If not set
* ({@code null}), default is JDBC driver default (If set to null then the
* java.sql.Connection.setAutoCommit(boolean) method will not be called.)
* @return the default auto-commit state or {@code null}
*/
Boolean getDefaultAutoCommit();
}
sql脚本执行加载
@Configuration
@Import({ DataSourceInitializerInvoker.class,
DataSourceInitializationConfiguration.Registrar.class })
class DataSourceInitializationConfiguration {
}
主要通过DataSourceInitializerInvoker类来进行sql脚本的执行加载,具体笔者就不贴代码了,作下简单的总结
- 如果spring.datasource.schema属性已指定相应的sql文件,则优先读取,并支持classpath路径查找
- 如果上述无配置,则默认读取
classpath*:schema.sql/classpath*:schema-${platform}.sql
文件(其中${platform}可用spring.datasource.platform指定) - 如果没有上述文件,则不执行
温馨提示:如果想在环境运行的时候执行相应的sql语句,则仍需要另外配置用户名(spring.datasource.schema-username)与密码(spring.datasource.schema-password),方可执行
MybatisAutoConfiguration
其加载顺序是在上述的DataSourceAutoConfiguration
之后的,看它头上的注解便可得知。读者在这之前最好已经了解了mybatis在spring中的相关用法,比如SqlsessionFactory、MappedStatement等基本概念,不了解也可直接戳笔者的先前文章Spring mybatis源码学习指引目录。
springboot支持用户使用@MapperScan或者@Mapper注解来注册扫描相应的dao接口。后者只能应用于单个的数据源,一般推荐使用前者来进行扫描注册,因为前者有更多的属性配置。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise
* annotation declarations e.g.:
* {@code @EnableMyBatisMapperScanner("org.my.pkg")} instead of {@code
* @EnableMyBatisMapperScanner(basePackages= "org.my.pkg"})}.
*/
String[] value() default {};
/**
* Base packages to scan for MyBatis interfaces. Note that only interfaces
* with at least one method will be registered; concrete classes will be
* ignored.
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages
* to scan for annotated components. The package of each class specified will be scanned.
* <p>Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};
/**
* The {@link BeanNameGenerator} class to be used for naming detected components
* within the Spring container.
*/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/**
* This property specifies the annotation that the scanner will search for.
* <p>
* The scanner will register all interfaces in the base package that also have
* the specified annotation.
* <p>
* Note this can be combined with markerInterface.
*/
Class<? extends Annotation> annotationClass() default Annotation.class;
/**
* This property specifies the parent that the scanner will search for.
* <p>
* The scanner will register all interfaces in the base package that also have
* the specified interface class as a parent.
* <p>
* Note this can be combined with annotationClass.
*/
Class<?> markerInterface() default Class.class;
/**
* Specifies which {@code SqlSessionTemplate} to use in the case that there is
* more than one in the spring context. Usually this is only needed when you
* have more than one datasource.
*/
String sqlSessionTemplateRef() default "";
/**
* Specifies which {@code SqlSessionFactory} to use in the case that there is
* more than one in the spring context. Usually this is only needed when you
* have more than one datasource.
*/
String sqlSessionFactoryRef() default "";
/**
* Specifies a custom MapperFactoryBean to return a mybatis proxy as spring bean.
*
*/
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
详细的属性注释也拷贝过来了,其可以针对不同的数据源扫描注册相应的dao接口,适用于多数据源应用。具体使用笔者就不展开了,具体看下其@MapperScan注解是如何被解析的,直接看MapperScannerRegistrar#registerBeanDefinitions()注册方法
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
说白了也就是通过mybatis的接口扫描类ClassPathMapperScanner类进行具体的处理,很简单。具体的解析步骤可详细戳上述的文章链接
小结
mybatis属性在springboot的使用,笔者并没有展开,读者可详看MybatisProperties类便可。笔者发现过多冗长的代码会影响笔者乃至读者的回看,于是笔者决定后续的文章,笔者尽可能详细记录自己的思考步骤,至于代码就贴出相应的关键部分即可方便后续回看~
springboot情操陶冶-web配置(六)的更多相关文章
- springboot情操陶冶-web配置(九)
承接前文springboot情操陶冶-web配置(八),本文在前文的基础上深入了解下WebSecurity类的运作逻辑 WebSecurityConfigurerAdapter 在剖析WebSecur ...
- springboot情操陶冶-web配置(七)
参数校验通常是OpenApi必做的操作,其会对不合法的输入做统一的校验以防止恶意的请求.本文则对参数校验这方面作下简单的分析 spring.factories 读者应该对此文件加以深刻的印象,很多sp ...
- springboot情操陶冶-web配置(四)
承接前文springboot情操陶冶-web配置(三),本文将在DispatcherServlet应用的基础上谈下websocket的使用 websocket websocket的简单了解可见维基百科 ...
- springboot情操陶冶-web配置(二)
承接前文springboot情操陶冶-web配置(一),在分析mvc的配置之前先了解下其默认的错误界面是如何显示的 404界面 springboot有个比较有趣的配置server.error.whit ...
- springboot情操陶冶-web配置(三)
承接前文springboot情操陶冶-web配置(二),本文将在前文的基础上分析下mvc的相关应用 MVC简单例子 直接编写一个Controller层的代码,返回格式为json package com ...
- springboot情操陶冶-web配置(一)
承接前文springboot情操陶冶-@SpringBootApplication注解解析,在前文讲解的基础上依次看下web方面的相关配置 环境包依赖 在pom.xml文件中引入web依赖,炒鸡简单, ...
- springboot情操陶冶-web配置(八)
本文关注应用的安全方面,涉及校验以及授权方面,以springboot自带的security板块作为讲解的内容 实例 建议用户可直接路由至博主的先前博客spring security整合cas方案.本文 ...
- springboot情操陶冶-web配置(五)
本文讲讲mvc的异常处理机制,方便查阅以及编写合理的异常响应方式 入口例子 很简单,根据之前的文章,我们只需要复写WebMvcConfigurer接口的异常添加方法即可,如下 1.创建简单的异常处理类 ...
- springboot情操陶冶-@SpringBootApplication注解解析
承接前文springboot情操陶冶-@Configuration注解解析,本文将在前文的基础上对@SpringBootApplication注解作下简单的分析 @SpringBootApplicat ...
随机推荐
- php一些高级函数方法
PHP高级函数 1.call_user_func (http://php.net/manual/zh/function.call-user-func.php) 2.get_class (http:// ...
- linux的一些基础命令
Linux是基于Unix的开源免费的操作系统,是部署服务器的很好选择. 系统:win10 工具:vm虚拟机+Xshell/CRT 虚拟机的系统为linux centos 7 首先看一下linux的基 ...
- Vue(三十二)SSR服务端渲染Nuxt.js
初始化Nuxt.js项目步骤 1.使用脚手架工具 create-nuxt-app 创建Nuxt项目 使用yarn或者npm $ yarn create nuxt-app <项目名> 注:根 ...
- HBase MVCC 机制介绍
关键词:MVCC HBase 一致性 本文最好结合源码进行阅读 什么是MVCC ? MVCC(MultiVersionConsistencyControl , 多版本控制协议),是一种通过数据的多版本 ...
- Python-JSON和pickle
笔记:一:简介 (1)JSON (JavaScript Object Notation) 是一种轻量级(XML重量级)的数据交换格式. 是为了数据交换而定制的一种规则,它基于ECMAScript的一个 ...
- Ajax实现聊天室
Ajax实现聊天室 运行效果如下: 代码显示: var net=new Object();//编写构造函数net.AjaxRequest=function(url,onload,onerror,met ...
- php 将一个或多个二维数组组合成一个二维数组并根据某个字段排序排序
最近再写项目的时候,碰到一个问题:如何将一个或多个二维数组组合成一个二维数组并根据某个字段排序排序:实在是想不到哪个php库中有哪个函数能实现,只能自己写一个了,将代码写出来后,发现自己的代码繁琐,并 ...
- TCP协议学习总结(上)
在计算机领域,数据的本质无非0和1,创造0和1的固然伟大,但真正百花齐放的还是基于0和1之上的各种层次之间的组合(数据结构)所带给我们人类各种各样的可能性.例如TCP协议,我们的生活无不无时无刻的站在 ...
- [Swift]LeetCode137. 只出现一次的数字 II | Single Number II
Given a non-empty array of integers, every element appears three times except for one, which appears ...
- [Swift]LeetCode282. 给表达式添加运算符 | Expression Add Operators
Given a string that contains only digits 0-9 and a target value, return all possibilities to add bin ...