Java jdk与cglib 动态代理

本文深入探讨了Java代理模式,包括静态代理和动态代理(JDK代理)。JDK代理需要目标对象实现接口,代理的是接口方法,而CGLIB代理则针对具体类,能代理所有方法。通过示例展示了如何使用这两种代理模式进行方法增强。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式

概念:

  1. 真实对象:被代理的对象
  2. 代理对象:
  3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的

实现方法:

  1. 静态代理:有一个类文件描述代理模式
  2. 动态代理:在内存中形成代理类
    • 实现步骤
      1. 代理对象和真实对象实现相同的接口
      2. 代理对象 = Proxy.newProxyInstance();
      3. 使用代理对象调用方法
      4. 增强方法
    • 增强方式
      1. 增强参数列表
      2. 增强返回值类型
      3. 增强方法体执行逻辑

JDK 代理

  • JDK 代理是需要有接口的, 在 Proxy.newProxyInstance() 的第二个参数中可以看出,是需要接口类的
  • 因为生成的代理类已经继承了一个Proxy对象,所以不能再继承了,那我们得 is-a 才能进行代理,也就是说才能执行他的方法。所以代理需要不是继承就是接口
  • 加VM参数:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true可以将生成的动态代理类输出到文件,这个文件默认处于当前项目的com.jdk包下
  • 也就是可以在代码中设置 System.setProperty(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);
  • 准确的说,jdk代理的是接口所持有的方法,而不是代理具体的实现类

案例

interface TargetDao {
    String doSomething();
}

class TargetDaoImpl implements TargetDao {
    @Override
    public String doSomething() {
        System.out.println("TargetDaoImpl 做一些事情");
        return "成功";
    }
}
class ProxyFactory{
     Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 每调用一个方法都会执行这里
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 该方法体内不能打印proxy代理类,否则会一直循环该代理
                System.out.println("jdk代理类的方法之前前增强");
                Object invoke = method.invoke(target, args);
                System.out.println("jdk代理类的方法之后后增强");
                return invoke;
            }
        });
    }
}

public class JdkProxyClient {
    public static void main(String[] args) {
        TargetDao targetDao = new TargetDaoImpl();
        ProxyFactory proxyFactory = new ProxyFactory(targetDao);
        TargetDao proxyInstance = (TargetDao)proxyFactory.getProxyInstance();
        String s = proxyInstance.doSomething();
        System.out.println(s);
    }
}

代理生成的.class

// 实现代理对象 TargetDao
public final class $Proxy0 extends Proxy implements TargetDao {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
	// 生成的代理方法
    public final String doSomething() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.jdk.TargetDao").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从生成的代理类.class中看出,他确实代理的是接口所持有的方法,而不会代理具体实现类,如果强转成具体实现类是不可行的

CGLIB代理

  1. cglib 依赖于 asm 需要代入asm的依赖
  2. cglib 不用局限于是否带有接口,但带有接口的,也能代理,因为生成的代理类默认实现了一个接口,所以还可以使用继承,而接口可以多实现,所以也可以代理接口的对象
  • 查看cglib生成的代理类

  • 在运行环境中添加参数-Dcglib.debugLocation=P:\java\jdkstudyidea\proxy\target\class

  • 生成代理类的结构:

    • public class TargetDao E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB91064b0b implements TargetDao, Factory

    • public class TargetDaoImpl E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBf99331 extends TargetDaoImpl implements Factory

  1. 总会实现一个Factory接口
  2. 代理类的方法都会有两个方法
  • 一个是原生的方法 使用CGLIB X X X XXX XXXi自增的方式记录

    • CGLIB$equals$0
    • CGLIB$toString$1
    • CGLIB$doSomething$4
  • 一个是代理生成的方法

    • equals

    • toString

    • doSomething

  1. 代理的方法

    public final String doSomething() {
    	// 这个就是我们设置的setCallback实现类
    	MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    	// 判断是否设置了,如果等于空,就设置一个默认的回调
    	if (var10000 == null) {
    		CGLIB$BIND_CALLBACKS(this);
    		var10000 = this.CGLIB$CALLBACK_0;
    	}
         // 如果没有自定义,那么就执行原生的方法,
         // 如果定义了callback, 那么就执行定义的
         return var10000 != null ? (String)var10000.intercept(this, CGLIB$doSomething$4$Method, CGLIB$emptyArgs, CGLIB$doSomething$4$Proxy) : super.doSomething();
    }
    

案例

加jar:

  1. cglib-3.3.0.jar
  2. asm-9.2.jar
interface TargetDao {
    String doSomething();
}

class TargetDaoImpl implements TargetDao {
    @Override
    public String doSomething() {
        System.out.println("TargetDaoImpl 做一些事情");
        return "成功";
    }
}

public class CglibProxyClient {
    public static void main(String[] args) {
        TargetDao target = new TargetDaoImpl();
        Enhancer enhancer = new Enhancer();
        // 如果.class 是接口,则实现,如果.class 是类或抽象类,则继承
        enhancer.setSuperclass(TargetDaoImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * 每调用一个方法都会执行这里
             * 生成的代理类执行时是这样的
             *  var10000.intercept(this, CGLIB$doSomething$4$Method, CGLIB$emptyArgs, CGLIB$doSomething$4$Proxy)
             * @param o 对应 this,即生成的代理类
             * @param method    正在执行的方法
             * @param params    正在执行的方法的参数
             * @param methodProxy   cglib定义的,方法代理,即生成的代理类中会出现两个方法
             *                          这个对象有 invoke 和 invokeSuper
             *                          invoke 执行的是代理类中的被增强的方法 doSomething
             *                          invokeSuper 执行的是代理类中的父类方法(原始没有被代理的方法) CGLIB$doSomething$4
             *                          但invokeSuper不能使用target对象,只能使用o对象(被代理的代理对象),因为代理类能转成目标类,而目标类不能转成代理类
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
                System.out.println("cglib 代理 前增强");
                Object methodReturnVal = method.invoke(target, params);
                // 代理类能转成目标类,而目标类不能转成代理类
//                methodProxy.invokeSuper(target, params);
                System.out.println("cglib 代理 前增强");
                return methodReturnVal;
            }
        });
        TargetDao o = (TargetDao)enhancer.create();
        String s = o.doSomething();
        System.out.println(s);
    }
}

代理生成的.class

public class TargetDaoImpl$$EnhancerByCGLIB$$f99331 extends TargetDaoImpl implements Factory {
    
}

总结

  1. jdk代理代理的是接口,而不是具体的实现类,从.class中可以看出,是实现需要代理的那个类的接口

    所以,具体实现类的独有方法是不能被代理的,包括继承了某个类,那么父类的方法是不能够代理的

  2. cglib代理代理的是具体类,即你需要代理哪个类他就是继承哪个类给你

    所以,所有方法都能被代理到,包括上面说的父类的方法

  3. jdk代理的是接口,cglib代理的是具体类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值