From 9e0e4d02a2b7ce774a03e72c474e453e5df349a8 Mon Sep 17 00:00:00 2001 From: trang Date: Tue, 18 Jul 2017 14:44:26 +0800 Subject: [PATCH] 1.0.2 --- TODO.md | 5 +- druid-spring-boot-autoconfigure/pom.xml | 4 + .../DataSourceInitializerConfiguration.java | 30 +++ .../trang/druid/DruidAutoConfiguration.java | 48 +--- .../github/trang/druid/DruidProperties.java | 255 ------------------ .../druid/DruidServletConfiguration.java | 32 +-- .../trang/druid/DruidStatConfiguration.java | 38 +-- .../datasource/DruidMultiDataSource.java | 10 + .../datasource/DruidParentDataSource.java | 72 +++++ .../init/DataSourceInitializer.java | 177 ++++++++++++ .../druid/properties/DruidProperties.java | 20 ++ .../properties/DruidServletProperties.java | 95 +++++++ .../druid/properties/DruidStatProperties.java | 135 ++++++++++ ...itional-spring-configuration-metadata.json | 26 +- .../main/resources/META-INF/spring.factories | 3 +- druid-spring-boot-samples/pom.xml | 16 ++ .../DruidSpringBootStarterApplication.java | 4 +- .../trang/druid/config/DynamicDataSource.java | 42 +++ .../druid/config/SpringDataSourceConfig.java | 51 ++++ .../github/trang/druid/mapper/CityMapper.java | 6 +- .../src/main/resources/application.yml | 35 ++- .../src/main/resources/import.sql | 10 +- .../com/github/trang/druid/test/BaseTest.java | 21 ++ .../druid/test/DynamicDataSourceTests.java | 58 ++++ .../github/trang/druid/test/MyBatisTests.java | 29 ++ ...dTests.java => SingleDataSourceTests.java} | 25 +- .../main/resources/META-INF/spring.provides | 2 +- pom.xml | 24 +- 28 files changed, 896 insertions(+), 377 deletions(-) create mode 100644 druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DataSourceInitializerConfiguration.java delete mode 100644 druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidProperties.java create mode 100644 druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/DruidMultiDataSource.java create mode 100644 druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/DruidParentDataSource.java create mode 100644 druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/init/DataSourceInitializer.java create mode 100644 druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidProperties.java create mode 100644 druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidServletProperties.java create mode 100644 druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidStatProperties.java create mode 100644 druid-spring-boot-samples/src/main/java/com/github/trang/druid/config/DynamicDataSource.java create mode 100644 druid-spring-boot-samples/src/main/java/com/github/trang/druid/config/SpringDataSourceConfig.java create mode 100644 druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/BaseTest.java create mode 100644 druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/DynamicDataSourceTests.java create mode 100644 druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/MyBatisTests.java rename druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/{DruidTests.java => SingleDataSourceTests.java} (61%) diff --git a/TODO.md b/TODO.md index 8a0dac6..0ec4c3d 100644 --- a/TODO.md +++ b/TODO.md @@ -2,8 +2,7 @@ ## 1.0.2 1. ~~增加 `WallConfig` 的提示~~ -2. 增加 `ConfigFilter` 的配置,替换默认的 ConnectProperties 方式 -3. 优化配置项的友好提示 +2. ~~增加多数据源支持~~ ## 1.0.3 -1. 增加读写分离 \ No newline at end of file +1. 增加 `ConfigFilter` 的配置,替换默认的 ConnectProperties 方式 \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/pom.xml b/druid-spring-boot-autoconfigure/pom.xml index 2e7fb55..85624dd 100644 --- a/druid-spring-boot-autoconfigure/pom.xml +++ b/druid-spring-boot-autoconfigure/pom.xml @@ -22,6 +22,10 @@ org.springframework.boot spring-boot-configuration-processor + + org.springframework + spring-jdbc + javax.servlet javax.servlet-api diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DataSourceInitializerConfiguration.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DataSourceInitializerConfiguration.java new file mode 100644 index 0000000..7de6e69 --- /dev/null +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DataSourceInitializerConfiguration.java @@ -0,0 +1,30 @@ +package com.github.trang.druid; + +import com.github.trang.druid.datasource.init.DataSourceInitializer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; + +/** + * 支持数据源初始化时执行 SQL + * + * 照搬自 #{@link org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer} + * + * 在多数据源场景下,#{@link DataSourceAutoConfiguration} 会报 #{@code DataSource} 和 + * #{@code DataSourceInitializer} 的循环依赖,目前的解决办法是排除 #{@link DataSourceAutoConfiguration}, + * 但是这样一来,Spring Boot 在数据源初始化时执行 SQL 的特性会被移除,此类是为了保留该特性 + * + * @author trang + */ +public class DataSourceInitializerConfiguration { + + @Bean + @ConditionalOnMissingBean(DataSourceInitializer.class) + public com.github.trang.druid.datasource.init.DataSourceInitializer dataSourceInitializer + (DataSourceProperties properties, ApplicationContext applicationContext) { + return new DataSourceInitializer(properties, applicationContext); + } + +} diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidAutoConfiguration.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidAutoConfiguration.java index 7402872..1870b0c 100644 --- a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidAutoConfiguration.java +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidAutoConfiguration.java @@ -1,6 +1,5 @@ package com.github.trang.druid; -import com.alibaba.druid.filter.Filter; import com.alibaba.druid.filter.config.ConfigFilter; import com.alibaba.druid.filter.logging.CommonsLogFilter; import com.alibaba.druid.filter.logging.Log4j2Filter; @@ -10,29 +9,24 @@ import com.alibaba.druid.filter.stat.StatFilter; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.wall.WallConfig; import com.alibaba.druid.wall.WallFilter; +import com.github.trang.druid.datasource.DruidParentDataSource; +import com.github.trang.druid.properties.DruidProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.util.StringUtils; import javax.sql.DataSource; -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; -import static com.github.trang.druid.DruidProperties.*; -import static java.util.stream.Collectors.toList; +import static com.github.trang.druid.properties.DruidProperties.*; /** * Druid 自动配置 @@ -76,6 +70,7 @@ public class DruidAutoConfiguration { @ConditionalOnProperty(prefix = DRUID_CONFIG_FILTER_PREFIX, name = "enabled", havingValue = "true") @Bean + @ConfigurationProperties(DRUID_CONFIG_FILTER_PREFIX) public ConfigFilter configFilter() { log.debug("druid config-filter init..."); return new ConfigFilter(); @@ -113,42 +108,11 @@ public class DruidAutoConfiguration { return new CommonsLogFilter(); } - /* - * 不使用 @Value 注入,避免因为找不到值抛出 NPE - */ - @Autowired - private DataSourceProperties dataSourceProperties; - @ConditionalOnMissingBean(DataSource.class) @Bean(initMethod = "init", destroyMethod = "close") - @ConfigurationProperties(DRUID_DATA_SOURCE_PREFIX) - public DruidDataSource dataSource(@Autowired(required = false) StatFilter statFilter, - @Autowired(required = false) WallFilter wallFilter, - @Autowired(required = false) ConfigFilter configFilter, - @Autowired(required = false) Slf4jLogFilter slf4jLogFilter, - @Autowired(required = false) Log4jFilter log4jFilter, - @Autowired(required = false) Log4j2Filter log4j2Filter, - @Autowired(required = false) CommonsLogFilter commonsLogFilter) { + public DruidDataSource dataSource() { log.debug("druid data-source init..."); - DruidDataSource dataSource = new DruidDataSource(); - if (!StringUtils.isEmpty(dataSourceProperties.getDriverClassName())) { - dataSource.setDriverClassName(dataSourceProperties.getDriverClassName()); - } - if (!StringUtils.isEmpty(dataSourceProperties.getUrl())) { - dataSource.setUrl(dataSourceProperties.getUrl()); - } - if (!StringUtils.isEmpty(dataSourceProperties.getUsername())) { - dataSource.setUsername(dataSourceProperties.getUsername()); - } - if (!StringUtils.isEmpty(dataSourceProperties.getPassword())) { - dataSource.setPassword(dataSourceProperties.getPassword()); - } - List filters = Stream.of(statFilter, wallFilter, configFilter, slf4jLogFilter, log4jFilter, - log4j2Filter, commonsLogFilter) - .filter(Objects::nonNull) - .collect(toList()); - dataSource.setProxyFilters(filters); - return dataSource; + return new DruidParentDataSource() {}; } } \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidProperties.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidProperties.java deleted file mode 100644 index eda02f6..0000000 --- a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidProperties.java +++ /dev/null @@ -1,255 +0,0 @@ -package com.github.trang.druid; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.github.trang.druid.DruidProperties.DRUID_DATA_SOURCE_PREFIX; - -/** - * Druid 自定义配置 - * - * @author trang - */ -@ConfigurationProperties(DRUID_DATA_SOURCE_PREFIX) -public class DruidProperties { - - public static final String DRUID_DATA_SOURCE_PREFIX = "spring.datasource.druid"; - public static final String DRUID_STAT_FILTER_PREFIX = "spring.datasource.druid.stat"; - public static final String DRUID_WALL_FILTER_PREFIX = "spring.datasource.druid.wall"; - public static final String DRUID_WALL_CONFIG_PREFIX = "spring.datasource.druid.wall.config"; - public static final String DRUID_CONFIG_FILTER_PREFIX = "spring.datasource.druid.config"; - public static final String DRUID_SLF4J_FILTER_PREFIX = "spring.datasource.druid.slf4j"; - public static final String DRUID_LOG4J_FILTER_PREFIX = "spring.datasource.druid.log4j"; - public static final String DRUID_LOG4J2_FILTER_PREFIX = "spring.datasource.druid.log4j2"; - public static final String DRUID_COMMONS_LOG_FILTER_PREFIX = "spring.datasource.druid.commons-log"; - public static final String DRUID_WEB_STATE_PREFIX = "spring.datasource.druid.web-stat"; - public static final String DRUID_AOP_STAT_PREFIX = "spring.datasource.druid.aop-stat"; - public static final String DRUID_STAT_VIEW_SERVLET_PREFIX = "spring.datasource.druid.stat-view-servlet"; - - @NestedConfigurationProperty - private WebStat webStat = new WebStat(); - @NestedConfigurationProperty - private AopStat aopStat = new AopStat(); - @NestedConfigurationProperty - private StatViewServlet statViewServlet = new StatViewServlet(); - - public WebStat getWebStat() { - return webStat; - } - - public void setWebStat(WebStat webStat) { - this.webStat = webStat; - } - - public AopStat getAopStat() { - return aopStat; - } - - public void setAopStat(AopStat aopStat) { - this.aopStat = aopStat; - } - - public StatViewServlet getStatViewServlet() { - return statViewServlet; - } - - public void setStatViewServlet(StatViewServlet statViewServlet) { - this.statViewServlet = statViewServlet; - } - - /** - * WebStat 自定义配置 - * - * @author trang - */ - public static class WebStat { - // 是否开启 web-jdbc 监控,默认否 - private boolean enabled; - // 过滤器 url 的映射规则 - private String urlPatterns = "/*"; - // 过滤器 url 的排除规则 - private String exclusions = "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"; - // 是否开启 session 统计,默认否 - private boolean sessionStatEnable; - // session 统计的最大值,默认值 1000 - private Integer sessionStatMaxCount = 1000; - // 配置当前 session 的用户 - private String principalSessionName; - // 配置当前 cookie 的用户 - private String principalCookieName; - // 是否开启监控单个 url 调用的 sql 列表,默认是 - private boolean profileEnable = true; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getUrlPatterns() { - return urlPatterns; - } - - public void setUrlPatterns(String urlPatterns) { - this.urlPatterns = urlPatterns; - } - - public String getExclusions() { - return exclusions; - } - - public void setExclusions(String exclusions) { - this.exclusions = exclusions; - } - - public boolean isSessionStatEnable() { - return sessionStatEnable; - } - - public void setSessionStatEnable(boolean sessionStatEnable) { - this.sessionStatEnable = sessionStatEnable; - } - - public Integer getSessionStatMaxCount() { - return sessionStatMaxCount; - } - - public void setSessionStatMaxCount(Integer sessionStatMaxCount) { - this.sessionStatMaxCount = sessionStatMaxCount; - } - - public String getPrincipalSessionName() { - return principalSessionName; - } - - public void setPrincipalSessionName(String principalSessionName) { - this.principalSessionName = principalSessionName; - } - - public String getPrincipalCookieName() { - return principalCookieName; - } - - public void setPrincipalCookieName(String principalCookieName) { - this.principalCookieName = principalCookieName; - } - - public boolean isProfileEnable() { - return profileEnable; - } - - public void setProfileEnable(boolean profileEnable) { - this.profileEnable = profileEnable; - } - - } - - /** - * Aop 监控自定义配置 - */ - public static class AopStat { - // 是否开启基于 aop 的监控,默认否 - private boolean enabled; - // aop 拦截规则 - private String[] patterns; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String[] getPatterns() { - return patterns; - } - - public void setPatterns(String[] patterns) { - this.patterns = patterns; - } - - } - - /** - * StatViewServlet 自定义配置 - * - * @author trang - */ - public static class StatViewServlet { - // 是否开启 Druid 的数据统计页面,默认否 - private boolean enabled= false; - // Servlet 的映射规则,默认访问 "http://xxx/druid/" - private String urlMappings = "/druid/*"; - // Druid 统计页面的登陆用户名 - private String loginUsername; - // Druid 统计页面的登陆密码 - private String loginPassword; - // Druid 统计页面的访问白名单,默认允许所有人访问 - private String allow; - // Druid 统计页面的访问黑名单,优先于白名单 - private String deny; - // 是否允许清空统计数据,默认否 - private boolean resetEnable; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getUrlMappings() { - return urlMappings; - } - - public void setUrlMappings(String urlMappings) { - this.urlMappings = urlMappings; - } - - public String getLoginUsername() { - return loginUsername; - } - - public void setLoginUsername(String loginUsername) { - this.loginUsername = loginUsername; - } - - public String getLoginPassword() { - return loginPassword; - } - - public void setLoginPassword(String loginPassword) { - this.loginPassword = loginPassword; - } - - public String getAllow() { - return allow; - } - - public void setAllow(String allow) { - this.allow = allow; - } - - public String getDeny() { - return deny; - } - - public void setDeny(String deny) { - this.deny = deny; - } - - public boolean isResetEnable() { - return resetEnable; - } - - public void setResetEnable(boolean resetEnable) { - this.resetEnable = resetEnable; - } - - } - -} \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidServletConfiguration.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidServletConfiguration.java index cbecad0..8d64673 100644 --- a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidServletConfiguration.java +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidServletConfiguration.java @@ -1,15 +1,17 @@ package com.github.trang.druid; import com.alibaba.druid.support.http.StatViewServlet; +import com.github.trang.druid.properties.DruidServletProperties.DruidStatViewServletProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.util.StringUtils; -import static com.github.trang.druid.DruidProperties.DRUID_STAT_VIEW_SERVLET_PREFIX; +import static com.github.trang.druid.properties.DruidServletProperties.DRUID_STAT_VIEW_SERVLET_PREFIX; /** * Druid Servlet 配置 @@ -17,6 +19,7 @@ import static com.github.trang.druid.DruidProperties.DRUID_STAT_VIEW_SERVLET_PRE * @author trang */ @ConditionalOnWebApplication +@EnableConfigurationProperties(DruidStatViewServletProperties.class) public class DruidServletConfiguration { private static final Logger log = LoggerFactory.getLogger(DruidServletConfiguration.class); @@ -24,31 +27,30 @@ public class DruidServletConfiguration { /** * Druid 提供了一个 StatViewServlet 用于展示 Druid 的统计信息 * 这个 StatViewServlet 的用途包括: - * 1. 提供监控信息展示的 HTML 页面 - * 2. 提供监控信息的 JSON API + * 1. 提供监控信息展示的 HTML 页面 + * 2. 提供监控信息的 JSON API */ @ConditionalOnProperty(prefix = DRUID_STAT_VIEW_SERVLET_PREFIX, name = "enabled", havingValue = "true") @Bean - public ServletRegistrationBean servletRegistrationBean(DruidProperties properties) { + public ServletRegistrationBean servletRegistrationBean(DruidStatViewServletProperties properties) { log.debug("druid stat-view-servlet init..."); ServletRegistrationBean registration = new ServletRegistrationBean(); - DruidProperties.StatViewServlet config = properties.getStatViewServlet(); StatViewServlet statViewServlet = new StatViewServlet(); registration.setServlet(statViewServlet); - registration.addUrlMappings(config.getUrlMappings()); - if (!StringUtils.isEmpty(config.getLoginUsername())) { - registration.addInitParameter("loginUsername", config.getLoginUsername()); + registration.addUrlMappings(properties.getUrlMappings()); + if (!StringUtils.isEmpty(properties.getLoginUsername())) { + registration.addInitParameter("loginUsername", properties.getLoginUsername()); } - if (!StringUtils.isEmpty(config.getLoginPassword())) { - registration.addInitParameter("loginPassword", config.getLoginPassword()); + if (!StringUtils.isEmpty(properties.getLoginPassword())) { + registration.addInitParameter("loginPassword", properties.getLoginPassword()); } - if (!StringUtils.isEmpty(config.getAllow())) { - registration.addInitParameter("allow", config.getAllow()); + if (!StringUtils.isEmpty(properties.getAllow())) { + registration.addInitParameter("allow", properties.getAllow()); } - if (!StringUtils.isEmpty(config.getDeny())) { - registration.addInitParameter("deny", config.getDeny()); + if (!StringUtils.isEmpty(properties.getDeny())) { + registration.addInitParameter("deny", properties.getDeny()); } - registration.addInitParameter("resetEnable", Boolean.toString(config.isResetEnable())); + registration.addInitParameter("resetEnable", Boolean.toString(properties.isResetEnable())); return registration; } diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidStatConfiguration.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidStatConfiguration.java index 97e5213..785362b 100644 --- a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidStatConfiguration.java +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/DruidStatConfiguration.java @@ -2,7 +2,8 @@ package com.github.trang.druid; import com.alibaba.druid.support.http.WebStatFilter; import com.alibaba.druid.support.spring.stat.DruidStatInterceptor; -import com.github.trang.druid.DruidProperties.WebStat; +import com.github.trang.druid.properties.DruidStatProperties.DruidAopStatProperties; +import com.github.trang.druid.properties.DruidStatProperties.DruidWebStatProperties; import org.aopalliance.aop.Advice; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,18 +13,20 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.util.StringUtils; -import static com.github.trang.druid.DruidProperties.DRUID_AOP_STAT_PREFIX; -import static com.github.trang.druid.DruidProperties.DRUID_WEB_STATE_PREFIX; +import static com.github.trang.druid.properties.DruidStatProperties.DRUID_AOP_STAT_PREFIX; +import static com.github.trang.druid.properties.DruidStatProperties.DRUID_WEB_STAT_PREFIX; /** * Druid 监控配置 * * @author trang */ +@EnableConfigurationProperties({DruidWebStatProperties.class, DruidAopStatProperties.class}) public class DruidStatConfiguration { /** @@ -44,9 +47,9 @@ public class DruidStatConfiguration { } @Bean - public RegexpMethodPointcutAdvisor druidStatAdvisor(DruidProperties druidProperties, + public RegexpMethodPointcutAdvisor druidStatAdvisor(DruidAopStatProperties properties, DruidStatInterceptor druidStatInterceptor) { - return new RegexpMethodPointcutAdvisor(druidProperties.getAopStat().getPatterns(), druidStatInterceptor); + return new RegexpMethodPointcutAdvisor(properties.getPatterns(), druidStatInterceptor); } @Bean @@ -67,27 +70,26 @@ public class DruidStatConfiguration { private static final Logger log = LoggerFactory.getLogger(DruidStatConfiguration.class); - @ConditionalOnProperty(prefix = DRUID_WEB_STATE_PREFIX, name = "enabled", havingValue = "true") + @ConditionalOnProperty(prefix = DRUID_WEB_STAT_PREFIX, name = "enabled", havingValue = "true") @Bean - public FilterRegistrationBean filterRegistrationBean(DruidProperties properties) { + public FilterRegistrationBean filterRegistrationBean(DruidWebStatProperties properties) { log.debug("druid web-stat-filter init..."); FilterRegistrationBean registration = new FilterRegistrationBean(); - WebStat config = properties.getWebStat(); WebStatFilter filter = new WebStatFilter(); registration.setFilter(filter); - registration.addUrlPatterns(config.getUrlPatterns()); - registration.addInitParameter("exclusions", config.getExclusions()); - registration.addInitParameter("sessionStatEnable", Boolean.toString(config.isSessionStatEnable())); - if (!StringUtils.isEmpty(config.getSessionStatMaxCount())) { - registration.addInitParameter("sessionStatMaxCount",Integer.toString(config.getSessionStatMaxCount())); + registration.addUrlPatterns(properties.getUrlPatterns()); + registration.addInitParameter("exclusions", properties.getExclusions()); + registration.addInitParameter("sessionStatEnable", Boolean.toString(properties.isSessionStatEnable())); + if (!StringUtils.isEmpty(properties.getSessionStatMaxCount())) { + registration.addInitParameter("sessionStatMaxCount",Integer.toString(properties.getSessionStatMaxCount())); } - if (!StringUtils.isEmpty(config.getPrincipalSessionName())) { - registration.addInitParameter("principalSessionName", config.getPrincipalSessionName()); + if (!StringUtils.isEmpty(properties.getPrincipalSessionName())) { + registration.addInitParameter("principalSessionName", properties.getPrincipalSessionName()); } - if (!StringUtils.isEmpty(config.getPrincipalCookieName())) { - registration.addInitParameter("principalCookieName", config.getPrincipalCookieName()); + if (!StringUtils.isEmpty(properties.getPrincipalCookieName())) { + registration.addInitParameter("principalCookieName", properties.getPrincipalCookieName()); } - registration.addInitParameter("profileEnable", Boolean.toString(config.isProfileEnable())); + registration.addInitParameter("profileEnable", Boolean.toString(properties.isProfileEnable())); return registration; } diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/DruidMultiDataSource.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/DruidMultiDataSource.java new file mode 100644 index 0000000..dc98d74 --- /dev/null +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/DruidMultiDataSource.java @@ -0,0 +1,10 @@ +package com.github.trang.druid.datasource; + +/** + * Druid 多数据源支持,支持共用公共参数 + * + * @author trang + */ +public class DruidMultiDataSource extends DruidParentDataSource { + +} \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/DruidParentDataSource.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/DruidParentDataSource.java new file mode 100644 index 0000000..fca3b00 --- /dev/null +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/DruidParentDataSource.java @@ -0,0 +1,72 @@ +package com.github.trang.druid.datasource; + +import com.alibaba.druid.filter.Filter; +import com.alibaba.druid.filter.config.ConfigFilter; +import com.alibaba.druid.filter.logging.CommonsLogFilter; +import com.alibaba.druid.filter.logging.Log4j2Filter; +import com.alibaba.druid.filter.logging.Log4jFilter; +import com.alibaba.druid.filter.logging.Slf4jLogFilter; +import com.alibaba.druid.filter.stat.StatFilter; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.wall.WallFilter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import static com.github.trang.druid.properties.DruidProperties.DRUID_DATA_SOURCE_PREFIX; + +/** + * Druid 多数据源支持,支持共用公共参数 + * + * @author trang + */ +@ConfigurationProperties(DRUID_DATA_SOURCE_PREFIX) +public abstract class DruidParentDataSource extends DruidDataSource { + + @Autowired(required = false) + private StatFilter statFilter; + @Autowired(required = false) + private WallFilter wallFilter; + @Autowired(required = false) + private ConfigFilter configFilter; + @Autowired(required = false) + private Slf4jLogFilter slf4jLogFilter; + @Autowired(required = false) + private Log4jFilter log4jFilter; + @Autowired(required = false) + private Log4j2Filter log4j2Filter; + @Autowired(required = false) + private CommonsLogFilter commonsLogFilter; + // 不使用 @Value 注入,避免因为找不到值抛出 NPE + @Autowired + private DataSourceProperties dataSourceProperties; + + @PostConstruct + public void initCommonParams() { + if (!StringUtils.isEmpty(dataSourceProperties.getDriverClassName())) { + super.setDriverClassName(dataSourceProperties.getDriverClassName()); + } + if (!StringUtils.isEmpty(dataSourceProperties.getUrl())) { + super.setUrl(dataSourceProperties.getUrl()); + } + if (!StringUtils.isEmpty(dataSourceProperties.getUsername())) { + super.setUsername(dataSourceProperties.getUsername()); + } + if (!StringUtils.isEmpty(dataSourceProperties.getPassword())) { + super.setPassword(dataSourceProperties.getPassword()); + } + List filters = super.getProxyFilters(); + Stream.of(statFilter, wallFilter, configFilter, slf4jLogFilter, log4jFilter, log4j2Filter, + commonsLogFilter) + .filter(Objects::nonNull) + .filter(filter -> !filters.contains(filter)) + .forEach(filters::add); + } + +} \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/init/DataSourceInitializer.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/init/DataSourceInitializer.java new file mode 100644 index 0000000..9e35c79 --- /dev/null +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/datasource/init/DataSourceInitializer.java @@ -0,0 +1,177 @@ +package com.github.trang.druid.datasource.init; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; +import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.config.ResourceNotFoundException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.core.io.Resource; +import org.springframework.jdbc.config.SortedResourcesFactoryBean; +import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Bean to handle {@link DataSource} initialization by running {@literal schema-*.sql} on + * {@link PostConstruct} and {@literal data-*.sql} SQL scripts on a + * {@link DataSourceInitializedEvent}. + * + * @author Dave Syer + * @author Phillip Webb + * @author Eddú Meléndez + * @author Stephane Nicoll + * @author Kazuki Shimizu + * @since 1.1.0 + * @see com.github.trang.druid.DataSourceInitializerConfiguration + */ +public class DataSourceInitializer implements ApplicationListener { + + private static final Logger logger = LoggerFactory.getLogger(DataSourceInitializer.class); + + private final DataSourceProperties properties; + + private final ApplicationContext applicationContext; + + private DataSource dataSource; + + private boolean initialized = false; + + public DataSourceInitializer(DataSourceProperties properties, + ApplicationContext applicationContext) { + this.properties = properties; + this.applicationContext = applicationContext; + } + + @PostConstruct + public void init() { + if (!this.properties.isInitialize()) { + logger.debug("Initialization disabled (not running DDL scripts)"); + return; + } + if (this.applicationContext.getBeanNamesForType(DataSource.class, false, + false).length > 0) { + this.dataSource = this.applicationContext.getBean(DataSource.class); + } + if (this.dataSource == null) { + logger.debug("No DataSource found so not initializing"); + return; + } + runSchemaScripts(); + } + + private void runSchemaScripts() { + List scripts = getScripts("spring.datasource.schema", + this.properties.getSchema(), "schema"); + if (!scripts.isEmpty()) { + String username = this.properties.getSchemaUsername(); + String password = this.properties.getSchemaPassword(); + runScripts(scripts, username, password); + try { + this.applicationContext + .publishEvent(new DataSourceInitializedEvent(this.dataSource)); + // The listener might not be registered yet, so don't rely on it. + if (!this.initialized) { + runDataScripts(); + this.initialized = true; + } + } catch (IllegalStateException ex) { + logger.warn("Could not send event to complete DataSource initialization (" + + ex.getMessage() + ")"); + } + } + } + + @Override + public void onApplicationEvent(DataSourceInitializedEvent event) { + if (!this.properties.isInitialize()) { + logger.debug("Initialization disabled (not running data scripts)"); + return; + } + // NOTE the event can happen more than once and + // the event datasource is not used here + if (!this.initialized) { + runDataScripts(); + this.initialized = true; + } + } + + private void runDataScripts() { + List scripts = getScripts("spring.datasource.data", + this.properties.getData(), "data"); + String username = this.properties.getDataUsername(); + String password = this.properties.getDataPassword(); + runScripts(scripts, username, password); + } + + private List getScripts(String propertyName, List resources, + String fallback) { + if (resources != null) { + return getResources(propertyName, resources, true); + } + String platform = this.properties.getPlatform(); + List fallbackResources = new ArrayList(); + fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql"); + fallbackResources.add("classpath*:" + fallback + ".sql"); + return getResources(propertyName, fallbackResources, false); + } + + private List getResources(String propertyName, List locations, + boolean validate) { + List resources = new ArrayList(); + for (String location : locations) { + for (Resource resource : doGetResources(location)) { + if (resource.exists()) { + resources.add(resource); + } else if (validate) { + throw new ResourceNotFoundException(propertyName, resource); + } + } + } + return resources; + } + + private Resource[] doGetResources(String location) { + try { + SortedResourcesFactoryBean factory = new SortedResourcesFactoryBean( + this.applicationContext, Collections.singletonList(location)); + factory.afterPropertiesSet(); + return factory.getObject(); + } catch (Exception ex) { + throw new IllegalStateException("Unable to load resources from " + location, + ex); + } + } + + private void runScripts(List resources, String username, String password) { + if (resources.isEmpty()) { + return; + } + ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); + populator.setContinueOnError(this.properties.isContinueOnError()); + populator.setSeparator(this.properties.getSeparator()); + if (this.properties.getSqlScriptEncoding() != null) { + populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name()); + } + for (Resource resource : resources) { + populator.addScript(resource); + } + DataSource dataSource = this.dataSource; + if (StringUtils.hasText(username) && StringUtils.hasText(password)) { + dataSource = DataSourceBuilder.create(this.properties.getClassLoader()) + .driverClassName(this.properties.determineDriverClassName()) + .url(this.properties.determineUrl()).username(username) + .password(password).build(); + } + DatabasePopulatorUtils.execute(populator, dataSource); + } + +} \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidProperties.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidProperties.java new file mode 100644 index 0000000..11f8443 --- /dev/null +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidProperties.java @@ -0,0 +1,20 @@ +package com.github.trang.druid.properties; + +/** + * Druid 自定义配置 + * + * @author trang + */ +public class DruidProperties { + + public static final String DRUID_DATA_SOURCE_PREFIX = "spring.datasource.druid"; + public static final String DRUID_STAT_FILTER_PREFIX = "spring.datasource.druid.stat"; + public static final String DRUID_WALL_FILTER_PREFIX = "spring.datasource.druid.wall"; + public static final String DRUID_WALL_CONFIG_PREFIX = "spring.datasource.druid.wall.config"; + public static final String DRUID_CONFIG_FILTER_PREFIX = "spring.datasource.druid.config"; + public static final String DRUID_SLF4J_FILTER_PREFIX = "spring.datasource.druid.slf4j"; + public static final String DRUID_LOG4J_FILTER_PREFIX = "spring.datasource.druid.log4j"; + public static final String DRUID_LOG4J2_FILTER_PREFIX = "spring.datasource.druid.log4j2"; + public static final String DRUID_COMMONS_LOG_FILTER_PREFIX = "spring.datasource.druid.commons-log"; + +} \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidServletProperties.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidServletProperties.java new file mode 100644 index 0000000..858bc7d --- /dev/null +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidServletProperties.java @@ -0,0 +1,95 @@ +package com.github.trang.druid.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Druid 数据展示配置 + * + * @author trang + */ +public class DruidServletProperties { + + public static final String DRUID_STAT_VIEW_SERVLET_PREFIX = "spring.datasource.druid.stat-view-servlet"; + + /** + * StatViewServlet 自定义配置 + * + * @author trang + */ + @ConfigurationProperties(DRUID_STAT_VIEW_SERVLET_PREFIX) + public static class DruidStatViewServletProperties { + + // 是否开启 Druid 的数据统计页面,默认否 + private boolean enabled; + // Servlet 的映射规则,默认访问 "http://xxx/druid/" + private String urlMappings = "/druid/*"; + // Druid 统计页面的登陆用户名 + private String loginUsername; + // Druid 统计页面的登陆密码 + private String loginPassword; + // Druid 统计页面的访问白名单,默认允许所有人访问 + private String allow; + // Druid 统计页面的访问黑名单,优先于白名单 + private String deny; + // 是否允许清空统计数据,默认否 + private boolean resetEnable; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getUrlMappings() { + return urlMappings; + } + + public void setUrlMappings(String urlMappings) { + this.urlMappings = urlMappings; + } + + public String getLoginUsername() { + return loginUsername; + } + + public void setLoginUsername(String loginUsername) { + this.loginUsername = loginUsername; + } + + public String getLoginPassword() { + return loginPassword; + } + + public void setLoginPassword(String loginPassword) { + this.loginPassword = loginPassword; + } + + public String getAllow() { + return allow; + } + + public void setAllow(String allow) { + this.allow = allow; + } + + public String getDeny() { + return deny; + } + + public void setDeny(String deny) { + this.deny = deny; + } + + public boolean isResetEnable() { + return resetEnable; + } + + public void setResetEnable(boolean resetEnable) { + this.resetEnable = resetEnable; + } + + } + +} \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidStatProperties.java b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidStatProperties.java new file mode 100644 index 0000000..78ff872 --- /dev/null +++ b/druid-spring-boot-autoconfigure/src/main/java/com/github/trang/druid/properties/DruidStatProperties.java @@ -0,0 +1,135 @@ +package com.github.trang.druid.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Druid 监控配置 + * + * @author trang + */ +public class DruidStatProperties { + + public static final String DRUID_WEB_STAT_PREFIX = "spring.datasource.druid.web-stat"; + public static final String DRUID_AOP_STAT_PREFIX = "spring.datasource.druid.aop-stat"; + + /** + * WebStat 自定义配置 + * + * @author trang + */ + @ConfigurationProperties(DRUID_WEB_STAT_PREFIX) + public static class DruidWebStatProperties { + + // 是否开启 web-jdbc 监控,默认否 + private boolean enabled; + // 过滤器 url 的映射规则 + private String urlPatterns = "/*"; + // 过滤器 url 的排除规则 + private String exclusions = "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"; + // 是否开启 session 统计,默认否 + private boolean sessionStatEnable; + // session 统计的最大值,默认值 1000 + private Integer sessionStatMaxCount = 1000; + // 配置当前 session 的用户 + private String principalSessionName; + // 配置当前 cookie 的用户 + private String principalCookieName; + // 是否开启监控单个 url 调用的 sql 列表,默认是 + private boolean profileEnable = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getUrlPatterns() { + return urlPatterns; + } + + public void setUrlPatterns(String urlPatterns) { + this.urlPatterns = urlPatterns; + } + + public String getExclusions() { + return exclusions; + } + + public void setExclusions(String exclusions) { + this.exclusions = exclusions; + } + + public boolean isSessionStatEnable() { + return sessionStatEnable; + } + + public void setSessionStatEnable(boolean sessionStatEnable) { + this.sessionStatEnable = sessionStatEnable; + } + + public Integer getSessionStatMaxCount() { + return sessionStatMaxCount; + } + + public void setSessionStatMaxCount(Integer sessionStatMaxCount) { + this.sessionStatMaxCount = sessionStatMaxCount; + } + + public String getPrincipalSessionName() { + return principalSessionName; + } + + public void setPrincipalSessionName(String principalSessionName) { + this.principalSessionName = principalSessionName; + } + + public String getPrincipalCookieName() { + return principalCookieName; + } + + public void setPrincipalCookieName(String principalCookieName) { + this.principalCookieName = principalCookieName; + } + + public boolean isProfileEnable() { + return profileEnable; + } + + public void setProfileEnable(boolean profileEnable) { + this.profileEnable = profileEnable; + } + + } + + /** + * Aop 监控自定义配置 + */ + @ConfigurationProperties(DRUID_AOP_STAT_PREFIX) + public static class DruidAopStatProperties { + + // 是否开启基于 aop 的监控,默认否 + private boolean enabled; + // aop 拦截规则 + private String[] patterns; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String[] getPatterns() { + return patterns; + } + + public void setPatterns(String[] patterns) { + this.patterns = patterns; + } + + } + +} \ No newline at end of file diff --git a/druid-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/druid-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index f2b1b95..9a585b9 100644 --- a/druid-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/druid-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -3,64 +3,64 @@ { "name": "spring.datasource.druid.stat.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid stat filter are enabled.", + "description": "Whether or not druid stat-filter are enabled.", "defaultValue": true }, { "name": "spring.datasource.druid.wall.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid wall filter are enabled.", + "description": "Whether or not druid wall-filter are enabled.", "defaultValue": false }, { "name": "spring.datasource.druid.config.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid config filter are enabled.", + "description": "Whether or not druid config-filter are enabled.", "defaultValue": false }, { "name": "spring.datasource.druid.slf4j.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid slf4j filter are enabled.", + "description": "Whether or not druid slf4j-filter are enabled.", "defaultValue": false }, { "name": "spring.datasource.druid.log4j.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid log4j filter are enabled.", + "description": "Whether or not druid log4j-filter are enabled.", "defaultValue": false }, { "name": "spring.datasource.druid.log4j2.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid log4j2 filter are enabled.", + "description": "Whether or not druid log4j2-filter are enabled.", "defaultValue": false }, { "name": "spring.datasource.druid.commons-log.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid commons logging filter are enabled.", + "description": "Whether or not druid commons-log-filter are enabled.", "defaultValue": false }, { "name": "spring.datasource.druid.aop-stat.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid aop stat are enabled.", - "sourceType": "com.github.trang.druid.DruidProperties$AopStat", + "description": "Whether or not druid aop-stat are enabled.", + "sourceType": "com.github.trang.druid.properties.DruidStatProperties$DruidAopStatProperties", "defaultValue": false }, { "name": "spring.datasource.druid.web-stat.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid web stat are enabled.", - "sourceType": "com.github.trang.druid.DruidProperties$WebStat", + "description": "Whether or not druid web-stat are enabled.", + "sourceType": "com.github.trang.druid.properties.DruidStatProperties$DruidWebStatProperties", "defaultValue": false }, { "name": "spring.datasource.druid.stat-view-servlet.enabled", "type": "java.lang.Boolean", - "description": "Whether or not druid stat view servlet are enabled.", - "sourceType": "com.github.trang.druid.DruidProperties$StatViewServlet", + "description": "Whether or not druid stat-view-servlet are enabled.", + "sourceType": "com.github.trang.druid.properties.DruidServletProperties$DruidStatViewServletProperties", "defaultValue": false } ], diff --git a/druid-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/druid-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index d308da2..d3f9714 100644 --- a/druid-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/druid-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -1 +1,2 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.github.trang.druid.DruidAutoConfiguration \ No newline at end of file +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.github.trang.druid.DruidAutoConfiguration \ No newline at end of file diff --git a/druid-spring-boot-samples/pom.xml b/druid-spring-boot-samples/pom.xml index d8e17d1..cf851a3 100644 --- a/druid-spring-boot-samples/pom.xml +++ b/druid-spring-boot-samples/pom.xml @@ -39,6 +39,16 @@ org.springframework.boot spring-boot-starter-web + + + com.fasterxml.jackson.core + jackson-databind + + + org.hibernate + hibernate-validator + + org.springframework.boot @@ -47,6 +57,12 @@ org.mybatis.spring.boot mybatis-spring-boot-starter + + + org.apache.tomcat + tomcat-jdbc + + com.github.drtrang diff --git a/druid-spring-boot-samples/src/main/java/com/github/trang/druid/DruidSpringBootStarterApplication.java b/druid-spring-boot-samples/src/main/java/com/github/trang/druid/DruidSpringBootStarterApplication.java index dbec32c..646c90b 100644 --- a/druid-spring-boot-samples/src/main/java/com/github/trang/druid/DruidSpringBootStarterApplication.java +++ b/druid-spring-boot-samples/src/main/java/com/github/trang/druid/DruidSpringBootStarterApplication.java @@ -1,6 +1,7 @@ package com.github.trang.druid; import com.github.trang.druid.mapper.CityMapper; +import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; @@ -12,6 +13,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; * @author trang */ @SpringBootApplication +@MapperScan("com.github.trang.druid.mapper") public class DruidSpringBootStarterApplication implements CommandLineRunner { public static void main(String[] args) { @@ -24,7 +26,7 @@ public class DruidSpringBootStarterApplication implements CommandLineRunner { @Override public void run(String... args) throws Exception { - System.out.println(cityMapper.findByState("CA")); + System.out.println(cityMapper.findAll()); } } \ No newline at end of file diff --git a/druid-spring-boot-samples/src/main/java/com/github/trang/druid/config/DynamicDataSource.java b/druid-spring-boot-samples/src/main/java/com/github/trang/druid/config/DynamicDataSource.java new file mode 100644 index 0000000..5e51775 --- /dev/null +++ b/druid-spring-boot-samples/src/main/java/com/github/trang/druid/config/DynamicDataSource.java @@ -0,0 +1,42 @@ +package com.github.trang.druid.config; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.sql.DataSource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 动态数据源,基于简单的轮询算法 + * + * @author trang + */ +public class DynamicDataSource extends AbstractRoutingDataSource { + + private AtomicInteger count = new AtomicInteger(); + private DataSource defaultTargetDataSource; + private Map targetDataSources; + + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) { + this.defaultTargetDataSource = defaultTargetDataSource; + this.targetDataSources = targetDataSources; + } + + @Override + protected Object determineCurrentLookupKey() { + int i = count.incrementAndGet(); + List list = new ArrayList<>(targetDataSources.keySet()); + return list.get(i % list.size()); + } + + @Override + public void afterPropertiesSet() { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(new HashMap<>(targetDataSources)); + super.afterPropertiesSet(); + } + +} \ No newline at end of file diff --git a/druid-spring-boot-samples/src/main/java/com/github/trang/druid/config/SpringDataSourceConfig.java b/druid-spring-boot-samples/src/main/java/com/github/trang/druid/config/SpringDataSourceConfig.java new file mode 100644 index 0000000..37de2bb --- /dev/null +++ b/druid-spring-boot-samples/src/main/java/com/github/trang/druid/config/SpringDataSourceConfig.java @@ -0,0 +1,51 @@ +package com.github.trang.druid.config; + +import com.alibaba.druid.pool.DruidDataSource; +import com.github.trang.druid.datasource.DruidMultiDataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/** + * 多数据源配置,只在 #{@code spring.profiles.active=dynamic} 时生效 + * + * @author trang + */ +@Configuration +@Profile("dynamic") +public class SpringDataSourceConfig { + + private static final Logger log = LoggerFactory.getLogger(SpringDataSourceConfig.class); + + @Bean + @ConfigurationProperties("spring.datasource.druid.one") + public DruidDataSource firstDataSource() { + log.debug("druid master-data-source init..."); + return new DruidMultiDataSource(); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.two") + public DruidDataSource secondDataSource() { + log.debug("druid slave-data-source init..."); + return new DruidMultiDataSource(); + } + + @Bean + @Primary + public DynamicDataSource dataSource(DruidDataSource firstDataSource, DruidDataSource secondDataSource) { + Map targetDataSources = new HashMap<>(); + targetDataSources.put("one", firstDataSource); + targetDataSources.put("two", secondDataSource); + return new DynamicDataSource(firstDataSource, targetDataSources); + } + +} \ No newline at end of file diff --git a/druid-spring-boot-samples/src/main/java/com/github/trang/druid/mapper/CityMapper.java b/druid-spring-boot-samples/src/main/java/com/github/trang/druid/mapper/CityMapper.java index 5adf71c..909acb8 100644 --- a/druid-spring-boot-samples/src/main/java/com/github/trang/druid/mapper/CityMapper.java +++ b/druid-spring-boot-samples/src/main/java/com/github/trang/druid/mapper/CityMapper.java @@ -5,11 +5,13 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import java.util.List; + @Mapper public interface CityMapper { - @Select("select * from city where state = #{state}") - City findByState(@Param("state") String state); + @Select("select * from city") + List findAll(); @Select("select * from city where id = #{id}") City findById(@Param("id") Long id); diff --git a/druid-spring-boot-samples/src/main/resources/application.yml b/druid-spring-boot-samples/src/main/resources/application.yml index 357ca2f..8df1e4a 100644 --- a/druid-spring-boot-samples/src/main/resources/application.yml +++ b/druid-spring-boot-samples/src/main/resources/application.yml @@ -9,11 +9,14 @@ logging: com.github.trang: debug spring: + profiles: + active: single datasource: driver-class-name: org.h2.Driver url: jdbc:h2:file:./samples username: root password: 123456 + schema: classpath:/import.sql druid: initial-size: 1 min-idle: 1 @@ -55,10 +58,9 @@ spring: patterns: - com.github.trang.* web-stat: - enabled: true + enabled: false stat-view-servlet: enabled: true - schema: classpath:import.sql transaction: rollback-on-commit-failure: true aop: @@ -66,4 +68,31 @@ spring: proxy-target-class: true mybatis: - type-aliases-package: com.github.trang.druid.model \ No newline at end of file + type-aliases-package: com.github.trang.druid.model + +--- + +debug: true + +spring: + profiles: single + +--- + +debug: true + +spring: + profiles: dynamic + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration + datasource: + druid: + one: + name: one + max-active: 50 + max-open-prepared-statements: 50 + two: + name: two + max-active: 25 + max-open-prepared-statements: 25 diff --git a/druid-spring-boot-samples/src/main/resources/import.sql b/druid-spring-boot-samples/src/main/resources/import.sql index 8c7c7d6..73548bc 100644 --- a/druid-spring-boot-samples/src/main/resources/import.sql +++ b/druid-spring-boot-samples/src/main/resources/import.sql @@ -1,10 +1,14 @@ DROP TABLE IF EXISTS city; CREATE TABLE city ( - id INT(6) PRIMARY KEY AUTO_INCREMENT, + id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(64), - state VARCHAR(32), - country VARCHAR(32) + state VARCHAR(16), + country VARCHAR(16) ); +INSERT INTO city (NAME, state, country) VALUES ('Beijing', 'BJ', 'CN'); +INSERT INTO city (NAME, state, country) VALUES ('Shanghai', 'SH', 'CN'); +INSERT INTO city (NAME, state, country) VALUES ('Guangzhou', 'GZ', 'CN'); +INSERT INTO city (NAME, state, country) VALUES ('Shenzhen', 'SZ', 'CN'); INSERT INTO city (NAME, state, country) VALUES ('San Francisco', 'CA', 'US'); \ No newline at end of file diff --git a/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/BaseTest.java b/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/BaseTest.java new file mode 100644 index 0000000..e54510f --- /dev/null +++ b/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/BaseTest.java @@ -0,0 +1,21 @@ +package com.github.trang.druid.test; + +import com.github.trang.druid.mapper.CityMapper; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * BaseTest + * + * @author trang + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public abstract class BaseTest { + + @Autowired(required = false) + protected CityMapper cityMapper; + +} \ No newline at end of file diff --git a/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/DynamicDataSourceTests.java b/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/DynamicDataSourceTests.java new file mode 100644 index 0000000..95fa223 --- /dev/null +++ b/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/DynamicDataSourceTests.java @@ -0,0 +1,58 @@ +package com.github.trang.druid.test; + +import com.alibaba.druid.pool.DruidDataSource; +import com.github.trang.druid.config.DynamicDataSource; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; + +import static org.junit.Assert.assertEquals; + +/** + * 动态数据源测试 + * + * @author trang + */ +@ActiveProfiles("dynamic") +public class DynamicDataSourceTests extends BaseTest { + + @Autowired + private DruidDataSource firstDataSource; + @Autowired + private DruidDataSource secondDataSource; + @Autowired + private DynamicDataSource dataSource; + + @Test + public void testFirstDataSource() { + assertEquals(1, firstDataSource.getInitialSize()); + assertEquals(1, firstDataSource.getMinIdle()); + assertEquals(50, firstDataSource.getMaxActive()); + assertEquals(30000, firstDataSource.getMaxWait()); + assertEquals(60000, firstDataSource.getTimeBetweenEvictionRunsMillis()); + assertEquals(300000, firstDataSource.getMinEvictableIdleTimeMillis()); + assertEquals("SELECT 1", firstDataSource.getValidationQuery()); + assertEquals(true, firstDataSource.isTestWhileIdle()); + assertEquals(false, firstDataSource.isTestOnBorrow()); + assertEquals(false, firstDataSource.isTestOnReturn()); + assertEquals(50, firstDataSource.getMaxPoolPreparedStatementPerConnectionSize()); + assertEquals(true, firstDataSource.isUseGlobalDataSourceStat()); + } + + @Test + public void testSecondDataSource() { + assertEquals(1, secondDataSource.getInitialSize()); + assertEquals(1, secondDataSource.getMinIdle()); + assertEquals(25, secondDataSource.getMaxActive()); + assertEquals(30000, secondDataSource.getMaxWait()); + assertEquals(60000, secondDataSource.getTimeBetweenEvictionRunsMillis()); + assertEquals(300000, secondDataSource.getMinEvictableIdleTimeMillis()); + assertEquals("SELECT 1", secondDataSource.getValidationQuery()); + assertEquals(true, secondDataSource.isTestWhileIdle()); + assertEquals(false, secondDataSource.isTestOnBorrow()); + assertEquals(false, secondDataSource.isTestOnReturn()); + assertEquals(25, secondDataSource.getMaxPoolPreparedStatementPerConnectionSize()); + assertEquals(true, secondDataSource.isUseGlobalDataSourceStat()); + } + +} \ No newline at end of file diff --git a/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/MyBatisTests.java b/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/MyBatisTests.java new file mode 100644 index 0000000..c2c0f85 --- /dev/null +++ b/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/MyBatisTests.java @@ -0,0 +1,29 @@ +package com.github.trang.druid.test; + +import com.github.trang.druid.model.City; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +/** + * MyBatis 测试 + * + * @author trang + */ +public class MyBatisTests extends BaseTest { + + @Test + public void testOne() { + City city = cityMapper.findById(1L); + System.out.println(city); + } + + @Test + public void testAll() { + List cities = cityMapper.findAll(); + System.out.println(cities); + Assert.assertEquals(5, cities.size()); + } + +} \ No newline at end of file diff --git a/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/DruidTests.java b/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/SingleDataSourceTests.java similarity index 61% rename from druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/DruidTests.java rename to druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/SingleDataSourceTests.java index de9a92d..c9dec7a 100644 --- a/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/DruidTests.java +++ b/druid-spring-boot-samples/src/test/java/com/github/trang/druid/test/SingleDataSourceTests.java @@ -1,33 +1,25 @@ package com.github.trang.druid.test; import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.wall.WallFilter; -import com.github.trang.druid.mapper.CityMapper; import org.junit.Test; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.ActiveProfiles; import static org.junit.Assert.assertEquals; /** + * 单数据源测试 + * * @author trang */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.NONE) -public class DruidTests { +@ActiveProfiles("single") +public class SingleDataSourceTests extends BaseTest { @Autowired private DruidDataSource dataSource; - @Autowired - private WallFilter wallFilter; - @Autowired - private CityMapper cityMapper; @Test - public void testDataSource() { + public void testSingleDataSource() { assertEquals(dataSource.getInitialSize(), 1); assertEquals(dataSource.getMinIdle(), 1); assertEquals(dataSource.getMaxActive(), 10); @@ -42,9 +34,4 @@ public class DruidTests { assertEquals(dataSource.isUseGlobalDataSourceStat(), true); } - @Test - public void testMybatis() { - System.out.println(cityMapper.findById(1L)); - } - } \ No newline at end of file diff --git a/druid-spring-boot-starter/src/main/resources/META-INF/spring.provides b/druid-spring-boot-starter/src/main/resources/META-INF/spring.provides index cab2686..6e9da6a 100644 --- a/druid-spring-boot-starter/src/main/resources/META-INF/spring.provides +++ b/druid-spring-boot-starter/src/main/resources/META-INF/spring.provides @@ -1 +1 @@ -provides: spring-boot-autoconfigure,druid,druid-spring-boot-autoconfigure \ No newline at end of file +provides: spring-boot-autoconfigure,spring-jdbc,druid,druid-spring-boot-autoconfigure \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2d367b6..dafa099 100644 --- a/pom.xml +++ b/pom.xml @@ -45,10 +45,11 @@ UTF-8 1.8 + 4.3.9.RELEASE 1.5.4.RELEASE 1.7.25 3.1.0 - 1.1.0 + 1.1.1 3.0.1 3.0.0 @@ -79,6 +80,11 @@ spring-boot-configuration-processor ${spring-boot.version} + + org.springframework + spring-jdbc + ${spring.version} + javax.servlet javax.servlet-api @@ -126,6 +132,7 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + true true @@ -141,11 +148,13 @@ org.eluder.coveralls coveralls-maven-plugin ${coveralls-maven-plugin.version} + true org.codehaus.mojo cobertura-maven-plugin ${cobertura-maven-plugin.version} + true xml 256m @@ -165,6 +174,7 @@ maven-javadoc-plugin ${maven-javadoc-plugin.version} + true package @@ -181,6 +191,7 @@ maven-gpg-plugin ${maven-gpg-plugin.version} + true verify @@ -193,6 +204,7 @@ maven-deploy-plugin ${maven-deploy-plugin.version} + true true @@ -200,6 +212,7 @@ maven-release-plugin ${maven-release-plugin.version} + true true true @@ -219,10 +232,12 @@ maven-dependency-plugin ${maven-dependency-plugin.version} + true maven-enforcer-plugin ${maven-enforcer-plugin.version} + true enforce-maven @@ -242,10 +257,12 @@ maven-clean-plugin ${maven-clean-plugin.version} + true maven-resources-plugin ${maven-resources-plugin.version} + true ${project.build.sourceEncoding} @@ -253,6 +270,7 @@ maven-compiler-plugin ${maven-compiler-plugin.version} + true ${project.build.sourceEncoding} ${java.version} @@ -262,6 +280,7 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + true **/com/github/trang/**/test/junit/**Test.java @@ -271,10 +290,12 @@ maven-jar-plugin ${maven-jar-plugin.version} + true maven-source-plugin ${maven-source-plugin.version} + true package @@ -287,6 +308,7 @@ maven-install-plugin ${maven-install-plugin.version} + true