前面介绍了SpringBoot的自动配置原理,用一句话归纳是:启动时加载全部,最终按照条件进行装配。本章节表面上是讲数据访问,但其核心仍是讲SpringBoot的自动配置,只不过自动配置的对象是数据库相关的依赖(如:druid、MyBatis、MyBatis-Plugs等)。这些依赖的导入与装配都是SpringBoot帮咱们自动完成的。css
SpringBoot默认使用的数据源是Hikari
(下面有源码分析),之后咱们将使用阿里的Druid
数据源进行数据库相关配置与操做。java
在本篇,咱们能够知道:mysql
能够直接跳转至第四点总结那查看源码结构图。git
咱们先基于SpringBoot默认的HikariDataSource数据源,导入JDBC场景,看看SpringBoot帮咱们自动配置了什么。github
首先导入JDBC场景依赖web
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency>
导入JDBC场景依赖后,咱们能够在Maven的Dependencies依赖里看出spring-boot-starter-data-jdbc
自动帮咱们引入了数据源、JDBC与事务相关jar包。spring
少了数据库链接驱动依赖sql
咱们能够发现,这其中没有数据库链接驱动依赖,道理很简单,SpringBoot并不知道咱们要使用什么数据库(MySQL仍是Oracle或其余)。数据库
直接在pom.xml文件里添加数据库驱动依赖便可app
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
咱们不用关注数据库驱动的版本,在SpringBoot里使用版本仲裁自动匹配( 数据库版本和驱动版本对应 ),固然,咱们也能够自定义版本。
自定义链接驱动版本:
pom.xml
里dependency
直接依赖引入具体版本(maven的就近依赖原则)
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>
pom.xml
里properties
从新声明版本(maven属性的就近优先原则)
<properties> <java.version>1.8</java.version> <mysql.version>5.1.49</mysql.version> </properties>
基于上面的例子,咱们来剖析SpringBoot数据源的自动装配原理。
因为数据源的配置是SpringBoot帮咱们自动配置的,所以咱们在外部依赖库里找到jdbc相关的自动配置:
往下翻翻找到jdbc的包:
能够看到引入jdbc相关依赖后SpringBoot帮咱们引入了不少自动配置类,如:
DataSourceAutoConfiguration
:数据源的自动配置类;
DataSourceTransactionManagerAutoConfiguration
:事务管理器的自动配置;
JdbcTemplateAutoConfiguration
:JdbcTemplate的自动配置,能够来对数据库进行crud(JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用);
JndiDataSourceAutoConfiguration
:jndi的自动配置;
XADataSourceAutoConfiguration
:分布式事务相关的自动配置;
等......
咱们对其中两个进行分析:
解释了SpringBoot底层的数据源是Hikari;
其中DataSourceAutoConfiguration
是数据源的自动配置类,咱们点进去看源码,发现其定义了一些静态方法,其中底层数据源相关的是:
@Configuration(proxyBeanMethods = false) @Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class}) @ConditionalOnMissingBean({DataSource.class, XADataSource.class}) @Import({Hikari.class, Tomcat.class, Dbcp2.class, OracleUcp.class, Generic.class, DataSourceJmxConfiguration.class}) protected static class PooledDataSourceConfiguration { protected PooledDataSourceConfiguration() { } }
其中老熟人@ConditionalOnMissingBean
注解的含义是当容器内没有DataSource数据源时,才进行下面的自动配置默认的数据源;而@Import
注解则说明了咱们要引入的默认数据源是Hikari数据源。也就是说,在咱们不作任何处理的状况下,SpringBoot为咱们底层配置好的链接池是:HikariDataSource
。
说明了jdbc配置的语法:包括前缀与用户可配置的属性有哪些;
JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用;
JdbcTemplateAutoConfiguration源码:
@Configuration(proxyBeanMethods = false) @ConditionalOnClass({DataSource.class, JdbcTemplate.class}) @ConditionalOnSingleCandidate(DataSource.class) @AutoConfigureAfter({DataSourceAutoConfiguration.class}) @EnableConfigurationProperties({JdbcProperties.class}) @Import({JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class}) public class JdbcTemplateAutoConfiguration { public JdbcTemplateAutoConfiguration() { } }
从@EnableConfigurationProperties
注解可知,自动配置的相关属性在JdbcProperties.class
类里,咱们点进去看看:
经过源码咱们能够知道如下信息:
spring.jdbc
这里直接给出示例:
spring: datasource: url: jdbc:mysql://localhost:3306/db_account username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver # 默认配置,可不写 # type: com.zaxxer.hikariDataSource
测试类以下:
@Slf4j @SpringBootTest class Boot05WebAdminApplicationTests { @Autowired JdbcTemplate jdbcTemplate; @Test void contextLoads() { // jdbcTemplate.queryForObject("select * from account_tbl") // jdbcTemplate.queryForList("select * from account_tbl",) Long aLong = jdbcTemplate.queryForObject("select count(*) from account_tbl", Long.class); log.info("记录总数:{}",aLong); } }
在实际生产中,咱们通常使用阿里的Druid替换默认的Hikari数据源。
Druid官方github地址为:https://github.com/alibaba/druid
在SpringBoot中,整合第三方技术有两种方式:自定义与starter。在实际生产中通常使用starter,它只须要引入一个xxx-spring-boot-starter依赖就好,能帮咱们省去不少配置工做。但咱们也可能遇到自定义配置的需求,所以这两种方法都会说起。
重点不在这,这里只列出相关步骤便可;
1) 首先引入druid数据源依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.17</version> </dependency>
2) SpringBoot采用编写配置类的方式
在config包下新建MyDataSourceConfig类
@Configuration public class MyDataSourceConfig { // 默认的自动配置是判断容器中没有才会配@ConditionalOnMissingBean(DataSource.class),即有@Bean后容器自动配置不生效 @ConfigurationProperties("spring.datasource") //绑定属性 @Bean public DataSource dataSource() throws SQLException { DruidDataSource druidDataSource = new DruidDataSource(); //在注册本身的数据源时要给核心属性赋值,又由于不能写死在代码里,故抽取到配置文件里 // druidDataSource.setUrl(); // druidDataSource.setUsername(); // druidDataSource.setPassword(); //加入监控功能(可写进配置文件) druidDataSource.setFilters("stat,wall"); //设置最大活跃线程数(可写进配置文件) druidDataSource.setMaxActive(10); return druidDataSource; } /** * 配置 druid的监控页功能 * @return */ @Bean public ServletRegistrationBean statViewServlet(){ StatViewServlet statViewServlet = new StatViewServlet(); ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*"); //配置初始化参数:须要帐号密码才能登录查看监控页面 registrationBean.addInitParameter("loginUsername","admin"); registrationBean.addInitParameter("loginPassword","123456"); return registrationBean; } /** * WebStatFilter 用于采集web-jdbc关联监控的数据 */ @Bean public FilterRegistrationBean webStatFilter(){ WebStatFilter webStatFilter = new WebStatFilter(); FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter); //设置拦截路径 filterRegistrationBean.setUrlPatterns(Arrays.asList("/*")); filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); return filterRegistrationBean; } }
优化写法:凡是setxxx方法均可以在配置文件中说明,优化后可在配置文件中直接说明。Spring原生配置方式:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="maxActive" value="20" /> <property name="initialSize" value="1" /> <property name="maxWait" value="60000" /> <property name="minIdle" value="1" /> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <property name="poolPreparedStatements" value="true" /> <property name="maxOpenPreparedStatements" value="20" />
在实际生产中,咱们大部分时候使用自动配置方式;相比上面繁琐而复杂的工做,自动配置就简单不少了,这里先列出相关步骤方法在,再作源码分析,看看SpringBoot为咱们干了些什么。
这里也是直接给出示例代码;
1) 引入druid-starter
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency>
*小知识:第三方提供的Starter统一用xxx-spring-boot-starter;而官方提供的Starter统一用spring-boot-starter-xxx。
2) 直接配置便可
spring: datasource: #数据源的基本属性 url: jdbc:mysql://localhost:3306/db_account username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver druid: #druid数据源相关配置 aop-patterns: com.atguigu.admin.* #监控SpringBean filters: stat,wall # 底层开启功能,stat(sql监控),wall(防火墙) stat-view-servlet: # 配置监控页功能 enabled: true #默认false,须要手动开启 login-username: admin #登陆的用户名 login-password: admin #登陆的密码 resetEnable: false #禁用重置按钮 web-stat-filter: # 监控web enabled: true urlPattern: /* #匹配的路径 exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' #排除的路径 filter: stat: # 对上面filters里面的stat的详细配置 enabled: true slow-sql-millis: 1000 #慢查询时间,超过1000ms的查询都是慢查询 logSlowSql: true #是否使用日志记录慢查询 wall: # 对上面filters里面的wall的详细配置 enabled: true config: drop-table-allow: false
SpringBoot配置官方示例:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
按上面操做就能把jdbc配置好了,但咱们须要知道两个问题:
1.为何引入一个依赖就能将jdbc配置好,换句话说引入这个依赖后SpringBoot帮咱们干了些什么?
2.用户可修改的配置项有哪些?
这些问题能够在下面的源码分析中找到答案。
【核心原理】原理概述:
导入starter场景启动器后,根据SpringBoot的设置模式,首先找到META-INF
包下的spring.factories
工厂,经过读取EnableAutoConfiguration
获取启动时加载的类 :XXXAutoConfiguration
自动配置类;
自动配置类会利用@Bean
注解把场景下相关组件注册进容器中,这些组件的核心配置项会经过@EnableConfigurationProperties
注解跟XXXProperties
配置文件绑定;
由此咱们能够得到配置类(XXXAutoConfiguration)与配置项(XXXProperties)信息。
配置类(XXXAutoConfiguration)里配置了核心组件;
配置项(XXXProperties)里主要包含两个信息。其一是经过@ConfigurationProperties
注解能够获取配置文件的前缀(prefix=Constants.XXX);其二是配置项可修改的参数(YYY)名称及参数(ZZZ)。咱们在yml里经过[前缀.参数名称=参数](XXX.YYY=ZZZ)修改默认参数;
【案例分析】Druid数据源自动配置的实现:
上述原理在Druid数据源自动配置中简而言之就是:导入starter场景启动器,根据SpringBoot的设置模式,会有DruidDataSourceAutoConfigure自动配置类,自动配置类会把场景下相关组件注册进容器中,相关组件的核心配置项跟配置文件绑定。
引入Druid的stater依赖后,能够在META-INF
包下的spring.factories
工厂里找到Druid数据源的自动配置类:
按住“Ctrl+左键”点进去查看DruidDataSourceAutoConfigure
源码:
@Configuration @ConditionalOnClass({DruidDataSource.class}) @AutoConfigureBefore({DataSourceAutoConfiguration.class}) @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) @Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class DruidDataSourceAutoConfigure { private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class); public DruidDataSourceAutoConfigure() { } @Bean(initMethod = "init") @ConditionalOnMissingBean public DataSource dataSource() { LOGGER.info("Init DruidDataSource"); return new DruidDataSourceWrapper(); } }
这里有几个须要注意的点:
@AutoConfigureBefore(DataSourseAutoConfiguration.class)
语句的含义是:在SpringBoot自动配置数据源前先配置Druid数据源;这是一个优先级的关系,即Druid场景的自动配置类优先执行;
@ConditionalOnMissingBean public DataSource dataSource(){ return new DruidDataSourceWrapper(); }
@Import()
注解给咱们导入了如下组件:
DruidSpringAopConfiguration.class
:利用AOP配置SpringBean监控的相关组件;
DruidStatViewServletConfiguration.class
:监控页相关配置;
DruidWebStatFilterConfiguration.class
:web监控配置;
DruidFilterConfiguration.class
:全部Druid默认filter的配置;
private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat"; private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config"; private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding"; private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j"; private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j"; private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2"; private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log"; private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filterwall";
在DataSource的构造方法里,给咱们new了一个DruidDataSourceWrapper(Druid数据源包装器);点进去查看源码:
spring.datasource.druid
里;DruidDataSourceWrapper源码:
@ConfigurationProperties("spring.datasource.druid") class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean { @Autowired private DataSourceProperties basicProperties; DruidDataSourceWrapper() { } public void afterPropertiesSet() throws Exception { if (super.getUsername() == null) { super.setUsername(this.basicProperties.determineUsername()); } if (super.getPassword() == null) { super.setPassword(this.basicProperties.determinePassword()); } if (super.getUrl() == null) { super.setUrl(this.basicProperties.determineUrl()); } if (super.getDriverClassName() == null) { super.setDriverClassName(this.basicProperties.getDriverClassName()); } } @Autowired( required = false ) public void autoAddFilters(List<Filter> filters) { super.filters.addAll(filters); } public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) { try { super.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); } catch (IllegalArgumentException var4) { super.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis; } } }
在本篇里咱们知道了数据源的自动配置原理,其原理结构图以下:
以及Druid数据源自动配置原理,其源码结构图以下:
最重要的是知道了SpringBoot整合第三方技术的两种方式:
对SpringBoot的自动配置逻辑也有必定的认识:
导入starter场景启动器后,根据SpringBoot的设置模式,首先找到META-INF
包下的spring.factories
工厂,经过读取EnableAutoConfiguration
获取启动时加载的类 :XXXAutoConfiguration
自动配置类;
自动配置类会利用@Bean
注解把场景下相关组件注册进容器中,这些组件的核心配置项会经过@EnableConfigurationProperties
注解跟XXXProperties
配置文件绑定;
由此咱们能够得到配置类(XXXAutoConfiguration)与配置项(XXXProperties)信息。
@ConfigurationProperties
注解能够获取配置文件的前缀(prefix=Constants.XXX);其二是配置项可修改的参数(YYY)名称及参数(ZZZ)。咱们在yml里经过[前缀.参数名称=参数](XXX.YYY=ZZZ)修改默认参数;