5、动态代理

1、介绍

切面编程 AOP
业务对象的的每个方法都要进行性能统计,存在大量重复的代码
代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的
Proxy 方法: public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

动态代理的优点

  • 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接口本身做代理
  • 可以为被代理对象的所有方法做代理
  • 可以在不改变方法源码的情况下,实现对方法功能的增强
  • 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率

关键步骤

  • 必须有接口,实现类要实现接口(代理通常是基于接口实现的)
  • 创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象

动态代理:目标对象 + 代理逻辑 = 代理对象

  • JDK:代理对象和目标对象出于同一个接口之下
  • CGLib:代理对象是目标对象的子对象
    image
// 接口
public interface UserService {
    String login(String loginName, String password);

    void selectUsers();

    boolean deleteUsers();
}
// 实现类
public class UserServiceImpl implements UserService {
    @Override
    public String login(String loginName, String password) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if ("admin".equals(loginName) && "123".equals(password)) return "success";
        return "fail";
    }

    @Override
    public void selectUsers() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("账号: admin, 密码: 123");
    }

    @Override
    public boolean deleteUsers() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
}

2、JDK 动态代理

JDK 动态代理,依赖接口

// 代理实现统计方法耗时
public class ProxyUtil {
    // 泛型方法: 为任何对象做代理
    public static <T> T getProxy(T obj) {
        return (T) Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),  // 类加载器, 负责加载代理类到内存中使用
                obj.getClass().getInterfaces(),   // 获取代理类实现的全部接口
                new InvocationHandler() {         // 代理逻辑
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 参数一: 代理对象本身, 一般不管
                        // 参数二: 正在被代理的方法
                        // 参数三: 被代理方法传入的参数
                        long startTime = System.currentTimeMillis();
                        Object result = method.invoke(obj, args); // 业务代码
                        long endTime = System.currentTimeMillis();
                        System.out.println(method.getName() + " 耗时: " + (endTime - startTime) / 1000.0);
                        return result;
                    }
                });
    }
}
// 测试
public class Test {
    public static void main(String[] args) {
        UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
        System.out.println(userService.login("admin", "123"));
        userService.selectUsers();
        userService.deleteUsers();
    }
}

// login 耗时: 1.014
// success

// 账号: admin, 密码: 123
// selectUsers 耗时: 1.013

// deleteUsers 耗时: 1.012

3、CGLib 动态代理

CGLib 动态代理,不依赖接口,但是不能对声明为 final 的类或者方法进行代理,因为 CGLib 原理是动态生成被代理类的子类

public class CGLibProxyUtil {
    public static <T> T getProxy(T obj) {
        // 1. 创建 Enhancer 对象, 类似 JDK 代理中的 Proxy 类
        Enhancer enhancer = new Enhancer();
        // 2. 设置父类的字节码对象
        enhancer.setSuperclass(obj.getClass());
        // 3. 设置回掉函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                /*
                    o             代理对象
                    method        真实对象中的方法的 Method 实例
                    args          实际参数
                    methodProxy   代理对象中的方法的 method 实例
                */
                long startTime = System.currentTimeMillis();
                Object result = method.invoke(obj, args); // 业务代码
                long endTime = System.currentTimeMillis();
                System.out.println(method.getName() + " 耗时: " + (endTime - startTime) / 1000.0);
                return result;
            }
        });
        // 4. 创建代理对象(属于目标对象类的子类的对象)
        return (T) enhancer.create();
    }
}

4、更多

AOP 如何判定是否可以使用 JDK 动态代理
有点深度的聊聊 JDK 动态代理

posted @ 2023-06-11 16:12  lidongdongdong~  阅读(17)  评论(0编辑  收藏  举报