zoukankan      html  css  js  c++  java
  • Hikaricp源码解读(4)——Proxy*代理类介绍

    4、Proxy*代理类介绍

    本文以v2.7.2源码为主进行分析

    HikariCP不同于一般连接池如proxool、c3p0等使用动态代理实现连接的操作转移,而是通过Javassist结合部分自定义代码实现对应接口实现的方式,减少了代理连接建立的代价,以下是HikariCP中的主要代理相关类:

    • ProxyConnection.java
    • ProxyStatement.java
    • ProxyPreparedStatement.java
    • ProxyCallableStatement.java
    • ProxyResultSet.java
    • ProxyFactory.java(工厂类)
    • JavassistProxyFactory.java(代码重构)

    以上代码通过Javassist进行代码重构建之后生成实际使用的对应接口代理类:

    • HikariProxyConnection.java
    • HikariProxyStatement.java
    • HikariProxyPreparedStatement.java
    • HikariProxyCallableStatement.java
    • HikariProxyResultSet.java

    我们从JavassistProxyFactory.java入手,其核心代码如下:

    try {
       // Cast is not needed for these
       String methodBody = "{ try { return delegate.method($$); } catch (SQLException e) { throw checkException(e); } }";
       generateProxyClass(Connection.class, ProxyConnection.class.getName(), methodBody);
       generateProxyClass(Statement.class, ProxyStatement.class.getName(), methodBody);
       generateProxyClass(ResultSet.class, ProxyResultSet.class.getName(), methodBody);
    
       // For these we have to cast the delegate
       methodBody = "{ try { return ((cast) delegate).method($$); } catch (SQLException e) { throw checkException(e); } }";
       generateProxyClass(PreparedStatement.class, ProxyPreparedStatement.class.getName(), methodBody);
       generateProxyClass(CallableStatement.class, ProxyCallableStatement.class.getName(), methodBody);
    
       modifyProxyFactory();
    }
    catch (Exception e) {
       throw new RuntimeException(e);
    }
    

    由上可以看出,核心构造方法为

    • generateProxyClass
    • modifyProxyFactory

    其中generateProxyClass负责生成实际使用的代理类字节码,modifyProxyFactory对应修改工厂类中的代理类获取方法。

    generateProxyClass

    Proxy*.java中定义的方法,在继承类中不进行overwrite,其他方法使用delegate执行对应的方法(methodBody中的method替换成对应方法的方法名):

    // 区分是否抛出SQLException异常
    if (isThrowsSqlException(intfMethod)) {
       modifiedBody = modifiedBody.replace("method", method.getName());
    }
    else {
       modifiedBody = "{ return ((cast) delegate).method($$); }".replace("method", method.getName()).replace("cast", primaryInterface.getName());
    }
    

    modifyProxyFactory

    直接替换Proxy.java为HikariProxy.java

    switch (method.getName()) {
    case "getProxyConnection":
       method.setBody("{return new " + packageName + ".HikariProxyConnection($$);}");
       break;
    case "getProxyStatement":
       method.setBody("{return new " + packageName + ".HikariProxyStatement($$);}");
       break;
    case "getProxyPreparedStatement":
       method.setBody("{return new " + packageName + ".HikariProxyPreparedStatement($$);}");
       break;
    case "getProxyCallableStatement":
       method.setBody("{return new " + packageName + ".HikariProxyCallableStatement($$);}");
       break;
    case "getProxyResultSet":
       method.setBody("{return new " + packageName + ".HikariProxyResultSet($$);}");
       break;
    default:
       // unhandled method
       break;
    }
    

    由此可知,Proxy*.java中的实现是代理类的核心代码实现。以下通过ProxyConnection.java、ProxyStatement.java、ProxyResultSet.java三个类展开介绍:

    ProxyConnection.java

    本类中代码大致分为三部分:以close为关键的代码overwrite、独立实现的ClosedConnection(动态代理实现的唯一实例化对象)、Hikari 连接池的自定义函数逻辑。

    • 自定义函数

    主要实现对打开的statement的缓存管理和连接标识:

    // 用于标识连接被访问或存在可提交数据
    final void markCommitStateDirty()
    {
       if (isAutoCommit) {
          lastAccess = currentTime();
       }
       else {
          isCommitStateDirty = true;
       }
    }
    
    // 缓存statement
    private synchronized <T extends Statement> T trackStatement(final T statement)
    {
       openStatements.add(statement);
    
       return statement;
    }
    
    // 移出statement缓存
    final synchronized void untrackStatement(final Statement statement)
    {
       openStatements.remove(statement);
    }
    
    // 关闭全部已打开的statement(只在close方法中调用)
    @SuppressWarnings("EmptyTryBlock")
    private synchronized void closeStatements()
    {
       final int size = openStatements.size();
       if (size > 0) {
          for (int i = 0; i < size && delegate != ClosedConnection.CLOSED_CONNECTION; i++) {
             try (Statement ignored = openStatements.get(i)) {
                // automatic resource cleanup
             }
             catch (SQLException e) {
                LOGGER.warn("{} - Connection {} marked as broken because of an exception closing open statements during Connection.close()",
                            poolEntry.getPoolName(), delegate);
                leakTask.cancel();
                poolEntry.evict("(exception closing Statements during Connection.close())");
                delegate = ClosedConnection.CLOSED_CONNECTION;
             }
          }
    
          openStatements.clear();
       }
    }
    
    • ClosedConnection

    java动态代理生成的全局唯一变量,作为已关闭连接的代理引用,为连接关闭后外界代理连接的引用调用提供处理,同时唯一类减少了内存消耗和比对代价。代码如下:

    private static final class ClosedConnection
    {
       static final Connection CLOSED_CONNECTION = getClosedConnection();
    
       private static Connection getClosedConnection()
       {
          InvocationHandler handler = (proxy, method, args) -> {
             // 只保留3个方法的快速返回,其他均抛出异常。
             final String methodName = method.getName();
             if ("abort".equals(methodName)) {
                return Void.TYPE;
             }
             else if ("isValid".equals(methodName)) {
                return Boolean.FALSE;
             }
             else if ("toString".equals(methodName)) {
                return ClosedConnection.class.getCanonicalName();
             }
    
             throw new SQLException("Connection is closed");
          };
    
          return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[] { Connection.class }, handler);
       }
    }
    
    • overwrite

    主要修改statement获取、close、状态修改相关的方法和数据变更:

    // ************statement获取************
    public PreparedStatement prepareStatement(String sql) throws SQLException
    {
       // 先缓存statement,然后通过工厂方法生成代理类。
       return ProxyFactory.getProxyPreparedStatement(this, trackStatement(delegate.prepareStatement(sql)));
    }
    
    // 其他statement获取类似
    
    // ***********状态修改相关***************
    public void setTransactionIsolation(int level) throws SQLException
    {
       // 设置状态
       delegate.setTransactionIsolation(level);
       // 记录变更值
       transactionIsolation = level;
       // 记录变更位
       dirtyBits |= DIRTY_BIT_ISOLATION;
    }
    
    //setCatalog、setReadOnly、setAutoCommit、
    //setNetworkTimeout、setSchema五个方法类似
    
    // **************close*****************
    public final void close() throws SQLException
    {
       // 关闭全部打开的连接(有可能触发连接关闭,所以放到判断外)
       closeStatements();
    
       if (delegate != ClosedConnection.CLOSED_CONNECTION) {
          ……
          try {
    	     // 如果存在提交数据且不是自动提交,则回滚重置数据
             if (isCommitStateDirty && !isAutoCommit) {
                delegate.rollback();
                lastAccess = currentTime();
                LOGGER.debug("{} - Executed rollback on connection {} due to dirty commit state on close().", poolEntry.getPoolName(), delegate);
             }
    
    		 // 如果存在设置变更位,则进行重置
             if (dirtyBits != 0) {
                poolEntry.resetConnectionState(this, dirtyBits);
                lastAccess = currentTime();
             }
    
    		 // 重置
             delegate.clearWarnings();
          }
          catch (SQLException e) {
             ……
          }
          finally {
    	     // 代理置为关闭连接
             delegate = ClosedConnection.CLOSED_CONNECTION;
    		 // 回收PoolEntry(若PoolEntry已经被关闭和remove,则由下个接收线程丢弃或者threadlocal中无引用被回收)
             poolEntry.recycle(lastAccess);
          }
       }
    }
    
    // ***********commit
    ollback***********
    public void commit
    ollback() throws SQLException
    {
       // 调用方法
       delegate.commit
    ollback();
       isCommitStateDirty = false;
       // 更新时间
       lastAccess = currentTime();
    }
    
    

    ProxyStatement.java

    由overwrite构成,主要包含执行方法、close两类。

    // **************执行方法****************
    public ResultSet executeQuery(String sql) throws SQLException
    {
       // 记录执行
       connection.markCommitStateDirty();
       // 调用方法
       ResultSet resultSet = delegate.executeQuery(sql);
       // 生成代理ResultSet
       return ProxyFactory.getProxyResultSet(connection, this, resultSet);
    }
    
    // **************close****************
    public final void close() throws SQLException
    {
       // 放置重复关闭
       synchronized (this) {
          if (isClosed) {
             return;
          }
    
          isClosed = true;
       }
    
       // 移出缓存
       connection.untrackStatement(delegate);
    
       try {
          // 关闭代理
          delegate.close();
       }
       catch (SQLException e) {
          throw connection.checkException(e);
       }
    }
    

    ProxyResultSet.java

    此类代理最简单,就只是分别为updateRow、insertRow、deleteRow增加了执行记录connection.markCommitStateDirty(),示例如下:

    public void updateRow() throws SQLException
    {
       connection.markCommitStateDirty();
       delegate.updateRow();
    }
    

    附录:Javassist更多介绍

  • 相关阅读:
    推荐系统之推荐系统的分类,即分析框架
    问题解决——OpenGL超级宝典 关于gltDrawTorus的错误解决
    SICP 习题 (1.10)解题总结
    [置顶] android 自定义圆角ImageView以及锯齿的处理
    Codeforces Round #199 (Div. 2)
    数组——约瑟夫问题
    素数距离问题_ny_24.java
    [置顶] Guava学习之Splitter
    植物-常见植物:地黄、熟地黄
    植物-常见植物:仙人掌
  • 原文地址:https://www.cnblogs.com/taisenki/p/7716724.html
Copyright © 2011-2022 走看看