CGLB动态代理

一、CGLIB实现接口

public interface ProductInterface {
void test();
}
/**
* 用来测试接口
*/
private static void testInterface() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductInterface.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行切面逻辑");
return null;
}
}); ProductInterface productInterfaceProxy = (ProductInterface)enhancer.create();
productInterfaceProxy.test();
}

二、CGLIB实现类

对应的类:

public class UserService {
public void test(){
System.out.println("execute userService.......");
}
}
/**
* 用来测试类
*/
private static void testClass() {
UserService userService = new UserService(); Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param o 当前的代理对象
* @param method 当前代理对象正在调用的方法
* @param objects 当前代理对象正在调用方法时候的参数
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开始来执行切面逻辑");
return method.invoke(userService,objects);
}
});
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.test();
}

三、原理探究

3.1、找到代理类源文件

现在首要的是先找到CGLIB产生代理对象之后的文件中对应的源代码

首先在idea -> run -> edit configurations 中设置 vm options中,添加

-Dcglib.debugLocation=D:\project\study\spring\spring-xml\target

然后执行如下代码:

在对应的路径下找到对应的文件

3.2、找到代理类中的属性和方法

3.2.1、调用测试方法

因为对于接口或者是类来说,里面都有个test方法。

对于第一个方法来说,是不需要经过动态代理直接调用父类中的方法的;

对于第二个方法来说,这里会来执行方法拦截器中的拦截逻辑,来执行真正的代理逻辑方法。

首先分析,CGLIB$CALLBACK_0最开始的时候是没有进行赋值的,所以会从当前类中的CGLIB$BIND_CALLBACKS方法中查询,注意当前的this代表的是代理对象

if (var10000 == null) {
// 设置代理对象
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}

3.2.2、方法拦截器在代理对象中如何进行设置的?

现在问题转换成了怎么从ThreadLocal中获取得到方法拦截器给当前的代理对象的

所以就得先看CGLIB$THREAD_CALLBACKS是从哪里设置对应值的

那么只需要找一下这个方法在哪里使用的即可

发现这个方法是在Factory接口中的newInstance方法

那么也就是说,利用Factory接口中的newInstance方法是可以创建出来一个代理对象的

Factory factory = (Factory) enhancer.create();
Object newInstance = factory.newInstance(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return null;
}
});

手动利用Factory手动创建一个新的代理对象出来,但是一般不会这样子来进行操作。

所以方法拦截器到底是在哪里设置进去的呢?从代理对象执行方法会进入到方法拦截器中去,可以看到

UserService userServiceProxy = (UserService) enhancer.create();

应该是在上面这行代码中进行设置的

查看一下方法注释:先创建新的类,然后根据类去创建代理对象的过程

首先创建key,可以看到key中保存的有父类和接口信息,这么设计的目的是干嘛的?

因为最终这里会将key保存到一个map中去,而对应的value是对应的代理类。

因为不是每次调用下面这行代码的时候,都会去创建新的代理类

UserService userServiceProxy = (UserService) enhancer.create();
UserService userServiceProxy = (UserService) enhancer.create();
UserService userServiceProxy = (UserService) enhancer.create();
UserService userServiceProxy = (UserService) enhancer.create();

我们的目的是根据代理类创建新的代理对象,所以我们可以设置

Enhancer enhancer = new Enhancer();
enhancer.setUseCache(true);

代理类可以重复获取得到,而不需要去重复的生成对应的代理类。

所以这里的生成的key就是为了来做一个缓存而已,表示是否复用第一次产生的代理类

所以在这行代码中既会产生代理类也会产生代理对象

Object result = super.create(key);

3.2.3、设置方法拦截器到线程上下文

利用代理类去创建代理对象!

那么看一下具体的实现逻辑

所以方法拦截器是在代理类生成之后,代理类对象生成之前,设置到代理类中去的。

所以产生代理对象之后,在调用方法的时候,就会去调用方法拦截器中的逻辑

四、MethodProxy

到现在还有一个参数没有来进行说明:MethodProxy

那么下面来说明一下这个类

最终生成的代理类中因为是继承了UserService,所以会有UserService中的方法

一个是自己代理方法增强后的逻辑,另外一个是执行父类中的方法

所以对于

// methodProxy代理的是test||CGLIB$test$0方法
// 如果方法名称是invoke的话,那么调用的是test方法
methodProxy.invoke(userService,objects);
// 如果方法名称是invokeSuper的话,那么调用的是CGLIB$test$0方法
methodProxy.invokeSuper(userService,objects);

但是需要注意的是,对于第一行代码来说,访问的是test方法,userService是有test方法的;

