Spring AOP 源码分析——创建代理对象,看完全都会了

userService.update(null);

}

}

测试结果如下:

如上,从测试结果中。我们可以看出,我们的代理逻辑正常执行了。另外,注意一下 userService 指向对象的类型,并非是 xyz.coolblog.proxy.UserServiceImpl,而是 com.sun.proxy.$Proxy4。

关于 JDK 动态代理,这里先说这么多。下一节,我来演示一下 CGLIB 动态代理,继续往下看吧。

2.2.2 基于 CGLIB 的动态代理

当我们要为未实现接口的类生成代理时,就无法使用 JDK 动态代理了。那么此类的目标对象生成代理时应该怎么办呢?当然是使用 CGLIB 了。在 CGLIB 中,代理逻辑是封装在 MethodInterceptor 实现类中的,代理对象则是通过 Enhancer 类的 create 方法进行创建。下面我来演示一下 CGLIB 创建代理对象的过程,如下:

本节的演示环节,打算调侃(无贬低之意)一下59式坦克,这是我们国家大量装备过的一款坦克。59式坦克有很多种改款,一般把改款统称为59改,59改这个梗也正是源于此。下面我们先来一览59式坦克的风采:

下面我们的工作就是为咱们的 59 创建一个代理,即 59改。好了,开始我们的魔改吧。

目标类,59式坦克:

public class Tank59 {

void run() {

System.out.println(“极速前行中…”);

}

void shoot() {

System.out.println(“轰…轰…轰…轰…”);

}

}

CGLIB 代理创建者

public class CglibProxyCreator implements ProxyCreator {

private Object target;

private MethodInterceptor methodInterceptor;

public CglibProxyCreator(Object target, MethodInterceptor methodInterceptor) {

assert (target != null && methodInterceptor != null);

this.target = target;

this.methodInterceptor = methodInterceptor;

}

@Override

public Object getProxy() {

Enhancer enhancer = new Enhancer();

// 设置代理类的父类

enhancer.setSuperclass(target.getClass());

// 设置代理逻辑

enhancer.setCallback(methodInterceptor);

// 创建代理对象

return enhancer.create();

}

}

方法拦截器 - 坦克再制造:

public class TankRemanufacture implements MethodInterceptor {

@Override

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

if (method.getName().equals(“run”)) {

System.out.println(“正在重造59坦克…”);

System.out.println(“重造成功,已获取 ✨59改 之 超音速飞行版✨”);

System.out.print(“已起飞,正在突破音障。”);

methodProxy.invokeSuper(o, objects);

System.out.println(“已击落黑鸟 SR-71,正在返航…”);

return null;

}

return methodProxy.invokeSuper(o, objects);

}

}

好了,下面开始演示,测试代码如下:

public class CglibProxyCreatorTest {

@Test

public void getProxy() throws Exception {

ProxyCreator proxyCreator = new CglibProxyCreator(new Tank59(), new TankRemanufacture());

Tank59 tank59 = (Tank59) proxyCreator.getProxy();

System.out.println("proxy class = " + tank59.getClass() + “\n”);

tank59.run();

System.out.println();

System.out.print(“射击测试:”);

tank59.shoot();

}

}

测试结果如下:

如上,“极速前行中…” 和 “轰…轰…轰…轰…” 这两行字符串是目标对象中的方法打印出来的,其他的则是由代理逻辑打印的。由此可知,我们的代理逻辑生效了。

好了,最后我们来看一下,经过魔改后的 59,也就是超音速59改的效果图:

本节用59式坦克举例,仅是调侃,并无恶意。作为年轻的一代,我们应感谢那些为国防事业做出贡献的科技人员们。没有他们贡献,我们怕是不会有像今天这样安全的环境了(尽管不完美)。

到此,背景知识就介绍完了。下一章,我将开始分析源码。源码不是很长,主逻辑比较容易懂,所以一起往下看吧。

3.源码分析


为目标 bean 创建代理对象前,需要先创建 AopProxy 对象,然后再调用该对象的 getProxy 方法创建实际的代理类。我们先来看看 AopProxy 这个接口的定义,如下:

public interface AopProxy {

/** 创建代理对象 */

Object getProxy();

Object getProxy(ClassLoader classLoader);

}

在 Spring 中,有两个类实现了 AopProxy,如下:

Spring 在为目标 bean 创建代理的过程中,要根据 bean 是否实现接口,以及一些其他配置来决定使用 AopProxy 何种实现类为目标 bean 创建代理对象。下面我们就来看一下代理创建的过程,如下:

protected Object createProxy(

Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

if (this.beanFactory instanceof ConfigurableListableBeanFactory) {

AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);

}

ProxyFactory proxyFactory = new ProxyFactory();

proxyFactory.copyFrom(this);

/*

  • 默认配置下,或用户显式配置 proxy-target-class = “false” 时,

  • 这里的 proxyFactory.isProxyTargetClass() 也为 false

*/

if (!proxyFactory.isProxyTargetClass()) {

if (shouldProxyTargetClass(beanClass, beanName)) {

proxyFactory.setProxyTargetClass(true);

}

else {

/*

  • 检测 beanClass 是否实现了接口,若未实现,则将

  • proxyFactory 的成员变量 proxyTargetClass 设为 true

*/

evaluateProxyInterfaces(beanClass, proxyFactory);

}

}

// specificInterceptors 中若包含有 Advice,此处将 Advice 转为 Advisor

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);

proxyFactory.addAdvisors(advisors);

proxyFactory.setTargetSource(targetSource);

customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);

if (advisorsPreFiltered()) {

proxyFactory.setPreFiltered(true);

}

// 创建代理

return proxyFactory.getProxy(getProxyClassLoader());

}

public Object getProxy(ClassLoader classLoader) {

// 先创建 AopProxy 实现类对象,然后再调用 getProxy 为目标 bean 创建代理对象

return createAopProxy().getProxy(classLoader);

}

getProxy 这里有两个方法调用,一个是调用 createAopProxy 创建 AopProxy 实现类对象,然后再调用 AopProxy 实现类对象中的 getProxy 创建代理对象。这里我们先来看一下创建 AopProxy 实现类对象的过程,如下:

protected final synchronized AopProxy createAopProxy() {

if (!this.active) {

activate();

}

return getAopProxyFactory().createAopProxy(this);

}

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

@Override

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {

/*

  • 下面的三个条件简单分析一下:

  • 条件1:config.isOptimize() - 是否需要优化,这个属性没怎么用过,

  •     细节我不是很清楚
    
  • 条件2:config.isProxyTargetClass() - 检测 proxyTargetClass 的值,

  •     前面的代码会设置这个值
    
  • 条件3:hasNoUserSuppliedProxyInterfaces(config)

  •     - 目标 bean 是否实现了接口
    

*/

if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {

Class<?> targetClass = config.getTargetClass();

if (targetClass == null) {

throw new AopConfigException("TargetSource cannot determine target class: " +

“Either an interface or a target is required for proxy creation.”);

}

if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {

return new JdkDynamicAopProxy(config);

}

// 创建 CGLIB 代理,ObjenesisCglibAopProxy 继承自 CglibAopProxy

return new ObjenesisCglibAopProxy(config);

}

else {

// 创建 JDK 动态代理

return new JdkDynamicAopProxy(config);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考

以下是部分内容截图
架构面试专题及架构学习笔记导图.png

[外链图片转存中…(img-m9vFLUlC-1712106189377)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-LysFAvO6-1712106189378)]

最后

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考

以下是部分内容截图
[外链图片转存中…(img-BXDApzLG-1712106189378)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值