动态代理的几种方式

JDK动态代理:是Java标准库提供的一种动态代理实现方式,基于接口生成代理对象。使用Proxy类和InvocationHandler接口来创建代理对象,被代理的类必须实现接口。

CGlib动态代理:使用CGLib库实现,通过生成目标类的子类来创建代理对象。不需要目标类实现接口,可以直接代理普通类。

区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

 

注意:类的私有方法、类的pulibc+final方法、以及final类,JDK和CGLib均无法进行动态代理,因为私有、final均不可被代理。

 

1. 接口类

public interface DDService {
    public void addUser(String id,String password);
}

2. 实现类

public class DDServiceImpl implements DDService{
    @Override
    public void addUser(String id, String password) {
        System.out.println("调用 addUser方法");
    }
}

3. JDK动态代理类

public class JDKProxy implements InvocationHandler {
    //需要代理的目标对象
    private Object targetObject;

    /**
     * 创建 JDK 动态代理类实例
     */
    public Object newProxy(Object targetObject) {
        // 将目标对象传入进行代理
        this.targetObject = targetObject;
        // JDK提供的用于创建代理对象的静态方法, 返回代理对象
        return Proxy.newProxyInstance(
                targetObject.getClass().getClassLoader(),   // 指定目标类的类加载
                targetObject.getClass().getInterfaces(),    // 代理需要实现的接口
                this    // 调用代理对象逻辑的拦截器,即InvocationHandler 实现类
        );
    }

    /**
     * 动态代理方法拦截器。每当代理对象的方法被调用时,都会进入该方法
     *
     * @param proxy  代理目标对象的代理对象,它是真实的代理对象。
     * @param method 被拦截的目标类的方法
     * @param args   执行目标类的方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //进行逻辑处理的函数
        checkPopedom();
        //调用目标对象上对应的方法,并传入相应的参数。
        Object ret = method.invoke(targetObject, args);
        //返回目标方法的执行结果。
        return ret;
    }

    /**
     * 检查权限
     */
    private void checkPopedom() {
        System.out.println("检查权限");
    }
}

4. CGLib动态代理类

public class CGLibProxy implements MethodInterceptor {
    /**
     * 创建 CGLib 动态代理类实例
     */
    public Object createProxyObject(Object targetObject) {
        // 创建Enhancer对象,用于创建代理对象
        Enhancer enhancer = new Enhancer();
        // 设置目标类的类加载器
        enhancer.setClassLoader(targetObject.getClass().getClassLoader());
        // 设置代理目标类
        enhancer.setSuperclass(targetObject.getClass());
        // 设置方法拦截器
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    /**
     * 动态代理方法拦截器。每当代理对象的方法被调用时,都会进入该方法
     *
     * @param proxy       代理目标对象的代理对象,它是真实的代理对象。
     * @param method      被拦截的目标类的方法
     * @param args        执行目标类的方法的参数
     * @param methodProxy CGLib提供的用于调用目标方法的代理对象。通过这个MethodProxy对象,可以调用目标方法而不必使用Java的反射机制。
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //进行逻辑处理的函数
        if ("addUser".equals(method.getName())) {
            //检查权限
            checkPopedom();
        }
        //调用目标对象上对应的方法,并传入相应的参数。
        Object obj = methodProxy.invokeSuper(proxy, args);
        return obj;
    }

    private void checkPopedom() {
        System.out.println("检查权限:checkPopedom()!");
    }
}

5. Test类

public static void main(String[] args) {
    System.out.println("JDKProxy:======================");
    DDService userManagerJDK = (DDService) new JDKProxy().newProxy(new DDServiceImpl());
    userManagerJDK.addUser("tom", "root");

    System.out.println("CGLib:======================");
    DDService userManager = (DDService) new CGLibProxy().createProxyObject(new DDServiceImpl());
    userManager.addUser("tom", "root");
}

输出

JDKProxy:======================
检查权限
调用 addUser方法
CGLib:======================
检查权限:checkPopedom()!
调用 addUser方法

 

posted @ 2023-07-25 16:55  yifanSJ  阅读(171)  评论(0编辑  收藏  举报