在前面几天获取连接的代码中,经常会看到 filters 这个属性,今天一起来看下过滤器是如何工作的。
找一个单侧,把里面连接串和用户名密码改成自己的:
src/test/java/com/alibaba/druid/pool/Case0.java
设置 stat 过滤器
里面有一行代码,就是设置过滤器的:
dataSource.setFilters("stat");
public void setFilters(String filters) throws SQLException {
// 如果以感叹号 ! 开头,先清除之前设置的所有过滤器(前提是设置了 clearFiltersEnable为 true)
if (filters != null && filters.startsWith("!")) {
filters = filters.substring(1);
this.clearFilters();
}
// 添加过滤器
this.addFilters(filters);
}
public void addFilters(String filters) throws SQLException {
// 为null或为空字符串,直接返回,不再往下执行
if (filters == null || filters.length() == 0) {
return;
}
// 使用逗号分隔为数组,即配置多个过滤器可以使用逗号分隔
String[] filterArray = filters.split("\\,");
for (String item : filterArray) {
// 加载过滤器,并保存到 filters 列表中
FilterManager.loadFilter(this.filters, item.trim());
}
}
FilterManager 类中有静态代码块,主要逻辑是把过滤器保存到 aliasMap 中:
static {
try {
Properties filterProperties = loadFilterConfig();
for (Map.Entry<Object, Object> entry : filterProperties.entrySet()) {
String key = (String) entry.getKey();
if (key.startsWith("druid.filters.")) {
String name = key.substring("druid.filters.".length());
aliasMap.put(name, (String) entry.getValue());
}
}
} catch (Throwable e) {
LOG.error("load filter config error", e);
}
}
public static Properties loadFilterConfig() throws IOException {
Properties filterProperties = new Properties();
// 真正能够加载配置的代码,因为是在当前项目 classpath 下文件中有13项配置,详见下图
loadFilterConfig(filterProperties, ClassLoader.getSystemClassLoader());
loadFilterConfig(filterProperties, FilterManager.class.getClassLoader());
loadFilterConfig(filterProperties, Thread.currentThread().getContextClassLoader());
return filterProperties;
}
private static void loadFilterConfig(Properties filterProperties, ClassLoader classLoader) throws IOException {
if (classLoader == null) {
return;
}
for (Enumeration<URL> 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);
}
}
可以看到,从 classpath 路径下找 META-INF/druid-filter.properties 文件进行加载。
然后把 druid.filters. 截断,只保留最后一个单词作为别名,同时也是 aliasMap 的 key。
等静态代码块执行完成后,回到 FilterManager.loadFilter(this.filters, item.trim()); 这个方法中:
public static void loadFilter(List<Filter> filters, String filterName) throws SQLException {
if (filterName.length() == 0) {
return;
}
String filterClassNames = getFilter(filterName);
if (filterClassNames != null) {
for (String filterClassName : filterClassNames.split(",")) {
if (existsFilter(filters, filterClassName)) {
continue;
}
// 加载过滤器类
Class<?> filterClass = Utils.loadClass(filterClassName);
if (filterClass == null) {
LOG.error("load filter error, filter not found : " + filterClassName);
continue;
}
Filter filter;
try {
filter = (Filter) filterClass.newInstance();
} catch (ClassCastException e) {
LOG.error("load filter error.", e);
continue;
} catch (InstantiationException e) {
throw new SQLException("load managed jdbc driver event listener error. " + filterName, e);
} catch (IllegalAccessException e) {
throw new SQLException("load managed jdbc driver event listener error. " + filterName, e);
} catch (RuntimeException e) {
throw new SQLException("load managed jdbc driver event listener error. " + filterName, e);
}
filters.add(filter);
}
return;
}
if (existsFilter(filters, filterName)) {
return;
}
Class<?> filterClass = Utils.loadClass(filterName);
if (filterClass == null) {
LOG.error("load filter error, filter not found : " + filterName);
return;
}
try {
Filter filter = (Filter) filterClass.newInstance();
filters.add(filter);
} catch (Exception e) {
throw new SQLException("load managed jdbc driver event listener error. " + filterName, e);
}
}
以上把设置的过滤器初始化成对象后,放到了 filters 列表中。
因为配置的是 stat 过滤器,列表中的对象就是 com.alibaba.druid.filter.stat.StatFilter。
过滤器责任链
下面获取连接时,会用到这个过滤器列表:
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
init();
if (filters.size() > 0) {
// 如果过滤器列表数量大于0时,构造过滤器责任链
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {
return getConnectionDirect(maxWaitMillis);
}
}
进入 dataSource_connect 方法
# FilterChainImpl.java
@Override
public DruidPooledConnection dataSource_connect(DruidDataSource dataSource, long maxWaitMillis) throws SQLException {
if (this.pos < filterSize) {
// 调用下一个责任链的 dataSource_connect 方法
DruidPooledConnection conn = nextFilter().dataSource_getConnection(this, dataSource, maxWaitMillis);
return conn;
}
return dataSource.getConnectionDirect(maxWaitMillis);
}
// nextFilter 下一个过滤器
private Filter nextFilter() {
return getFilters()
.get(pos++);
}
# StatFilter.java
@Override
public DruidPooledConnection dataSource_getConnection(FilterChain chain, DruidDataSource dataSource, long maxWaitMillis) throws SQLException {
// 回调责任链的 dataSource_connect 方法
DruidPooledConnection conn = chain.dataSource_connect(dataSource, maxWaitMillis);
if (conn != null) {
conn.setConnectedTimeNano();
StatFilterContext.getInstance().pool_connection_open();
}
return conn;
}
debug可以看到连接被增强了
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1a4927d6
今天先看到这里,明天看过滤器的作用和如何发挥作用。