JDK动态代理与CGLib动态代理

    xiaoxiao2023-10-02  152

    JDK动态代理

    先写一个JDK动态代理,一个演员需要有经纪人,我们把演员当成被代理类,经纪人当成代理类

    演员类Actor.java:

    package com.lw.designpattern.proxy.dynamic; public class Actor { public void act() { System.out.println("某毯星戛纳走红毯"); } }

    调用处理类ActorInvocation.java,为什么要设计这么一个类,后面讲

    package com.lw.designpattern.proxy.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ActorInvocation<T> implements InvocationHandler { T target; public ActorInvocation(T target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("经纪人准备"); method.invoke(this.target, args); System.out.println("经纪人善后"); return null; } }

    测试类:

    package com.lw.designpattern.proxy.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class DynamicProxyTest { public static void main(String[] args) { Actor actor = new Actor(); InvocationHandler actorHandler = new ActorInvocation<Actor>(actor); Actor actorProxy = (Actor) Proxy.newProxyInstance(Actor.class.getClassLoader(), new Class<?>[] { Actor.class }, actorHandler); actorProxy.act(); } }

    测试结果: 

    Exception in thread "main" java.lang.IllegalArgumentException: com.lw.designpattern.proxy.dynamic.Actor is not an interface at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590) at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557) at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230) at java.lang.reflect.WeakCache.get(WeakCache.java:127) at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419) at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719) at com.lw.designpattern.proxy.dynamic.DynamicProxyTest.main(DynamicProxyTest.java:12)

    咦,报错了。。。。提示Actor类不是一个接口,原因是啥,暂且按下不表,先改成接口再说

    package com.lw.designpattern.proxy.dynamic; public interface Actor { public void act(); }

    增加实现类:

    package com.lw.designpattern.proxy.dynamic; public class BBFan implements Actor { @Override public void act() { System.out.println("某毯星戛纳走红毯"); } }

    测试结果:

    经纪人准备 某毯星戛纳走红毯 经纪人善后

    OK,搞定,下面讲讲实现原理

     

    JDK动态代理原理

    在说原理之前,先搞清楚概念,所谓的动态,其实就是动态生成一个代理类Class,相比较静态代理模式的手写代理类,动态代理在这方面可谓是更加灵活。下面先看看生成的代理类是个什么样子。

    代理类我是直接通过ProxyGenerator类生成的,这也是JDK源码使用的类,所以生成的代理类中没有包含自定义的代理功能。

    当然也可以在测试方法中加上这句:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

    byte[] proxyClassFile = ProxyGenerator.generateProxyClass("ActorProxy", new Class[]{Actor.class}); try { FileOutputStream f = new FileOutputStream("ActorProxy.class"); f.write(proxyClassFile); f.flush(); f.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }

    生成代理类class的反编译源码: 

    import com.lw.pattern.proxy.dynamic.Actor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class ActorProxy extends Proxy implements Actor { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public ActorProxy(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 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 void act() throws { try { // 这边通过父类的h属性调用实际业务方法 super.h.invoke(this, m3, (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")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.lw.pattern.proxy.dynamic.Actor").getMethod("act"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }

    从这个源码我们来解释一下上面遗留的两个问题:

    一,代理类继承了JDK的Proxy Class,因为Java规范设计一个类只能继承一个父类,所以,这也就解释了为什么JDK的动态代理要求被代理类一定要实现接口。

    二,Invocation的设计是为了动态代理的调用,我们可以在Proxy类中找到关于Invocation的属性定义:

    /** * the invocation handler for this proxy instance. * @serial */ protected InvocationHandler h;

    有了这个实例变量的定义,我们就可以把这个Invocation看作是动态代理的模板方法,这样的话,每new一个新的被代理类,就不需要像静态动态代理那样修改代理类。

     

    看到这里,就会发现,动态代理真的没有什么高深的东西,本质上同静态代理的实现思路也一致。

     

    CGLib动态代理

    CGlib 动态代理模式的实现原理是对代理目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类必须要有接口的缺陷。同时,由于是继承方式,如果是 static方法,private方法,final方法等描述的方法是不能被代理的,cglib动态代理底层是借助asm字节码技术;

     

    最新回复(0)