CGLIB动态代理-使用及原理

java 专栏收录该内容
36 篇文章 0 订阅

CGLIB动态代理

简单使用

还是最常见的例子,租客——中介——房东

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mxuEiBds-1603552704732)(D:\笔记\博客\images\java\代理\jdk动态代理\租房.jpg)]

代码中使用输出语句代替正真的业务!

首先,我们定义一个房东类

public class Landlord{
     public void deliver() {
        try {
            System.out.println("告知房东出租成功,房东收钱");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止");
        }
    }

    public void out() {
        try {
            System.out.println("告知房东,租客退房");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止");
        }
    }
}

接下来创建一个类,实现org.springframework.cglib.proxy.MethodInterceptor接口或net.sf.cglib.proxy.MethodInterceptor接口,该类用来对原来的方法增强。(注意包,别导错)

依赖:自己选择,使用一个就行,我这里使用cglib

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibMethodInterceptor implements MethodInterceptor {

    // 需要被代理的对象
    private Object target;

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * 通过 method 引用实例    Object result = method.invoke(target, args); 形式反射调用被代理类方法,
     * target 实例代表被代理类对象引用, 初始化 CglibMethodInterceptor 时候被赋值 。但是Cglib不推荐使用这种方式
     * @param obj    代表Cglib 生成的动态代理类 对象本身
     * @param method 代理类中被拦截的接口方法 Method 实例
     * @param args   接口方法参数
     * @param proxy  用于调用父类真正的业务类方法。可以直接调用被代理类接口方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object retVal = null;
        try {
            System.out.println("与中介的交谈");
            // 房东收钱
            Object result = proxy.invokeSuper(obj, args);
            // Object result = method.invoke(target, args);
            System.out.println("达成协议");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止协议");
        }
        return retVal;
    }
}

最后写测试类(租客)

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

import java.lang.reflect.Field;
import java.util.Properties;

public class CglibTest {
    public static void main(String[] args) throws Exception {
        // 两个new对象和设置对象应该交给spring去做,不应该在这里写的,为了方便理解,就不关联到spring了!
        // 增强类
        CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
        // 需被代理的对象
        Landlord landlord = new Landlord();
        // 把真实对象放入增强类中,隐藏起来
        cglibMethodInterceptor.setTarget(landlord);

        // 创建Enhancer,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        // 为代理类指定需要代理的类
        enhancer.setSuperclass(cglibMethodInterceptor.getTarget().getClass());
        // 设置调用代理类会触发的增强类
        enhancer.setCallback(cglibMethodInterceptor);

        // 获取动态代理类对象并返回
        Landlord proxy = (Landlord) enhancer.create();

        // 调用代理类的方法
        proxy.deliver();
        System.out.println("==================");
        proxy.out();
    }
}
执行结果:
与中介的交谈
告知房东出租成功,房东收钱
达成协议
==================
与中介的交谈
告知房东,租客退房
达成协议

又到了疑惑环节,怎么创建代理类的?又怎么会调用增强类的intercept方法的?那就接下去看看原理吧!


原理

1. 测试类中的setSuperclass方法,点进去

// 为代理类指定需要代理的类
// cglibMethodInterceptor.getTarget().getClass():需被代理的类
enhancer.setSuperclass(cglibMethodInterceptor.getTarget().getClass());

到Enhancer类的setSuperclass方法,省去不必要的代码

public void setSuperclass(Class superclass) {
    // 这里就简单的把真实类(需被代理的类)赋值给Enhancer对象的字段,后面创建代理类时会被使用
    this.superclass = superclass;
}

2. 测试类中的setCallback方法,点进去

// 设置调用代理类会触发的增强类
// cglibMethodInterceptor:增强类
enhancer.setCallback(cglibMethodInterceptor);

到Enhancer类,往setCallbacks方法走

public void setCallback(Callback callback) {
    this.setCallbacks(new Callback[]{callback});
}

省去不必要的代码

public void setCallbacks(Callback[] callbacks) {
    // 把增强类赋值给Enhancer对象的字段,后面创建代理类时会被使用
    this.callbacks = callbacks;
}

3. 测试类中的create方法,点进去

// 获取动态代理类对象并返回
Landlord proxy = (Landlord) enhancer.create();

到Enhancer类,省去不必要的代码,往createHelper方法走

public Object create() {
    return this.createHelper();
}

省去不必要的代码

 private Object createHelper() {
     // 参数不用管
     Object result = super.create(key);
     return result;
 }

进入父类(AbstractClassGenerator)的create,省去不必要的代码

