当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2023-10-10 18:32:50 668观看
导读今天我们来思考这样一个问题:在 Spring AOP 中,被代理的对象是单例的吗?当我们每次获取到代理对象的时候,都会重新获取一个新的被代理对象吗?还是被代理的对象始终是同一个?为什么要思考这个问题,因为在松哥接下来要讲的 @Sc

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

为什么要思考这个问题,因为在松哥接下来要讲的 @Scope 注解高级用法中涉及到这个知识点。nqu28资讯网——每日最新资讯28at.com

1. 问题呈现

假设我有如下一个计算器接口:nqu28资讯网——每日最新资讯28at.com

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

然后给这个接口提供一个实现类:nqu28资讯网——每日最新资讯28at.com

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

现在假设我要生成一个代理对象,利用编程式的方式,代码如下:nqu28资讯网——每日最新资讯28at.com

ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(new CalculatorImpl());proxyFactory.addInterface(ICalculator.class);proxyFactory.addAdvice(new MethodInterceptor() {    @Override    public 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);

这里几个方法应该都好理解:nqu28资讯网——每日最新资讯28at.com

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

最终打印结果如下:nqu28资讯网——每日最新资讯28at.com

图片图片nqu28资讯网——每日最新资讯28at.com

这是一个简单的 AOP 案例。nqu28资讯网——每日最新资讯28at.com

现在我们的问题在于 setTarget 方法上。nqu28资讯网——每日最新资讯28at.com

我们点进来到 setTarget 方法上看一下这个方法做了什么:nqu28资讯网——每日最新资讯28at.com

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

小伙伴们看到,setTarget 方法内部调用了 setTargetSource 方法,这个方法设置了一个 SingletonTargetSource 来作为 targetSource,从名字上就能看出来,这个 SingletonTargetSource 是一个单例的 targetSource。nqu28资讯网——每日最新资讯28at.com

因此,对于上面的代码,我们可以推断,多个不同的代理对象中持有的相同的被代理对象,例如下面这段代码:nqu28资讯网——每日最新资讯28at.com

ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(new CalculatorImpl());proxyFactory.addInterface(ICalculator.class);proxyFactory.addAdvice(new MethodInterceptor() {    @Override    public 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 两个代理对象,但是实际上,这两个代理对象中持有的是同一个被代理对象,如下图:nqu28资讯网——每日最新资讯28at.com

图片图片nqu28资讯网——每日最新资讯28at.com

从这张图可以看出,代理对象不是同一个,但是被代理对象其实是同一个。nqu28资讯网——每日最新资讯28at.com

2. TargetSource

在 Spring AOP 中,否则处理代理对象的接口是 TargetSource,TargetSource 有诸多实现类,不同实现类具备不同的能力:nqu28资讯网——每日最新资讯28at.com

图片图片nqu28资讯网——每日最新资讯28at.com

很多实现类单纯从名字上就能看出来其特点了。nqu28资讯网——每日最新资讯28at.com

我们先来看下 TargetSource 接口:nqu28资讯网——每日最新资讯28at.com

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

这个接口一共是四个方法:nqu28资讯网——每日最新资讯28at.com

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

TargetSource 的实现类比较多,我们来看几个典型的实现类。nqu28资讯网——每日最新资讯28at.com

2.1 SingletonTargetSource

先来看这个类的定义:nqu28资讯网——每日最新资讯28at.com

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; } @Override public Class<?> getTargetClass() {  return this.target.getClass(); } @Override public Object getTarget() {  return this.target; } @Override public void releaseTarget(Object target) {  // nothing to do } @Override public boolean isStatic() {  return true; }}

如果被代理的对象是单例的,那么我们就会选择使用 SingletonTargetSource,被代理的对象总是在 getTarget 方法中被调用,然而这个方法返回的总是同一个对象,所以最终被代理的对象就是单例的。nqu28资讯网——每日最新资讯28at.com

同时,由于被代理对象是单例的,因此 isStatic 方法返回 true,releaseTarget 中不需要额外操作。nqu28资讯网——每日最新资讯28at.com

2.2 SimpleBeanTargetSource

SimpleBeanTargetSource 比较典型,这个是每当需要的时候,就去 Spring 容器中查找相应的被代理的 Bean,至于这个被代理的 Bean 是否为单例,就由 Spring 容器来控制了:nqu28资讯网——每日最新资讯28at.com

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource { @Override public Object getTarget() throws Exception {  return getBeanFactory().getBean(getTargetBeanName()); }}public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSource, BeanFactoryAware, Serializable { @Nullable private String targetBeanName; @Nullable private volatile Class<?> targetClass; @Nullable private 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; } @Override public void setBeanFactory(BeanFactory beanFactory) {  this.beanFactory = beanFactory; } public BeanFactory getBeanFactory() {  Assert.state(this.beanFactory != null, "BeanFactory not set");  return this.beanFactory; } @Override @Nullable public 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;  } } @Override public boolean isStatic() {  return false; } @Override public void releaseTarget(Object target) throws Exception {  // Nothing to do here. }}

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

小伙伴们要记着 SimpleBeanTargetSource 的特点,因为在下一篇文章中,松哥要和大家聊的 @Scope 注解的高级用法,就涉及到这一点了。nqu28资讯网——每日最新资讯28at.com

2.3 LazyInitTargetSource

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

public class LazyInitTargetSource extends AbstractBeanFactoryBasedTargetSource { @Nullable private Object target; @Override public 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) { }}

好啦,其他的类我就不挨个说了,感兴趣的小伙伴可以自行查看,这一块的源码还是比较好理解的~nqu28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-12767-0.htmlSpring AOP 中被代理的对象一定是单例吗?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Promise 和 Async/Await的区别

下一篇: 亚马逊计划向韩国云计算基础设施再投资58.5亿美元

标签:
  • 热门焦点
  • 6月安卓手机性能榜:vivo/iQOO霸占旗舰排行榜前三

    2023年上半年已经正式过去了,我们也迎来了安兔兔V10版本,在新的骁龙8Gen3和天玑9300发布之前,性能榜的榜单大体会以骁龙8Gen2和天玑9200+为主,至于那颗3.36GHz的骁龙8Gen2领先
  • JavaScript 混淆及反混淆代码工具

    介绍在我们开始学习反混淆之前,我们首先要了解一下代码混淆。如果不了解代码是如何混淆的,我们可能无法成功对代码进行反混淆,尤其是使用自定义混淆器对其进行混淆时。什么是混
  • 之家push系统迭代之路

    前言在这个信息爆炸的互联网时代,能够及时准确获取信息是当今社会要解决的关键问题之一。随着之家用户体量和内容规模的不断增大,传统的靠"主动拉"获取信息的方式已不能满足用
  • 三万字盘点 Spring 九大核心基础功能

    大家好,我是三友~~今天来跟大家聊一聊Spring的9大核心基础功能。话不多说,先上目录:图片友情提示,本文过长,建议收藏,嘿嘿嘿!一、资源管理资源管理是Spring的一个核心的基础功能,不
  • 使用LLM插件从命令行访问Llama 2

    最近的一个大新闻是Meta AI推出了新的开源授权的大型语言模型Llama 2。这是一项非常重要的进展:Llama 2可免费用于研究和商业用途。(几小时前,swyy发现它已从LLaMA 2更名为Lla
  • 得物宠物生意「狂飙」,发力“它经济”

    作者|花花小萌主近日,得物宣布正式上线宠物鉴别,通过得物App内的&ldquo;在线鉴别&rdquo;,可找到鉴别宠物的选项。通过上传自家宠物的部位细节,就能收获拥有专业资质认证的得物鉴
  • 猿辅导与新东方的两种“归途”

    作者|卓心月 出品|零态LT(ID:LingTai_LT)如何成为一家伟大企业?答案一定是对&ldquo;势&rdquo;的把握,这其中最关键的当属对企业战略的制定,且能够站在未来看现在,即使这其中的
  • 小米公益基金会捐赠2500万元驰援北京、河北暴雨救灾

    8月2日消息,今日小米科技创始人雷军在其微博上发布消息称,小米公益基金会宣布捐赠2500万元驰援北京、河北暴雨救灾。携手抗灾,京冀安康!以下为公告原文
  • AI艺术欣赏体验会在上海梅赛德斯奔驰中心音乐俱乐部上演

    光影交错的镜像世界,虚实幻化的视觉奇观,虚拟偶像与真人共同主持,这些场景都出现在2019世界人工智能大会的舞台上。8月29日至31日,“AI艺术欣赏体验会”在上海
Top