Spring AOP 中被代理的对象一定是单例吗?

今天我们来思考这样一个问题:在 Spring AOP 中,被代理的对象是单例的吗?当我们每次获取到代理对象的时候,都会重新获取一个新的被代理对象吗?还是被代理的对象始终是同一个?

为什么要思考这个问题,因为在松哥接下来要讲的 @Scope 注解高级用法中涉及到这个知识点。

1. 问题呈现

假设我有如下一个计算器接口:

public interface ICalculator {void add(int a, int b);int minus(int a, int b);
}

然后给这个接口提供一个实现类:

public class CalculatorImpl implements ICalculator {@Overridepublic void add(int a, int b) {System.out.println(a + "+" + b + "=" + (a + b));}@Overridepublic int minus(int a, int b) {return a - b;}
}

现在假设我要生成一个代理对象,利用编程式的方式,代码如下:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new CalculatorImpl());
proxyFactory.addInterface(ICalculator.class);
proxyFactory.addAdvice(new MethodInterceptor() {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();String name = method.getName();System.out.println(name+" 方法开始执行了。。。");Object proceed = invocation.proceed();System.out.println(name+" 方法执行结束了。。。");return proceed;}
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
calculator.add(3, 4);

这里几个方法应该都好理解:

  1. setTarget 方法是设置真正的被代理对象。这个在我们之前的 @Lazy 注解为啥就能破解死循环?一文中大家已经接触过了。
  2. addInterface,基于 JDK 的动态代理是需要有接口的,这个方法就是设置代理对象的接口。
  3. addAdvice 方法就是添加增强/通知。
  4. 最后通过 getProxy 方法获取到一个代理对象然后去执行。

最终打印结果如下:

这是一个简单的 AOP 案例。

现在我们的问题在于 setTarget 方法上。

我们点进来到 setTarget 方法上看一下这个方法做了什么:

public void setTarget(Object target) {setTargetSource(new SingletonTargetSource(target));
}

小伙伴们看到,setTarget 方法内部调用了 setTargetSource 方法,这个方法设置了一个 SingletonTargetSource 来作为 targetSource,从名字上就能看出来,这个 SingletonTargetSource 是一个单例的 targetSource。

因此,对于上面的代码,我们可以推断,多个不同的代理对象中持有的相同的被代理对象,例如下面这段代码:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new CalculatorImpl());
proxyFactory.addInterface(ICalculator.class);
proxyFactory.addAdvice(new MethodInterceptor() {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();String name = method.getName();System.out.println(name+" 方法开始执行了。。。");Object proceed = invocation.proceed();System.out.println(name+" 方法执行结束了。。。");return proceed;}
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
ICalculator calculator2 = (ICalculator) proxyFactory.getProxy();
calculator2.add(2, 3);

我们分别获取了 calculator 和 calculator2 两个代理对象,但是实际上,这两个代理对象中持有的是同一个被代理对象,如下图:

从这张图可以看出,代理对象不是同一个,但是被代理对象其实是同一个。

2. TargetSource

在 Spring AOP 中,否则处理代理对象的接口是 TargetSource,TargetSource 有诸多实现类,不同实现类具备不同的能力:

很多实现类单纯从名字上就能看出来其特点了。

我们先来看下 TargetSource 接口:

public interface TargetSource extends TargetClassAware {@Override@NullableClass<?> getTargetClass();boolean isStatic();@NullableObject getTarget() throws Exception;void releaseTarget(Object target) throws Exception;
}

这个接口一共是四个方法:

  1. getTargetClass:这个是返回被代理对象的类型。
  2. isStatic:这个方法判断被代理对象是否是不变的,也可以理解为返回被代理对象是否是单例的,不过这个方法并不控制单例的实现,这个方法存在意义在于,如果该方法返回 true,表示被代理的对象是单例的,那么将来就不用调用 releaseTarget 方法去释放对象,反之,如果这个方法返回 false,表示被代理的对象不是单例的,那么就需要在使用完被代理的对象之后,调用 releaseTarget 方法将之释放掉。
  3. getTarget:这个方法就是返回被代理对象。
  4. releaseTarget:释放被代理的对象。

TargetSource 的实现类比较多,我们来看几个典型的实现类。

2.1 SingletonTargetSource

先来看这个类的定义:

public class SingletonTargetSource implements TargetSource, Serializable {@SuppressWarnings("serial")private final Object target;public SingletonTargetSource(Object target) {Assert.notNull(target, "Target object must not be null");this.target = target;}@Overridepublic Class<?> getTargetClass() {return this.target.getClass();}@Overridepublic Object getTarget() {return this.target;}@Overridepublic void releaseTarget(Object target) {// nothing to do}@Overridepublic boolean isStatic() {return true;}
}

如果被代理的对象是单例的,那么我们就会选择使用 SingletonTargetSource,被代理的对象总是在 getTarget 方法中被调用,然而这个方法返回的总是同一个对象,所以最终被代理的对象就是单例的。

同时,由于被代理对象是单例的,因此 isStatic 方法返回 true,releaseTarget 中不需要额外操作。

2.2 SimpleBeanTargetSource

SimpleBeanTargetSource 比较典型,这个是每当需要的时候,就去 Spring 容器中查找相应的被代理的 Bean,至于这个被代理的 Bean 是否为单例,就由 Spring 容器来控制了:

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {@Overridepublic Object getTarget() throws Exception {return getBeanFactory().getBean(getTargetBeanName());}
}
public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSource, BeanFactoryAware, Serializable {@Nullableprivate String targetBeanName;@Nullableprivate volatile Class<?> targetClass;@Nullableprivate BeanFactory beanFactory;public void setTargetBeanName(String targetBeanName) {this.targetBeanName = targetBeanName;}public String getTargetBeanName() {Assert.state(this.targetBeanName != null, "Target bean name not set");return this.targetBeanName;}public void setTargetClass(Class<?> targetClass) {this.targetClass = targetClass;}@Overridepublic void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = beanFactory;}public BeanFactory getBeanFactory() {Assert.state(this.beanFactory != null, "BeanFactory not set");return this.beanFactory;}@Override@Nullablepublic Class<?> getTargetClass() {Class<?> targetClass = this.targetClass;if (targetClass != null) {return targetClass;}synchronized (this) {targetClass = this.targetClass;if (targetClass == null && this.beanFactory != null && this.targetBeanName != null) {targetClass = this.beanFactory.getType(this.targetBeanName);if (targetClass == null) {Object beanInstance = this.beanFactory.getBean(this.targetBeanName);targetClass = beanInstance.getClass();}this.targetClass = targetClass;}return targetClass;}}@Overridepublic boolean isStatic() {return false;}@Overridepublic void releaseTarget(Object target) throws Exception {// Nothing to do here.}
}

从上面这段源码中大家可以看到,SimpleBeanTargetSource 在使用的时候,需要传入 targetBeanName,也就是被代理的 bean 名称,还需要传入 Spring 容器 BeanFactory,这样,在每次需要被代理对象的时候去调用 getTarget 方法的时候,就直接从容器中查询出来目标 Bean。因此,被代理的对象到底是不是单例,就要看 Spring 容器返回的对象到底是不是单例!

小伙伴们要记着 SimpleBeanTargetSource 的特点,因为在下一篇文章中,松哥要和大家聊的 @Scope 注解的高级用法,就涉及到这一点了。

2.3 LazyInitTargetSource

LazyInitTargetSource 有点类似于 SimpleBeanTargetSource,也是从 Spring 容器中查找被代理的 Bean,不同的是,LazyInitTargetSource 具备延迟初始化的能力,也就是在第一次进行调用的时候才会去获取被代理对象:

public class LazyInitTargetSource extends AbstractBeanFactoryBasedTargetSource {@Nullableprivate Object target;@Overridepublic synchronized Object getTarget() throws BeansException {if (this.target == null) {this.target = getBeanFactory().getBean(getTargetBeanName());postProcessTargetObject(this.target);}return this.target;}protected void postProcessTargetObject(Object targetObject) {}}

好啦,其他的类我就不挨个说了,感兴趣的小伙伴可以自行查看,这一块的源码还是比较好理解的~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/97412.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

均值滤波算法及例程

均值滤波算法是一种简单的图像滤波方法&#xff0c;它使用一个固定大小的滤波器来平滑图像。该滤波器由一个矩形的窗口组成&#xff0c;窗口中的像素值取平均值作为中心像素的新值。以下是均值滤波算法的步骤&#xff1a; 定义滤波器的大小&#xff08;窗口大小&#xff09;&a…

【ES的优势和原理及分布式开发的好处与坏处】

文章目录 ES的优势及分布式开发的好处1.ES的优势1.1 优势概述1.2 相关问题1&#xff09;为什么需要 Elasticsearch&#xff1f;MySQL 不行吗&#xff1f;2&#xff09;SQL检索的问题&#xff1a;3&#xff09;ES检索快的原理 2.分布式开发的好处与坏处 ES的优势及分布式开发的好…

09.selenium入门

Selenium采集数据 ​ Selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),[Mozilla Chrome](https://baike.baidu.com/item/Mozilla Chrome/3504923),Safari,Google Chrome,Ope…

1200*C. Challenging Cliffs(模拟构造贪心)

Problem - 1537C - Codeforces Challenging Cliffs - 洛谷 解析&#xff1a; 排序数组&#xff0c;然后找出间隔最短的两个相邻的数 a&#xff0c;b&#xff0c;c&#xff0c;d&#xff0c;e&#xff0c;f &#xff08;假设b&#xff0c;c为差最小的两个数&#xff09;。 然后…

BUGKU-simple_SSTI_1漏洞注入

SSTI漏洞注入 SSTI全称Server side template injection.服务端模板注入这节课主要讲flask的模板注入.flask会把类似于 的变量当做参数来渲染并填充到web页面,如果该参数可控并被后台解析则有可能被注入恶意代码导致注入漏洞请注意 模板注入只会存在于二次渲染中,无二次渲染不会…

FPGA 芯片点亮标准?

芯片设计完成&#xff0c;给到工厂制造&#xff0c;封装后回来&#xff0c;要经过最重要的一个点亮的环节&#xff0c;你知道什么叫做点亮吗&#xff1f; 其实&#xff0c;什么样叫做点亮&#xff0c;每家公司有每家的标准&#xff0c;本着自已不为难自已的原则&#xff0c;一…

代码随想录二刷day55

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣392. 判断子序列二、力扣115. 不同的子序列 前言 一、力扣392. 判断子序列 class Solution {public boolean isSubsequence(String s, String t) {int…

Kotlin中的泛型理解与应用

前言 泛型是一种在编程中用于增加代码的灵活性和重用性的概念。它允许我们编写可以在多种类型上工作的通用代码&#xff0c;而不需要为每种类型都编写不同的代码。 泛型应用 在Kotlin中&#xff0c;泛型可以应用于类、函数和接口等。下面是一些关于泛型在Kotlin中的理解和示例…

详解TCP三次握手(建立连接)和四次握手(释放连接)

TCP是是一个面向连接的协议&#xff0c;无论哪一方发送数据之前&#xff0c;都必须在双方之间建立一条连接。 一、建立连接 建立TCP连接时&#xff0c;要经历这样的流程&#xff1a; ① 请求端(通常称为客户)发送一个SYN段指明客户打算连接的服务器的端口&#xff0c;以及初始…

【kubernetes】k8s中的选主机制

leader-election选主机制 1 为什么需要leader-election&#xff1f; 在集群中存在某种业务场景&#xff0c;一批相同功能的进程同时运行&#xff0c;但是同一时刻&#xff0c;只能有一个工作&#xff0c;只有当正在工作的进程异常时&#xff0c;才会由另一个进程进行接管。这…

git全局与单仓库的密码管理

概要 在使用git时&#xff0c;有一个默认的全局配置&#xff0c;每个仓库也有自己的配置&#xff0c;在使用时常常傻傻分不清楚&#xff0c;现在进行一个简单的整理记录&#xff0c;一般情况下全局配置中的git账号和邮箱通常设置成自己的&#xff0c;其他仓库再根据项目需要进…

发现很多人分不清 jwt session token 的区别?

1. JWT&#xff08;JSON Web Token&#xff09; 1.1 什么是JWT&#xff1f; JWT&#xff0c;全称为JSON Web Token&#xff0c;是一种用于在网络上安全传输信息的开放标准。它的设计初衷是用于跨域通信&#xff0c;在不同域之间传递声明性信息。JWT是一种自包含的令牌&#x…

猜数字游戏(Python)

一、猜数字游戏是一个古老的密码破译类、益智类小游戏&#xff0c;通常由两个人参与&#xff0c;一个人设置一个数字&#xff0c;一个人猜数字&#xff0c;当猜数字的人说出一个数字&#xff0c;由出数字的人告知是否猜中&#xff1a;若猜测的数字大于设置的数字&#xff0c;出…

《软件方法(下)》第8章2023版连载(05)关于实体类

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 8.2 建模步骤C-1 识别类和属性 8.2.2 三种分析类 8.2.2.2 关于边界类 边界类的责任是接受输入、提供输出以及做简单的过滤。 图8-20中提到边界类的映射方法——每个有接口的外系统…

C#WPF标记扩展应用实例

本文介绍C#WPF标记扩展应用实例 一、标记扩展 标记扩展是一个 XAML 语言概念。 用于提供特性语法的值时,大括号({ 和 })表示标记扩展用法。 此用法指示 XAML 处理不要像通常那样将特性值视为文本字符串或者可转换为字符串的值。就是类似于值用变量的意思。 WPF 应用编程中…

sheng的学习笔记-【中文】【吴恩达课后测验】Course 1 - 神经网络和深度学习 - 第一周测验

课程1_第1周_测验题 目录&#xff1a;目录 第一题 1.“人工智能是新电力” 这个比喻指的是什么&#xff1f; A. 【  】人工智能为我们的家庭和办公室的个人设备供电&#xff0c;类似于电力。 B. 【  】通过“智能电网”&#xff0c;人工智能正在传递新一波的电力。 C. …

报考浙江工商大学2024年工商管理硕士(MBA)联考指南

1. 预报名时间&#xff1a;2023年9月24日-27日每天09&#xff1a;00-22&#xff1a;00 2. 正式报名时间&#xff1a;2023年10月8日-25日每天09&#xff1a;00-22&#xff1a;00 3. 浙江省网上确认&#xff08;现场确认&#xff09;时间&#xff1a;2023年10月31日-11月4日17&…

cvpr24写作模板pdfLaTex编译器注意点小结

文章目录 1 更改作者显示 Anonymous CVPR submission2 \label标签3 换行符// 与换列符&4 \medskip5 首行缩进6 插入图片6.1 单幅图片6.2 并排显示\hfill Reference https://cvpr.thecvf.com/Conferences/2024 1 更改作者显示 Anonymous CVPR submission 这一行开头加上% …

超全整理,Jmeter接口性能测试-Beanshell调用jar包加密(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、添加beanshell…

【2023集创赛】平头哥杯一等奖作品:基于无剑100开源SoC平台构建双核TEE安全系统

本文为2023年第七届全国大学生集成电路创新创业大赛&#xff08;“集创赛”&#xff09;平头哥杯一等奖作品分享&#xff0c;参加极术社区的【有奖征集】分享你的2023集创赛作品&#xff0c;秀出作品风采&#xff0c;分享2023集创赛作品扩大影响力&#xff0c;更有丰富电子礼品…