數據庫鏈接池負責分配、管理和釋放數據庫鏈接,它容許應用程序重複使用一個現有的數據庫鏈接,而不是再從新創建一個;釋放空閒時間超過最大空閒時間的數據庫鏈接來避免由於沒有釋放數據庫鏈接而引發的數據庫鏈接遺漏。經過數據庫鏈接池能明顯提升對數據庫操做的性能。在Java應用程序開發中,經常使用的鏈接池有DBCP、C3P0、Proxool等。css
Spring Boot默認提供了若干種可用的鏈接池,默認的數據源是:org.apache.tomcat.jdbc.pool.DataSource。而Druid是阿里系提供的一個開源鏈接池,除在鏈接池以外,Druid還提供了很是優秀的數據庫監控和擴展功能。接下來,咱們就來說解如何實現Spring Boot與Druid鏈接池的集成。html
Druid是阿里開源的一個JDBC應用組件, 其包括三部分:java
經過Druid鏈接池中間件, 咱們能夠實現:mysql
更多詳細信息參考官方文檔:https://github.com/alibaba/druid/wikigit
接下來,咱們就經過實際案例來說解如何集成Druid數據源,爲了不重複篇幅,此篇教程的源碼基於《Spring Boot:整合MyBatis框架》一篇的源碼實現,讀者請先參考並根據教程連接先行獲取基礎源碼和數據庫內容。github
打開pom文件,添加 druid 相關的 maven 依賴。web
pom.xmlspring
<!-- druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency>
Druid Spring Boot Starter 是阿里官方提供的 Spring Boot 插件,用於幫助在Spring Boot項目中輕鬆集成Druid數據庫鏈接池和監控。sql
更多資料參考:數據庫
Druid: https://github.com/alibaba/druid
Druid Spring Starter: https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
把原有的數據源配置替換成 druid 數據源並配置數據源相關參數。
application.yml
# Tomcat server: tomcat: uri-encoding: UTF-8 max-threads: 1000 min-spare-threads: 30 port: 8080 #context-path: /springboot # DataSource spring: datasource: name: druidDataSource type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8 username: root password: 123456 filters: stat,wall,log4j,config max-active: 100 initial-size: 1 max-wait: 60000 min-idle: 1 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: select 'x' test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-open-prepared-statements: 50 max-pool-prepared-statement-per-connection-size: 20
參數說明:
- spring.datasource.druid.max-active 最大鏈接數
- spring.datasource.druid.initial-size 初始化大小
- spring.datasource.druid.min-idle 最小鏈接數
- spring.datasource.druid.max-wait 獲取鏈接等待超時時間
- spring.datasource.druid.time-between-eviction-runs-millis 間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒
- spring.datasource.druid.min-evictable-idle-time-millis 一個鏈接在池中最小生存的時間,單位是毫秒
- spring.datasource.druid.filters=config,stat,wall,log4j 配置監控統計攔截的filters,去掉後監控界面SQL沒法進行統計,’wall’用於防火牆
Druid提供如下幾種Filter信息:
若是須要經過定製的配置文件對druid進行自定義屬性配置,在config包中添加屬性配置類。
DruidDataSourceProperties.java
package com.louis.springboot.demo.config; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "spring.datasource.druid") public class DruidDataSourceProperties { // jdbc private String driverClassName; private String url; private String username; private String password; // jdbc connection pool private int initialSize; private int minIdle; private int maxActive = 100; private long maxWait; private long timeBetweenEvictionRunsMillis; private long minEvictableIdleTimeMillis; private String validationQuery; private boolean testWhileIdle; private boolean testOnBorrow; private boolean testOnReturn; private boolean poolPreparedStatements; private int maxPoolPreparedStatementPerConnectionSize; // filter private String filters; public int getInitialSize() { return initialSize; } public void setInitialSize(int initialSize) { this.initialSize = initialSize; } public int getMinIdle() { return minIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } public int getMaxActive() { return maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public long getMaxWait() { return maxWait; } public void setMaxWait(long maxWait) { this.maxWait = maxWait; } public long getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRunsMillis; } public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; } public long getMinEvictableIdleTimeMillis() { return minEvictableIdleTimeMillis; } public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; } public String getValidationQuery() { return validationQuery; } public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; } public boolean isTestWhileIdle() { return testWhileIdle; } public void setTestWhileIdle(boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; } public boolean isTestOnBorrow() { return testOnBorrow; } public void setTestOnBorrow(boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } public boolean isTestOnReturn() { return testOnReturn; } public void setTestOnReturn(boolean testOnReturn) { this.testOnReturn = testOnReturn; } public boolean isPoolPreparedStatements() { return poolPreparedStatements; } public void setPoolPreparedStatements(boolean poolPreparedStatements) { this.poolPreparedStatements = poolPreparedStatements; } public int getMaxPoolPreparedStatementPerConnectionSize() { return maxPoolPreparedStatementPerConnectionSize; } public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) { this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize; } public String getFilters() { return filters; } public void setFilters(String filters) { this.filters = filters; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
Druid Spring Starter 簡化了不少配置,若是默認配置知足不了你的需求,能夠自定義配置。更多配置參考:
Druid Spring Starter: https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
在config包中添加一個DruidConfig配置類。
DruidConfig.java
package com.louis.springboot.demo.config; import java.sql.SQLException; import javax.servlet.Filter; import javax.servlet.Servlet; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; @Configuration @EnableConfigurationProperties({DruidDataSourceProperties.class}) public class DruidConfig { @Autowired private DruidDataSourceProperties properties; @Bean @ConditionalOnMissingBean public DataSource druidDataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(properties.getDriverClassName()); druidDataSource.setUrl(properties.getUrl()); druidDataSource.setUsername(properties.getUsername()); druidDataSource.setPassword(properties.getPassword()); druidDataSource.setInitialSize(properties.getInitialSize()); druidDataSource.setMinIdle(properties.getMinIdle()); druidDataSource.setMaxActive(properties.getMaxActive()); druidDataSource.setMaxWait(properties.getMaxWait()); druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis()); druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis()); druidDataSource.setValidationQuery(properties.getValidationQuery()); druidDataSource.setTestWhileIdle(properties.isTestWhileIdle()); druidDataSource.setTestOnBorrow(properties.isTestOnBorrow()); druidDataSource.setTestOnReturn(properties.isTestOnReturn()); druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements()); druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(properties.getMaxPoolPreparedStatementPerConnectionSize()); try { druidDataSource.setFilters(properties.getFilters()); druidDataSource.init(); } catch (SQLException e) { e.printStackTrace(); } return druidDataSource; } /** * 註冊Servlet信息, 配置監控視圖 * * @return */ @Bean @ConditionalOnMissingBean public ServletRegistrationBean<Servlet> druidServlet() { ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<Servlet>(new StatViewServlet(), "/druid/*"); //白名單: servletRegistrationBean.addInitParameter("allow","192.168.1.195"); //IP黑名單 (存在共同時,deny優先於allow) : 若是知足deny的話提示:Sorry, you are not permitted to view this page. servletRegistrationBean.addInitParameter("deny","192.168.1.119"); //登陸查看信息的帳號密碼, 用於登陸Druid監控後臺 servletRegistrationBean.addInitParameter("loginUsername", "admin"); servletRegistrationBean.addInitParameter("loginPassword", "admin"); //是否可以重置數據. servletRegistrationBean.addInitParameter("resetEnable", "true"); return servletRegistrationBean; } /** * 註冊Filter信息, 監控攔截器 * * @return */ @Bean @ConditionalOnMissingBean public FilterRegistrationBean<Filter> filterRegistrationBean() { FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); return filterRegistrationBean; } }
說明:
- @EnableConfigurationProperties({DruidDataSourceProperties.class}) 用於導入上一步Druid的配置信息
- public ServletRegistrationBean druidServlet() 至關於Web Servlet配置
- public FilterRegistrationBean filterRegistrationBean() 至關於Web Filter配置
若是不使用上述的Servlet和Filter配置, 也能夠經過下述監控器配置實現:
DruidStatFilter.java
package com.louis.kitty.boot.config; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebInitParam; import com.alibaba.druid.support.http.WebStatFilter; /** * 配置監控攔截器, druid監控攔截器 */ @WebFilter(filterName="druidWebStatFilter", urlPatterns="/*", initParams={ @WebInitParam(name="exclusions",value="*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"), // 忽略資源 }) public class DruidStatFilter extends WebStatFilter { }
DruidStatViewServlet.java
package com.louis.kitty.boot.config; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import com.alibaba.druid.support.http.StatViewServlet; /** * druid監控視圖配置 */ @WebServlet(urlPatterns = "/druid/*", initParams={ @WebInitParam(name="allow",value="192.168.6.195"), // IP白名單 (沒有配置或者爲空,則容許全部訪問) @WebInitParam(name="deny",value="192.168.6.73"), // IP黑名單 (存在共同時,deny優先於allow) @WebInitParam(name="loginUsername",value="admin"), // 用戶名 @WebInitParam(name="loginPassword",value="admin"), // 密碼 @WebInitParam(name="resetEnable",value="true") // 禁用HTML頁面上的「Reset All」功能 }) public class DruidStatViewServlet extends StatViewServlet { private static final long serialVersionUID = 7359758657306626394L; }
到這裏Druid的配置就完成了,可是此時啓動應用,發現出錯了,錯誤內容大體以下,提示 log4j 相關的類查找不到,而後查看依賴,發現確實沒有 log4j 的依賴,有些奇怪,嘗試了一下,也沒發現其餘辦法,手動加一下吧。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mybatisConfig': Unsatisfied dependency expressed through field 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'druidDataSource' defined in class path resource [com/louis/kitty/boot/config/DruidConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'druidDataSource' threw exception; nested exception is java.lang.NoClassDefFoundError: org/apache/log4j/Priority at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:586) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:91) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:372) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1341) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:398) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:330) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1258) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE] at com.louis.kitty.boot.KittyApplication.main(KittyApplication.java:10) [classes/:na]
...
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Priority
at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_131]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_131]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) ~[na:1.8.0_131]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_131]
... 49 common frames omitted
pom.xml 添加 log4j 依賴,最新版本是 1.2.17。
<!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
在 resources 目錄下,新建一個 log4j.properties 參數配置文件,並鍵入以下內容:
### set log levels ### log4j.rootLogger = INFO,DEBUG, console, infoFile, errorFile ,debugfile,mail LocationInfo=true log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern =[%d{yyyy-MM-dd HH:mm:ss,SSS}]-[%p]:%m %x %n log4j.appender.infoFile = org.apache.log4j.DailyRollingFileAppender log4j.appender.infoFile.Threshold = INFO log4j.appender.infoFile.File = D:/logs/log log4j.appender.infoFile.DatePattern = '.'yyyy-MM-dd'.log' log4j.appender.infoFile.Append=true log4j.appender.infoFile.layout = org.apache.log4j.PatternLayout log4j.appender.infoFile.layout.ConversionPattern =[%d{yyyy-MM-dd HH:mm:ss,SSS}]-[%p]:%m %x %n log4j.appender.errorFile = org.apache.log4j.DailyRollingFileAppender log4j.appender.errorFile.Threshold = ERROR log4j.appender.errorFile.File = D:/logs/error log4j.appender.errorFile.DatePattern = '.'yyyy-MM-dd'.log' log4j.appender.errorFile.Append=true log4j.appender.errorFile.layout = org.apache.log4j.PatternLayout log4j.appender.errorFile.layout.ConversionPattern =[%d{yyyy-MM-dd HH:mm:ss,SSS}]-[%p]:%m %x %n #log4j.appender.debugfile = org.apache.log4j.DailyRollingFileAppender #log4j.appender.debugfile.Threshold = DEBUG #log4j.appender.debugfile.File = D:/logs/debug #log4j.appender.debugfile.DatePattern = '.'yyyy-MM-dd'.log' #log4j.appender.debugfile.Append=true #log4j.appender.debugfile.layout = org.apache.log4j.PatternLayout #log4j.appender.debugfile.layout.ConversionPattern =[%d{yyyy-MM-dd HH:mm:ss,SSS}]-[%p]:%m %x %n
配置完成後,從新編譯啓動,發現已經能夠啓動了。按理說,Spring Boot 已經集成了 log4j, 這個問題出現的有點奇怪,有知道答案的朋友,歡迎賜教,感激涕零。
啓動應用,訪問: http://localhost:8080/druid/login.html, 進入Druid監控後臺頁面。
注意:登陸用戶名和密碼,就是在DruidConfig配置類中配置的,查看並進行登陸。
首頁信息。
顯示鏈接數據源的相關信息。
分別訪問下面兩個接口以後,SQL監控的記錄結果。
http://localhost:8080/user/findByUserId?userId=1
http://localhost:8080/user/findAll
分別訪問下面兩個接口以後,URI監控的記錄結果。
http://localhost:8080/user/findByUserId?userId=1
http://localhost:8080/user/findAll
要想數據庫性能好,數據庫調優少不了。
服務器配置須要弄,SQL調優也得搞。
要問數據源哪家好,阿里DRUID準沒跑。
SQL監控作得好,語句調優沒煩惱。
官方網站:https://druid.apache.org/
官方文檔:https://github.com/alibaba/druid/wiki
在線文檔:http://tool.oschina.net/apidocs/apidoc?api=druid0.26
碼雲:https://gitee.com/liuge1988/spring-boot-demo.git
做者:朝雨憶輕塵
出處:https://www.cnblogs.com/xifengxiaoma/
版權全部,歡迎轉載,轉載請註明原文做者及出處。