【Druid源码阅读】9. 过滤器

11 篇文章 0 订阅
本文详细介绍了 Druid 数据源中过滤器的工作原理,包括设置过滤器、加载过滤器配置、过滤器责任链的构造,以 stat 过滤器为例展示了过滤器如何在获取连接时发挥作用。通过配置过滤器,可以实现对数据库连接池的监控和性能优化。
摘要由CSDN通过智能技术生成

在前面几天获取连接的代码中,经常会看到 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

今天先看到这里,明天看过滤器的作用和如何发挥作用。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值