1. 程式人生 > >java動態代理之CGLIB實現

java動態代理之CGLIB實現

ssl return 其他 ase ger pac 父類 linked nic

動態代理(CGlib 與連接池的案例)

Cglib代理: 針對類來實現代理,對指定目標 產生一個子類 通過方法攔截技術攔截所有父類方法的調用。
我們要使用cglib代理必須引入 cglib的jar包

 <dependencies>
        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.8</version>
        </dependency>
    </dependencies>

定義一個UserService的普通類

public class UserService {

    public void add(){
        System.out.println("添加用戶");
    }

    public void findUser(){
        System.out.println("查找用戶");
    }
}


定義一個方法攔截器,類似於JDK中的InvocationHandler

/*private Object target;
    public UserServiceInterceptor(Object target){
        this.target = target;
    }
*/ /** * 攔截方法 * @param proxy 代理對象 * @param method 目標對象正在調用的方法 * @param args 目標對象方法所需的參數 * @param methodProxy 目標對象方法的代理實例 * @return * @throws Throwable */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws
Throwable { System.out.println("before..."); //Object returnVal = method.invoke(target, args); //使用methodProxy來回調父類的方法,第一個參數是代理對象,第二個參數是目標方法所需的參數 Object returnVal = methodProxy.invokeSuper(proxy, args); System.out.println("after..."); return returnVal; }

2.1連接池

定義一個DBUtil類:

public class DBUtil {

    private static String driver = "com.mysql.jdbc.Driver";
    private static String url = "jdbc:mysql://localhost:3306/homework?useSSL=true&useUnicode=true&characterEncoding=utf-8";
    private static String user = "root";
    private static String password = "root";

    static {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static Connection getConnection(){
        try {
            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

定義一個連接池的類:

public class ConnectionPool {

    /**
     * 連接池集合
     */
    private static LinkedList<Connection> pool = new LinkedList<>();

    /**
     *
     * @param initSize 初始化連接池的大小
     */
    public ConnectionPool(int initSize){
        for(int i= 0; i<initSize; i++){
            //從數據庫獲取一個連接對象
            Connection conn = DBUtil.getConnection();
            //將這個conn對象進行一層代理
            conn = proxyConnection(conn);
            //放入連接池,返給池中的是一個代理的對象
            pool.add(conn);
        }
    }

    /**
     * 給Connection對象創建代理實例
     * @return
     */
    private Connection proxyConnection(Connection conn){
        Object proxy = Proxy.newProxyInstance(conn.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //如果當前調用的是close方法,則將連接放回連接池
                if("close".equals(method.getName())){
                    //註意:pool放入的是代理對象,不是conn這個目標對象
                    pool.addLast((Connection) proxy);
                    return null;
                }
                //除close方法以外的其他方法正常調用
                return method.invoke(conn, args);
            }
        });
        return (Connection) proxy;
    }

    /**
     * 提供一個從連接池獲取連接的方法
     * @return
     */
    public Connection getConnection(){
        if(pool.size() > 0){
            return pool.removeFirst();
        }
        throw new RuntimeException("連接池無可用連接");
    }

    /**
     * 查看連接池大小的方法
     * @return
     */
    public int size(){
        return pool.size();
    }
}

測試Main方法

public class Main {

    public static void main(String[] args) throws SQLException {
        ConnectionPool pool = new ConnectionPool(10);
        System.out.println("當前連接池大小: "+pool.size());
        Connection conn = pool.getConnection();
        System.out.println("當前連接池大小: "+pool.size());
        conn.close();
        System.out.println("當前連接池大小: "+pool.size());
    }
}

運行結果:

技術分享圖片


2.2利用動態代理實現數據庫連接池的操作

package mybatis.tools;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

/**
 * 自定義連接池, 管理連接
 * 代碼實現:
 1.  MyPool.java  連接池類,
 2.  指定全局參數:  初始化數目、最大連接數、當前連接、   連接池集合
 3.  構造函數:循環創建3個連接
 4.  寫一個創建連接的方法
 5.  獲取連接
 ------>  判斷: 池中有連接, 直接拿
 ------>                池中沒有連接,
 ------>                 判斷,是否達到最大連接數; 達到,拋出異常;沒有達到最大連接數,
 創建新的連接
 6. 釋放連接
 ------->  連接放回集合中(..)
 *
 */

/**
 * 描述:
 * 連接池
 *
 * @author lance
 * @create 2018-10-15 14:58
 */
public class MyPool {
    // 初始化連接數目
    private int init_count = 3;
    // 最大連接數
    private int max_count = 6;
    // 記錄當前使用連接數
    private int current_count = 0;
    // 連接池 (存放所有的初始化連接)
    private LinkedList<Connection> pool = new LinkedList<Connection>();


    //1.  構造函數中,初始化連接放入連接池
    public MyPool() {
        // 初始化連接
        for (int i=0; i<init_count; i++){
            // 記錄當前連接數目
            current_count++;
            // 創建原始的連接對象
            Connection con = createConnection();
            // 把連接加入連接池
            pool.addLast(con);
        }
    }

    /**
     * 2. 創建一個新的連接的方法
     */
    private Connection createConnection(){
        try {
            Class.forName("com.mysql.jdbc.Driver");
            // 原始的目標對象
            final Connection con = DriverManager.getConnection("jdbc:mysql:///mydb", "root", "root");

            /**********對con對象代理**************/

            // 對con創建其代理對象
            Connection proxy = (Connection) Proxy.newProxyInstance(
                    // 類加載器
                    con.getClass().getClassLoader(),
                    // 當目標對象是一個具體的類的時候
                    //con.getClass().getInterfaces(),
                    // 目標對象實現的接口
                    new Class[]{Connection.class},
                    // 當調用con對象方法的時候, 自動觸發事務處理器
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            // 方法返回值
                            Object result = null;
                            // 當前執行的方法的方法名
                            String methodName = method.getName();

                            // 判斷當執行了close方法的時候,把連接放入連接池
                            if ("close".equals(methodName)) {
                                System.out.println("begin:當前執行close方法開始!");
                                // 連接放入連接池 (判斷..)
                                pool.addLast(con);
                                System.out.println("end: 當前連接已經放入連接池了!");
                            } else {
                                // 調用目標對象方法
                                result = method.invoke(con, args);
                            }
                            return result;
                        }
                    }
            );
            return proxy;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *   3. 獲取連接
     */
    public Connection getConnection(){

        // 3.1 判斷連接池中是否有連接, 如果有連接,就直接從連接池取出
        if (pool.size() > 0){
            return pool.removeFirst();
        }

        // 3.2 連接池中沒有連接: 判斷,如果沒有達到最大連接數,創建;
        if (current_count < max_count) {
            // 記錄當前使用的連接數
            current_count++;
            // 創建連接
            return createConnection();
        }

        // 3.3 如果當前已經達到最大連接數,拋出異常
        throw new RuntimeException("當前連接已經達到最大連接數目 !");
    }


    /**
     *  4. 釋放連接
     */
    public void realeaseConnection(Connection con) {
        // 4.1 判斷: 池的數目如果小於初始化連接,就放入池中
        if (pool.size() < init_count){
            pool.addLast(con);
        } else {
            try {
                // 4.2 關閉
                current_count--;
                con.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws SQLException {
        MyPool pool = new MyPool();
        System.out.println("當前連接: " + pool.current_count);

        // 使用連接
        //pool.getConnection();
        pool.getConnection();
        Connection con4 = pool.getConnection();
        Connection con3 = pool.getConnection();
        Connection con2 = pool.getConnection();
        Connection con1 = pool.getConnection();

        con1.close();
        con2.close();
        con3.close();
        // 再獲取
        //pool.getConnection();
        //pool.getConnection();


        System.out.println("連接池:" + pool.pool.size());
        System.out.println("當前連接: " + pool.current_count);
    }

}

java動態代理之CGLIB實現