一.springboot自动配置数据源解析:
以前我们在操作数据库(sql、nosql)时,需要导入对饮给的依赖,然后再配置我们需要使用的数据源,这样我们就可以使用java的方式,访问数据库了,但是现在我们使用的springboot框架,最大的核心就是自动配置,所以我们的数据源也会自动装配,那么我们就来探析一下自动装配数据源的原理:
- 我们都知道每一个自动配置都会有一个xxx AutoConfiguration的自动配置类,我们的数据源也有一个自动配置类:DataSourceAutoConfiguration
我们可以看到这个自动配置类里面会有一个这个内部类:
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({
DataSource.class, XADataSource.class })
@Import({
DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
最主要的就是这个类上边自动导入了一些类:
@Import({
DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
这些类都是属于DataSourceConfiguration这个类里面的,我们都知道springboot自动装配时,对应功能都会有一个xxxConfiguration的配置类,我们进去看一下
DataSourceConfiguration这个类里面放了很多的内部类:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
matchIfMissing = true)
static class Tomcat {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties,
org.apache.tomcat.jdbc.pool.DataSource.class);
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
String validationQuery = databaseDriver.getValidationQuery();
if (validationQuery != null) {
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(validationQuery);
}
return dataSource;
}
}
/**
* Hikari DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
/**
* DBCP DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp2.BasicDataSource",
matchIfMissing = true)
static class Dbcp2 {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dbcp2")
org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, org.apache.commons.dbcp2.BasicDataSource.class);
}
}
/**
* Oracle UCP DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({
PoolDataSourceImpl.class, OracleConnection.class })
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "oracle.ucp.jdbc.PoolDataSource",
matchIfMissing = true)
static class OracleUcp {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.oracleucp")
PoolDataSourceImpl dataSource(DataSourceProperties properties) throws SQLException {
PoolDataSourceImpl dataSource = createDataSource(properties, PoolDataSourceImpl.class);
dataSource.setValidateConnectionOnBorrow(true);
if (StringUtils.hasText(properties.getName())) {
dataSource.setConnectionPoolName(properties.getName());
}
return dataSource;
}
}
//=======================================
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
意思:会去配置文件中找spring.datasource.type属性的值,去和havingValue 中的值匹配,相同返回true,不相同返回false
matchIfMissing:缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错
这其实就是springboot为我们自动配置的一些数据源,但是每个内部类上边会有ConditionalOnxxx的注解,其实也就是满足这个条件,这个类就执行,所以我们发现,springboot为我们自动配置了一个com.zaxxer.hikari.HikariDataSource的数据源(号称速度最快),不需要我们配置任何配置,他也可以生效。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
如果我们要使用别的数据源时,他也为我们提供了一个内部类:
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
这个内部类的意思就是:当我们需要使用特殊的数据源时,就需要再配置文件中配置一个spring.datasource.type的属性,属性值也就是具体数据源饿全类名,这样他就可以按照我们的需求创建数据源了。
Spring Boot支持自定义数据源有一种机制:使用DataSourceBuilder创建数据源,利用反射创建对应type的数据源,并且绑定相关属性。这里使用的是建造者模式:properties.initializeDataSourceBuilder().build()
public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName())
.url(determineUrl()).username(determineUsername()).password(determinePassword());
}
以上就是我们的自动装配数据源的分析(第一次理解,不对望理解)
二.springboot整合druid
- 导入依赖:
<!--log4j:druid配备了log4j作为记录日志工具,不配置会报错(可以不在配置文件中配置)-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
还需要导入另外的两个依赖
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
- 配置文件:
spring:
#数据库连接配置
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
#serverTimezone=UTC:配置时区
#useUnicode=true&characterEncoding=utf-8&useSSL=true:防止乱码
url: jdbc:mysql:///is?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
#设置数据源,默认使用springboot自带的数据源
type: com.alibaba.druid.pool.DruidDataSource
#druid的其他配置:可选择配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- 编写配置类:
@Configuration
public class DruidConfig {
//自己配置一个数据源,默认接收spring.datasource的配置,然后把他放入到工厂中
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DruidDataSource druidDataSource(){
return new DruidDataSource();
}
//配置Druid的监控 可以使用:http://localhost:端口/下边配置的拦截路径,即可访问
//1、配置一个管理后台的Servlet
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String,String> initParams = new HashMap<>();
initParams.put("loginUsername","admin");
initParams.put("loginPassword","123456");
initParams.put("allow","");//默认就是允许所有访问
initParams.put("deny","192.168.15.21");
bean.setInitParameters(initParams);
return bean;
}
//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
Map<String,String> initParams = new HashMap<>();
initParams.put("exclusions","*.js,*.css,/druid/*");
bean.setInitParameters(initParams);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
-
测试:
-
druid的监控中心
登陆用户名和密码就是在配置文件的:
未知理解:如果要使用druid的其他配置(如:监控中心),那么我们就需要自己创建DataSource,并接收配置文件有关的内容,才可以生效。
大概的原因是springboot底层帮根据全类名映射出来的数据源只能绑定基础的四个属性,其他的配置不会生效(不知道对不对)
3.springboot整合mybatis
- 需要导入的依赖
<!--mybatis,还需要导入其他的就不一一列举了-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
-
springboot整合mybatis需要使用的注解:
@Mapper:标注在dao接口上,标注的接口一定要存在于ioc容器中
@MapperScan(dao层包路径):标注在springboot主入口上
作用:mybaits会有一个拦截器,自动拦截标注了@Mapper注解的类或者扫描的包下的所有的类,然后使用代理的方式,创建对应的实现类,我们就可以直接在需要使用的地方,使用@Autowarid注解注入了,像以前ssm框架中的作用。 -
编写dao层接口:
注解方式:
@Repository
public interface UserDao {
/*查询全部的用户 可以直接使用注解的方式编写sql*/
@Select("select * from users")
List<Users> findAll();
/*根据id查询用户*/
//@Select("select * from users where id = #{id}")
Users findUsersByid(Integer id);
/*当多个参数时,一定要加上注解@Param*/
@Select("select * from users where username = #{username} and password = #{password}")
Users findUsersByUsernameAndPassword(@Param("username") String username,
@Param("password") String password);
}
在需要使用的地方@AutoWarid注解注入即可使用。
@Controller
public class UsersController {
@Autowired
private UserDao userDao;
@Autowired
private DataSource dataSource;
@RequestMapping("/AllUsers")
public void AllUsers(){
System.out.println(dataSource.getClass());
List<Users> all = userDao.findAll();
for (Users users : all) {
System.out.println(users);
}
}
}
===================================================
配置文件方式:
编写接口(不需要再使用注解编写sql):
@Repository
public interface UserDao {
/*查询全部的用户 可以直接使用注解的方式编写sql*/
//@Select("select * from users")
List<Users> findAll();
/*根据id查询用户*/
//@Select("select * from users where id = #{id}")
Users findUsersByid(Integer id);
/*当多个参数时,一定要加上注解@Param*/
//@Select("select * from users where username = #{username} and password = #{password}")
Users findUsersByUsernameAndPassword(@Param("username") String username,
@Param("password") String password);
}
编写配置文件(同以前方式:)
<?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.ceh.dao.UserDao">
<!--/*查询全部的用户 可以直接使用注解的方式编写sql*/
/*@Select("select * from users")*/
List<Users> findAll();-->
<select id="findAll" resultType="Users">
select * from users
</select>
<!-- /*根据id查询用户*/
//@Select("select * from users where id = #{id}")
Users findUsersByid(Integer id);-->
<select id="findUsersByid" resultType="Users" parameterType="Integer">
select * from users where id = #{id}
</select>
<!--/*当多个参数时,一定要加上注解@Param*/
//@Select("select * from users where username = #{username} and password = #{password}")
Users findUsersByUsernameAndPassword(@Param("username") String username,
@Param("password") String password);-->
<select id="findUsersByUsernameAndPassword" resultType="Users">
select * from users where username = #{username} and password = #{password}
</select>
xml中的namespace一定要和dao接口保持一致。
再application.yaml中编写配置:
#整合mybatis
mybatis:
#配置mapper文件位置目录,ssm中的扫描mapper文件包是一样的
mapper-locations: classpath:mappers/*.xml
#配置别名
type-aliases-package: com.ceh.pojo
此时注意:
mapper-locations: classpath:mappers/*.xml的位置
目录结构: