SpringAOP源码解析之TargetSource(四)

前言

在Spring框架中,TargetSource是一个接口,用于封装获取目标对象(也就是被代理的对象)的逻辑。它的主要作用是提供代理对象使用的目标对象,并且允许在运行时动态地切换目标对象。TargetSource在Spring的AOP(面向切面编程)中非常重要,它定义了代理对象的目标是谁以及在何时获取目标对象。

具体来说,TargetSource接口定义了以下两个方法:

  1. getTarget():这个方法用于获取目标对象。AOP代理对象在执行方法时,会先调用getTarget()方法获取目标对象,然后再在目标对象上执行相应的方法。

  2. releaseTarget(Object target):这个方法用于释放目标对象。在一些特殊的场景中,可能需要手动释放目标对象,以便它可以被垃圾回收或者被缓存起来以供下次使用。

  3. isStatic() 方法:该方法用于指示目标对象是否是静态的。如果目标对象是静态的,那么TargetSource可以在代理创建时确定代理对象的类型,从而进行更好的性能优化。如果目标对象是动态的(例如在运行时根据条件切换不同的实例),则返回false。

  4. getTargetClass() 方法: 该方法返回代理的目标对象的类类型。在使用基于接口的代理(例如JDK动态代理)时,这个方法返回接口的类型。在使用基于类的代理(例如CGLIB代理)时,这个方法返回目标对象的类型。这个信息通常用于确定代理的类型。

TargetSource的作用体现在以下几个方面:

  • AOP代理: 在AOP中,TargetSource允许你控制代理对象的目标对象是谁。例如,你可以创建一个TargetSource实现,根据特定条件选择不同的目标对象。这种灵活性使得你能够动态地选择在代理对象上应用哪些切面逻辑。

  • 性能优化: 通过TargetSource,你可以实现一些性能优化的技术,比如对象池(Object Pooling)。你可以将对象缓存起来,而不是每次都创建新的对象,从而减少了对象的创建和销毁开销。

  • 多数据源切换: 在一些应用中,可能需要根据不同的请求选择不同的数据源。通过自定义的TargetSource,你可以根据请求动态切换目标数据库。

  • 状态管理和缓存: TargetSource可以用于状态管理和缓存策略。你可以在getTarget()方法中加入状态管理逻辑,确保每次获取到的目标对象都是处于特定状态的。同时,也可以在这里实现缓存逻辑,将经常使用的对象缓存起来,提高系统性能。

官方TargetSource地址

Spring中TargetSource的默认实现

PrototypeTargetSource、SingletonTargetSource、SimpleBeanTargetSource、ThreadLocalTargetSource 和 CommonsPool2TargetSource 都是 Spring 框架中的 TargetSource 实现,它们分别适用于不同的场景,具有不同的特点和用途。

1. PrototypeTargetSource:

  • 作用: PrototypeTargetSource 用于原型(prototype)的 Bean,每次请求时都会创建一个新的实例。
  • 特点: 适用于需要在每次请求时都创建新实例的目标对象,保持状态隔离。

2. SingletonTargetSource:

  • 作用: SingletonTargetSource 用于单例的 Bean,整个应用程序生命周期内只有一个实例。
  • 特点: 适用于状态无关的、可共享的目标对象。

3. SimpleBeanTargetSource:

  • 作用: SimpleBeanTargetSource 用于简单的 Bean 对象,通常是指那些没有接口或者特殊要求的类。
  • 特点: 将普通的 Java 对象转化为 Spring AOP 可以处理的代理对象。

4. ThreadLocalTargetSource:

  • 作用: ThreadLocalTargetSource 将目标对象保存在 ThreadLocal 中,使得每个线程都有自己的目标对象实例。
  • 特点: 适用于多线程环境下,每个线程都拥有独立的目标对象实例。

5. CommonsPool2TargetSource:

  • 作用: CommonsPool2TargetSource 使用 Apache Commons Pool 2 来管理目标对象的池化。
  • 特点: 可以灵活地控制对象池的大小和行为,适用于需要对象池管理的场景。

区别和使用场景总结:

  • PrototypeTargetSourceSingletonTargetSource 分别适用于需要每次请求都创建新实例和整个应用程序生命周期内共享同一个实例的场景。

  • SimpleBeanTargetSource 适用于没有接口和特殊要求的普通 Java 对象。

  • ThreadLocalTargetSource 适用于多线程环境,需要每个线程都有独立目标对象实例的场景。

  • CommonsPool2TargetSource 适用于需要对象池管理的场景,可以灵活地控制对象池的行为,比如最大池大小、最小池大小等。

普通的 AOP 代理创建使用的 TargetSource

日常工作中使用Spring AOP 代理 bean 基本都会走 AutoProxyCreator#wrapIfNecessary() 来进行创建。
它使用的 TargetSource 是 SingletonTargetSource,也就是每次 SingletonTargetSource#getTarget() 都返回的是同一个对象。

在这里插入图片描述

TargetSource#isStatic()

在Spring AOP中,TargetSource可以分为“静态”和“动态”两类,这两者的区别在于代理对象的目标对象是在代理创建时就确定好(静态)还是在运行时动态确定(动态)。

1. 静态TargetSource:

  • 目标对象确定性: 静态TargetSource在代理对象创建时,目标对象就已经确定了,它不会随着时间或者特定条件的改变而改变。通常,静态TargetSource的目标对象是单例(Singleton)的,即整个应用程序生命周期内只有一个实例。
  • 作用: 静态TargetSource适用于那些在整个应用程序中共享同一个实例的目标对象,这种目标对象通常是无状态的、可共享的,例如服务类、工具类等。使用静态TargetSource可以带来性能上的优势,因为代理对象只需要在创建时确定一次目标对象即可。

2. 动态TargetSource:

  • 目标对象的动态性: 动态TargetSource在代理对象创建后,可以在运行时动态地改变代理对象的目标对象。这种改变可以基于特定的条件、用户权限、请求内容等动态决定。动态TargetSource的目标对象可以随着时间、请求、或者其他因素的改变而改变。
  • 作用: 动态TargetSource适用于那些需要根据特定条件或者动态情况来确定目标对象的场景。例如,在多数据源切换时,根据用户的请求或者业务逻辑需要选择不同的数据库连接。动态TargetSource允许在运行时动态选择代理的目标对象。

作用比较:

  • 静态TargetSource适用于那些整个应用程序生命周期内只需要一个实例的目标对象,比如无状态的服务类。

  • 动态TargetSource适用于那些需要根据条件动态选择目标对象的场景,比如根据用户权限、请求内容等动态选择不同的实例。

选择静态还是动态的TargetSource取决于你的应用需求。如果你的目标对象是无状态的、可共享的,使用静态TargetSource可以带来性能上的优势。如果你的目标对象是有状态的、需要根据条件动态切换的,使用动态TargetSource可以实现更灵活的代理目标对象选择逻辑。

在Spring AOP中,CglibAopProxy和JdkDynamicAopProxy是两种不同的AOP代理方式,分别使用CGLIB(Code Generation Library)和JDK动态代理技术来创建代理对象。这两者在处理isStatic()方法时有所不同。

3. CglibAopProxy:

在CglibAopProxy中,StaticUnadvisedExposedInterceptor 和 DynamicUnadvisedExposedInterceptor 是两个不同的拦截器类。它们分别用于处理静态代理和动态代理。

StaticUnadvisedExposedInterceptor:

StaticUnadvisedExposedInterceptor 主要用于处理基于接口的静态代理,它的实例被用于拦截那些实现了接口的目标对象。这个拦截器的构造函数接受一个目标对象作为参数,并且这个目标对象是在拦截器创建时就已经确定的,是静态的,不会在运行时改变。

new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget())

在这里,this.advised.getTargetSource().getTarget() 是一个静态的目标对象,它在代理创建时就已经确定,不会在后续的运行时动态改变。

DynamicUnadvisedExposedInterceptor:

DynamicUnadvisedExposedInterceptor 用于处理CGLIB或者其他动态代理方式创建的代理对象。这个拦截器的构造函数接受一个TargetSource作为参数,TargetSource可以动态地确定目标对象。DynamicUnadvisedExposedInterceptor 的目标对象是动态的,它可能会在运行时根据条件或者其他因素改变。

new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource())

在这里,this.advised.getTargetSource() 返回的是一个TargetSource对象,这个TargetSource可能会根据特定的条件在运行时切换目标对象。因此,DynamicUnadvisedExposedInterceptor 用于处理动态代理,目标对象可能在运行时发生改变。

在这里插入图片描述

4. JdkDynamicAopProxy:

Jdk的isStatic的处理和cglib有点区别,Jdk Proxy每次调用会通过TargetSource#getTarget()获取目标对象。最后通过如果isStatic为false得时候会去releaseTarget。

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring AOPSpring框架的一个重要模块,它提供了一种面向切面编程的方式,可以让开发者将一些通用的、横切的关注点(如事务、安全、缓存等)从业务逻辑剥离出来,使得业务逻辑更加清晰简洁,代码复用更加方便。 Spring AOP的实现原理主要基于Java动态代理和CGLIB动态代理两种方式,其Java动态代理主要用于接口代理,而CGLIB动态代理则主要用于类代理Spring AOP的核心概念是切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)和织入(Weaving)。 在Spring AOP,切面是一个横向的关注点,它跨越多个对象和方法,通常包含一些通用的功能,如日志记录、安全控制等。连接点则是程序可以被切面拦截的特定点,如方法调用、异常抛出等。通知是切面在连接点执行前后所执行的动作,包括前置通知(Before)、后置通知(After)、异常通知(AfterThrowing)、返回通知(AfterReturning)和环绕通知(Around)。切点则是用来匹配连接点的规则,它可以指定哪些连接点会被切面拦截。织入则是将切面应用到目标对象的过程,它可以在编译时、类加载时、运行时等不同的阶段进行。 Spring AOP源码解析涉及到很多细节,包括代理的生成、通知的执行、切点的匹配等,需要深入了解Spring框架的内部实现和Java的反射机制。对于初学者而言,可以先从Spring AOP的基本概念和用法入手,了解其实现原理的同时,也可以通过调试和查看源码来加深理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thomas & Friends

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值