Day03 Druid 中过滤器链的装载和使用

一、过滤器的装载

Druid 中过滤器的管理工作,主要是在 FilterManager 类中实现。其中 FilterManager#loadFilter() 负责加载 filter 并放入 filter集合中。

public static void loadFilter(List<Filter> filters, String filterName) throws SQLException {
  if (filterName.length() == 0) {
      return;
  }

  // 读取 druid-filter.properties 文件,获取 filterName 对应的过滤器类的名称
  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;
  }

  // 已存在 filterName 对应的过滤器,直接返回
  if (existsFilter(filters, filterName)) {
      return;
  }

  // 加载 filterName 对应的过滤器
  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);
  }
}

其中 getFilter() 方法 会从初始化好的 ConcurrentHashMap——aliasMap 中获取指定的 filter,而 aliasMap 则根据 druid-filter.properties 文件中配的 filter 来初始化。

在这里插入图片描述

二、过滤器装载的时机

1. 初始化时——initFromSPIServiceLoader();
    private void initFromSPIServiceLoader() {
        // 是否跳过装载filter
        if (loadSpifilterSkip) {
            return;
        }

        if (autoFilters == null) {
            List<Filter> filters = new ArrayList<Filter>();
            ServiceLoader<Filter> 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);
        }
    }
2. 获取数据源时
    @Override
    public Connection connect(String url, Properties info) throws SQLException {
        if (!acceptsURL(url)) {
            return null;
        }

        connectCount.incrementAndGet();

        // 获取数据源时,会加载 filter: 
        // FilterManager.loadFilter(config.getFilters(), filterItem);
        DataSourceProxyImpl dataSource = getDataSource(url, info);

        return dataSource.connect(info);
    }

三、filterChain 的使用

从 Druid 连接池获取连接的时候便会使用 nextFilter() 方法从装载 filter 的集合中拿到一个 filter, 并执行这个 filter 的 dataSource_getConnection 方法,将获取到的 DruidPooledConnection 对象依次返回到前面的 filter。这里是一个递归调用,层层下沉,使得可以依次执行 filterchain 的下一个filter,当拿到 DruidPooledConnection 对象,并层层返回时,有一个好处是:返回时每经过一个 filter ,都可以执行 该 filter 的 if (conn != null) {} 里的特殊逻辑。

	@Override
    public DruidPooledConnection dataSource_connect(DruidDataSource dataSource, long maxWaitMillis) throws SQLException {
        if (this.pos < filterSize) {
            DruidPooledConnection conn = nextFilter().dataSource_getConnection(this, dataSource, maxWaitMillis);
            return conn;
        }

        return dataSource.getConnectionDirect(maxWaitMillis);
    }

比如StatFilter#dataSource_connect():

    @Override
    public DruidPooledConnection dataSource_getConnection(FilterChain chain, DruidDataSource dataSource, long maxWaitMillis) throws SQLException {
        // 将调用关系传递到filterchain的下一个filter
        DruidPooledConnection conn = chain.dataSource_connect(dataSource, maxWaitMillis);

        if (conn != null) {
            conn.setConnectedTimeNano();

            StatFilterContext.getInstance().pool_connection_open();
        }

        return conn;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值