动态代理

本文介绍了Java中的动态代理技术,包括JDK动态代理和Cglib动态代理。JDK动态代理依赖于接口,通过InvocationHandler实现解耦,而Cglib动态代理则针对未实现接口的类,创建其子类进行代理,效率相对较高。

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

静态代理

首先看看一般的代理模式即静态代理。我们直接来看代码。

public interface HelloService{
    void sayHello();
}
/**
 * 被代理类
 */
public class HelloServiceImpl implements HelloService{
    public void sayHello(){
        System.out.println("Hello World!");
    }
}
/**
 * 代理类
 */
public class HelloServiceProxy implements HelloService{
    private HelloServiceImpl target;
    public HelloServiceProxy(HelloServiceImpl target){
        this.target = target;
    }
    public void sayHello(){
        System.out.println("额外操作")
        target.sayHello();
    }
}
public class Client{
    public static void main(String[] args){
        HelloService service = new HelloServiceProxy(new HelloServiceImpl());
        service.sayHello()
    }
}

被代理类与代理类实现相同的接口,代理类的方法实现就是调用被代理类的同名方法,同时加入一些额外操作,比如通用的日志打印等。这就是代理。

但这种方式的弊端很明显,代理类与被代理耦合太严重。

  1. 方法名变了,代理类的方法名也要修改;
  2. 新增了方法,代理类也要新增方法;
  3. 被代理类实现了新接口,代理类也要实现新接口。

即,代理类需要我们手动地编写修改,这很麻烦。动态代理就在这种情况下应运而生。

JDK动态代理

上面的例子中代理类与别代理类实现了相同的接口,JDK动态代理的适用情景和上面一样,被代理类必须实现接口。

JDK动态代理通过增加一个中间层InvocationHandler来解耦。

public class MyJdkProxy implements InvocationHandler{
    private Object target;
    public Object getProxyInstance(Object target){
        this.target = target;
        return Proxy.newInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        System.out.println("额外操作");
        return method.invoke(target, args);
    }
}

通过Proxy.newInstance(…args)生成代理对象,代理对象在调用接口方法时,会转为调用invoke方法,在invoke方法中通过反射调用被代理对象的真实方法。

其中,Proxy.newInstance(…args)生成代理对象的关键步骤通过查看源码可以得到:

  1. 查找或生成指定的代理类。
Class<?> cl = getProxyClass0(loader, intfs);
  1. 通过反射获取入参为InvocationHandler的构造函数对象。
final Constructor<?> cons = cl.getConstructor(constructorParams);
  1. 通过构造函数对象生成代理对象。
return cons.newInstance(new Object[]{h});

很明显,最核心的就是第一步生成指定代理类。本来需要我们自己编写的代理类现在有JDK动态代理自动生成。被代理类发生了之前所说的变化后,不再像静态代理那样需要相应地改变代理类。现在,我们只需专注于代理逻辑的编写,即在invoke方法中编写相应逻辑。

为加深理解,可查看自动生成的代理类 P r o x y 0. c l a s s 。 在 代 码 中 加 上 下 面 的 代 码 , 在 项 目 根 目 录 下 会 生 成 文 件 c o m . s u n . p r o x y . Proxy0.class。在代码中加上下面的代码,在项目根目录下会生成文件com.sun.proxy. Proxy0.classcom.sun.proxy.Proxy0.class。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

截取其中的方法实现的代码,可以看到是通过调用InvocationHandler的invoke方法来实现的。

public final void sayHello(String var1) throws  {
    try {
        super.h.invoke(this, m3, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
        throw var3;
    } catch (Throwable var4) {
        throw new UndeclaredThrowableException(var4);
    }
}

Cglib动态代理

Cglib动态代理可对未实现接口的类实现代理,代理类是被代理类的子类。

Cglib动态代理通过增加中间层MethodInterceptor实现解耦。

public class MyCglibProxy implements MethodInterceptor{
    public Object getProxyInstance(Class cls){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperClass(cls);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object object, Mehtod method, Object[] args, MethodProxy methodProxy){
        System.out.println("额外操作");
        return methodProxy.invokeSuper(object, args);
    }
}

通过enhancer生成代理对象,代理对象在调用方法时,转为调用intercept方法,在intercept方法中通过invokeSuper调用被代理对象的真实方法,该方法属于直接调用,而不是反射,因此Cglib动态代理比JDK动态代理效率要高。


代码链接:码云

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值