Druid的监控统计功能是通过filter-chain扩展实现,filter给开发者带来了很好的扩展性,使得我们可以自定义实现一些功能。
在spring boot环境下,除了系统环境变量及jdbc url方式配置filter,大多常用的有以下几种:
例如:
@Component
public class DemoFilter extends FilterEventAdapter {
@Override
public void init(DataSourceProxy dataSource) {
super.init(dataSource);
}
}
那这个自定义的filter就自动被注入到DruidDataSource 的filter集合中,要使用这种,实例化DruidDataSource的方式还得借助druid-spring-boot-starter,
@Configuration
public class DataSourceConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
@Bean(name = "dataSource")
@ConfigurationProperties("spring.datasource.sdcuike")
public DataSource dataSource() {
logger.info("Init DruidDataSource");
return DruidDataSourceBuilder.create().build();
}
}
为了和spring boot 融合,用了装饰者模式com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper。使得项目中的所有filter实例自动注入:
@Autowired(required = false)
public void autoAddFilters(List filters){
super.filters.addAll(filters);
}
上述代码使用了spring 的setter注入方式。
spring:
datasource:
sdcuike:
filters: stat,demo3Filter
其扩展思想来源于SPI,其实dubbo的扩展机制也是起源于SPI,在META-INF/druid-filter.properties配置的类会在合适的条件加载类型,并根据配置的别名来决定是不是实例化。
其加载的详细部分代码在com.alibaba.druid.filter.FilterManager#loadFilterConfig(java.util.Properties, java.lang.ClassLoader)
private static void loadFilterConfig(Properties filterProperties, ClassLoader classLoader) throws IOException {
if (classLoader == null) {
return;
}
for (Enumeration e = classLoader.getResources("META-INF/druid-filter.properties"); e.hasMoreElements();) {
URL url = e.nextElement();
Properties property = new Properties();
InputStream is = null;
try {
is = url.openStream();
property.load(is);
} finally {
JdbcUtils.close(is);
}
filterProperties.putAll(property);
}
}
别名注入的方式也是通过spring 的setter方式注入的:
com.alibaba.druid.pool.DruidAbstractDataSource#setFilters
public void setFilters(String filters) throws SQLException {
我们需要在文件/src/main/resources/META-INF/services/com.alibaba.druid.filter.Filter,按照java标准的SPI规范配置扩展的filter类型,如com.sdcuike.springboot.druid.filter.Demo2Filter,并且在类上加上注解@AutoLoad即可。
@AutoLoad
public class Demo2Filter extends FilterEventAdapter {
@Override
public void init(DataSourceProxy dataSource) {
super.init(dataSource);
}
}
其实现代码在com.alibaba.druid.pool.DruidDataSource#initFromSPIServiceLoader
/**
* load filters from SPI ServiceLoader
*
* @see ServiceLoader
*/
private void initFromSPIServiceLoader() {
if (loadSpifilterSkip) {
return;
}
if (autoFilters == null) {
List filters = new ArrayList();
ServiceLoader autoFilterLoader = ServiceLoader.load(Filter.class);
for (Filter filter : autoFilterLoader) {
AutoLoad autoLoad = filter.getClass().getAnnotation(AutoLoad.class);
if (autoLoad != null && autoLoad.value()) {
filters.add(filter);
}
}
autoFilters = filters;
}
for (Filter filter : autoFilters) {
if (LOG.isInfoEnabled()) {
LOG.info("load filter from spi :" + filter.getClass().getName());
}
addFilter(filter);
}
}
以上介绍的三种配置方式,各有优劣。
https://github.com/sdcuike/spring-boot2-practice/tree/druid_filter/src/main/java/com/sdcuike/springboot/druid/filter