ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java设计模式之代理模式 - CGLib 动态代理分析

2021-01-28 09:03:56  阅读:145  来源: 互联网

标签:Java 代理 private final static CGLIB 设计模式 Method


JDK代理一样,CGLib 代理也是一种动态代理方式,而且相比JDK代理更加的灵活,可以代理任何类(除了final修饰的)。

先上代码,还是以买房为例,看下具体实现:

需要被代理的类,这次不是某个接口的实现类,而是一个普通类

public class HouseBuyer {

    public void buy() {
        System.out.println("要买房");
    }

}

代理类

public class CglibHouseCompanyProxy implements MethodInterceptor {

    public Object getInstance(Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(o, objects);
        after();
        return obj;
    }

    private void before() {
        System.out.println("before method ...");
    }

    private void after() {
        System.out.println("after method ...");
    }
}

在被客户端实际调用的时候

HouseBuyer buyer = (HouseBuyer) new CglibHouseCompanyProxy().getInstance(new HouseBuyer());
buyer.buy();

是不是似曾相识,代理类都是实现了某个接口,然后重写一个方法,看着都是利用反射去获取被代理类的方法,经过增强处理然后去执行。

同样的,经过断点调试后会发现,真正执行方法的对象也是也是新的对象,

image-20210127160600597

可以通过org.springframework.cglib.core包里的DebuggingClassWriter类将代理过程的字节码文件生成到磁盘上来简单分析下,在上面调用方法的前面加上

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "external-file/cglib-debug");

重新跑一遍代码,在项目根目录下会生成external-file文件夹,在里面找到HouseBuyer$$EnhancerByCGLIB$$a1e313be类进行反编译,节选部分代码

public class HouseBuyer$$EnhancerByCGLIB$$a1e313be extends HouseBuyer implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$buy$0$Method;
    private static final MethodProxy CGLIB$buy$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("xxx.xxx.xxx.cglibproxy.HouseBuyer$$EnhancerByCGLIB$$a1e313be");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        CGLIB$buy$0$Method = ReflectUtils.findMethods(new String[]{"buy", "()V"}, (var1 = Class.forName("xxx.xxx.xxx.cglibproxy.HouseBuyer")).getDeclaredMethods())[0];
        CGLIB$buy$0$Proxy = MethodProxy.create(var1, var0, "()V", "buy", "CGLIB$buy$0");
    }

    final void CGLIB$buy$0() {
        super.buy();
    }

    public final void buy() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$buy$0$Method, CGLIB$emptyArgs, CGLIB$buy$0$Proxy);
        } else {
            super.buy();
        }
    }
}

代理生成的类继承了被代理类,并且里面还有一个被final修饰的buy方法,这个方法里MethodInterceptor获取引用之后就会执行intercept方法,而我们的代理类也是MethodInterceptor接口的实现类,所以在客户端类时间执行的时候最终执行的是代理类的intercept方法。至于CGLib是如何生成这个代理对象的,可以从代理类获取增强实例enhancer.create()那里一步一步分析,这里就不做了。

小结

CGLib 通过为代理类生成一个继承的子类形式来代理,对被代理类没有限制,不需要像JDK代理那样需要实现接口。底层通过字节码处理框架ASM来生成Java的字节码形成新的类,也就是上文新的代理类。简而言之:

  • JDK 代理是实现了被代理对象的接口,而CGLib代理是继承了被代理对象
  • 两者都是在运行期间生成字节码,JDK代理是直接写Class字节码,而CGLib是通过ASM框架来写Class字节码

标签:Java,代理,private,final,static,CGLIB,设计模式,Method
来源: https://blog.csdn.net/liu_1024_/article/details/113307196

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有