真正创建代理对象的方法在nextInstance方法中

protected Object create(Object key) {
    return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
}

AbstractClassGenerator类的nextInstance是抽象方法,进入子类(Enhancer)的nextInstance方法,省去不必要的代码

newInstance方法通过反射生成代理对象

protected Object nextInstance(Object instance) {
    // 第一个参数为代理对象的构成器类型,第二个为代理对象构造方法参数,第三个为对应回调对象
    return data.newInstance(argumentTypes, arguments, this.callbacks);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r6YtLvF4-1603552704737)(D:\笔记\博客\images\java\代理\cglib动态代理\代理类.png)]

不会主动生成代理类的class文件,我们需要主动去获取出来

以下代码包含获取代理类字节码,并放到指定位置

此方法要在创建代理类前使用!

public class CglibTest {
    public static void main(String[] args) throws Exception {
        // 开启 保存cglib生成的动态代理类类文件
        // System.getProperty("user.dir"):当前项目绝对路径
        saveGeneratedCGlibProxyFiles(System.getProperty("user.dir"));

        CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
        Landlord landlord = new Landlord();
        cglibMethodInterceptor.setTarget(landlord);

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cglibMethodInterceptor.getTarget().getClass());
        enhancer.setCallback(cglibMethodInterceptor);

        Landlord proxy = (Landlord) enhancer.create();

        proxy.deliver();
        System.out.println("==================");
        proxy.out();
    }

    /**
     * 设置保存Cglib代理生成的类文件。
     */
    public static void saveGeneratedCGlibProxyFiles(String dir) throws Exception {
        Field field = System.class.getDeclaredField("props");
        field.setAccessible(true);
        Properties props = (Properties) field.get(null);
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, dir);//dir为保存文件路径
        props.put("net.sf.cglib.core.DebuggingClassWriter.traceEnabled", "true");
    }
}

代理类生成的class文件有三个

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tx2fDrJM-1603552704740)(D:\笔记\博客\images\java\代理\cglib动态代理\字节码位置.jpg)]

我们看中间那个,代理省略Object的方法和不必要代码

public class Landlord$$EnhancerByCGLIB$$f377692b extends Landlord implements Factory {
    private MethodInterceptor CGLIB$CALLBACK_0;		// 拦截器(增强类)
    private static final Object[] CGLIB$emptyArgs;		// 参数
    private static final Method CGLIB$deliver$1$Method;	// 被代理方法
    private static final MethodProxy CGLIB$deliver$1$Proxy;	// 代理方法
    private static final Method CGLIB$out$0$Method;		// 被代理方法
    private static final MethodProxy CGLIB$out$0$Proxy;	// 代理方法

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        // 代理类
        Class var0 = Class.forName("com.example.demo.cglibtest.Landlord$$EnhancerByCGLIB$$f377692b");
        // 被代理类
        Class var1;
        // 方法集合
        var10000 = ReflectUtils.findMethods(new String[]{"out", "()V", "deliver", "()V"}, (var1 = Class.forName("com.example.demo.cglibtest.Landlord")).getDeclaredMethods());
        // 方法实例赋值给字段(被代理方法)
        CGLIB$out$0$Method = var10000[0];
        // 方法实例赋值给字段(代理方法)
        CGLIB$out$0$Proxy = MethodProxy.create(var1, var0, "()V", "out", "CGLIB$out$0");
        CGLIB$deliver$1$Method = var10000[1];
        CGLIB$deliver$1$Proxy = MethodProxy.create(var1, var0, "()V", "deliver", "CGLIB$deliver$1");
    }

    public final void out() {
         // 获取拦截器(增强类)
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        // 拦截器(增强类)
        var10000.intercept(this, CGLIB$out$0$Method, CGLIB$emptyArgs, CGLIB$out$0$Proxy);

    }

    public final void deliver() {
        // 获取拦截器(增强类)
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        // 拦截器(增强类)
        var10000.intercept(this, CGLIB$deliver$1$Method, CGLIB$emptyArgs, CGLIB$deliver$1$Proxy);
    }
}

创建代理类时就把代理类方法和被代理类方法、参数等设置给字段!

当我们调用测试类的方法时,就会调用到上述代码的 public final void deliver() {}public final void out() {}代码,最终都会调用拦截器(增强类)的intercept()方法!

// 调用代理类的方法
proxy.deliver();
proxy.out();

可以看出,调用代理类最终会调用增强类的intercept方法。

  • 1
    点赞
  • 1
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页

打赏作者

Coding~Farmer

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值