前面介紹了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)修改默認參數;