java动态代理之CGLIB实现

Posted

True master

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java动态代理之CGLIB实现相关的知识,希望对你有一定的参考价值。

动态代理(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实现的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点# 设计模式之代理模式:cglib动态代理

Java代理之(jdk静态代理/jdk动态代理/cglib动态代理/aop/aspectj)

AOP动态代理之CGLIB代理

java之动态代理

动态代理之 CGLIB 动态代理

动态代理之 cglib 实现