对于第二行代码来说,访问的是userService中的CGLIB$test$0方法,但是userService是没有CGLIB$test$0方法的

但是一定要注意的是下面这行代码:

methodProxy.invoke(o,objects);

这行代码一旦执行会发生栈溢出,因为执行代理对象的test方法,会不断的进入到方法拦截器中来。

那么methodProxy执行底层对应的逻辑是怎么样的?

后续会来进行补充说明。

CGLB动态代理的更多相关文章

  1. (十二)mybatis之动态代理

    mybatis之动态代理的应用 在前文(https://www.cnblogs.com/NYfor2018/p/9093472.html)我们知道了,Mybatis的使用需要用到Mapper映射文件, ...

  2. spring中使用动态代理(AOP)

    spring是整合了BGLIB和JDK两种动态代理 示例:使用CGLIB代理 public class MyCar { private String color = "blue"; ...

  3. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...

  4. AOP之Castle DynamicProxy 动态代理

    这里主要介绍使用castle这个动态代理,在.net一些开源的框架里可以找到它的影子,就连微软的rchard也是使用这个进行方法拦截等可以基于这个进行方法拦截,在这个方面PostSharp算是比较好用 ...

  5. java动态代理的2种实现方式

    java的动态代理在接java的api上有说明,这里就不写了.我理解的代理: 对特定接口中特定方法的功能进行扩展,这就是代理.代理是通过代理实例关联的调用处理程序对象调用方法. 下面通过一个例子看一下 ...

  6. JDK动态代理实现原理

    之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的.直到看了他的文章才彻底明白,附网址:htt ...

  7. java中动态代理的实现

    动态代理的实现 使用的模式:代理模式. 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.类似租房的中介. 两种动态代理: (1)jdk动态代理,jdk动态代理是由Java内部的反射机制 ...

  8. 静态代理和利用反射形成的动态代理(JDK动态代理)

    代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 静态代理 1.新建 ...

  9. Java动态代理

    代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关 ...

  10. Java 动态代理机制详解

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

随机推荐

  1. .net做一个基于ChatGpt的微信机器人吧~[全教程]

    最近这个ChatGPT很火啊,看了B站上很多视频,自己非常手痒,高低自己得整一个啊,很多人都是把ChatGPT和微信结合在一起,正巧我是Wechaty框架的.net sdk贡献者,这不是一应俱全了吗? ...

  2. java中继承的内存分析

    本文主要讲述java中继承的内存分析. 示例1,代码如下: public class EncapsulationTest { public static void main(String[] args ...

  3. Jmeter之响应数据乱码问题

    在进行接口测试时经常长出现响应结果中中文乱码问题 解决以上现象最简洁有效的方法为在测试计划下添加beanshell后置处理器 1.右击测试计划->添加->后置处理器->beanshe ...

  4. 脚本运行正常,但抛出警告ResourceWarning: unclosed <socket.socket fd=688, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 61803)

    脚本运行正常,但抛出警告,如下图,此时可导入warnings,忽略警告.

  5. vulnhub靶场之HACKER KID: 1.0.1

    准备: 攻击机:虚拟机kali.本机win10. 靶机:Hacker kid: 1.0.1,下载地址:https://download.vulnhub.com/hackerkid/Hacker_Kid ...

  6. App几个可能造成内存泄漏的情况:

    App几个可能造成内存泄漏的情况: 1.block块中直接用self调用,self将会被block copy到内部增加一次饮用计数,形成循环引用 在block里调用self会不会造成循环引用和这个bl ...

  7. Openmp Runtime 库函数汇总(下)——深入剖析锁🔒原理与实现

    Openmp Runtime 库函数汇总(下)--深入剖析锁原理与实现 前言 在本篇文章当中主要给大家介绍一下 OpenMP 当中经常使用到的锁并且仔细分析它其中的内部原理!在 OpenMP 当中主要 ...

  8. vs2019 内核驱动编译失败:网络共享盘问题

    怀念2008 我以为这个年代了,中文路径问题应该解决了,记得vs 2008写c的时候曾因中文路径的问题导致编译不通过,从2015版本开始发现有时候项目名称和路径里包含中文也可以编译通过了,习惯之后后面 ...

  9. C#高性能数组拷贝实验

    前言 昨天 wc(Wyu_Cnk) 提了个问题 C# 里多维数组拷贝有没有什么比较优雅的写法? 这不是问对人了吗?正好我最近在搞图像处理,要和内存打交道,我一下就想到了在C#里面直接像C/C++一样做 ...

  10. SEO关键词布局方法

    关键词在<title>标签. description属性. keywords属性中是如何布局的. 1.<title>标签中布局关键词 <title>标签是用来定义网 ...