动态代理

静态代理

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

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方法中编写相应逻辑。

为加深理解,可查看自动生成的代理类 <math> <semantics> <mrow> <mi> P </mi> <mi> r </mi> <mi> o </mi> <mi> x </mi> <mi> y </mi> <mn> 0. </mn> <mi> c </mi> <mi> l </mi> <mi> a </mi> <mi> s </mi> <mi> s </mi> <mi mathvariant="normal"> 。 </mi> <mi mathvariant="normal"> 在 </mi> <mi mathvariant="normal"> 代 </mi> <mi mathvariant="normal"> 码 </mi> <mi mathvariant="normal"> 中 </mi> <mi mathvariant="normal"> 加 </mi> <mi mathvariant="normal"> 上 </mi> <mi mathvariant="normal"> 下 </mi> <mi mathvariant="normal"> 面 </mi> <mi mathvariant="normal"> 的 </mi> <mi mathvariant="normal"> 代 </mi> <mi mathvariant="normal"> 码 </mi> <mi mathvariant="normal"> , </mi> <mi mathvariant="normal"> 在 </mi> <mi mathvariant="normal"> 项 </mi> <mi mathvariant="normal"> 目 </mi> <mi mathvariant="normal"> 根 </mi> <mi mathvariant="normal"> 目 </mi> <mi mathvariant="normal"> 录 </mi> <mi mathvariant="normal"> 下 </mi> <mi mathvariant="normal"> 会 </mi> <mi mathvariant="normal"> 生 </mi> <mi mathvariant="normal"> 成 </mi> <mi mathvariant="normal"> 文 </mi> <mi mathvariant="normal"> 件 </mi> <mi> c </mi> <mi> o </mi> <mi> m </mi> <mi mathvariant="normal"> . </mi> <mi> s </mi> <mi> u </mi> <mi> n </mi> <mi mathvariant="normal"> . </mi> <mi> p </mi> <mi> r </mi> <mi> o </mi> <mi> x </mi> <mi> y </mi> <mi mathvariant="normal"> . </mi> </mrow> <annotation encoding="application&#47;x&#45;tex"> Proxy0.class。在代码中加上下面的代码,在项目根目录下会生成文件com.sun.proxy. </annotation> </semantics> </math>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动态代理效率要高。


代码链接:码云

全部评论

相关推荐

听说改名字就能收到offer哈:Radis写错了兄弟
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务