前言
在Spring框架中,TargetSource是一个接口,用于封装获取目标对象(也就是被代理的对象)的逻辑。它的主要作用是提供代理对象使用的目标对象,并且允许在运行时动态地切换目标对象。TargetSource在Spring的AOP(面向切面编程)中非常重要,它定义了代理对象的目标是谁以及在何时获取目标对象。
具体来说,TargetSource接口定义了以下两个方法:
-
getTarget():这个方法用于获取目标对象。AOP代理对象在执行方法时,会先调用getTarget()方法获取目标对象,然后再在目标对象上执行相应的方法。
-
releaseTarget(Object target):这个方法用于释放目标对象。在一些特殊的场景中,可能需要手动释放目标对象,以便它可以被垃圾回收或者被缓存起来以供下次使用。
-
isStatic() 方法:该方法用于指示目标对象是否是静态的。如果目标对象是静态的,那么TargetSource可以在代理创建时确定代理对象的类型,从而进行更好的性能优化。如果目标对象是动态的(例如在运行时根据条件切换不同的实例),则返回false。
-
getTargetClass() 方法: 该方法返回代理的目标对象的类类型。在使用基于接口的代理(例如JDK动态代理)时,这个方法返回接口的类型。在使用基于类的代理(例如CGLIB代理)时,这个方法返回目标对象的类型。这个信息通常用于确定代理的类型。
TargetSource的作用体现在以下几个方面:
-
AOP代理: 在AOP中,TargetSource允许你控制代理对象的目标对象是谁。例如,你可以创建一个TargetSource实现,根据特定条件选择不同的目标对象。这种灵活性使得你能够动态地选择在代理对象上应用哪些切面逻辑。
-
性能优化: 通过TargetSource,你可以实现一些性能优化的技术,比如对象池(Object Pooling)。你可以将对象缓存起来,而不是每次都创建新的对象,从而减少了对象的创建和销毁开销。
-
多数据源切换: 在一些应用中,可能需要根据不同的请求选择不同的数据源。通过自定义的TargetSource,你可以根据请求动态切换目标数据库。
-
状态管理和缓存: TargetSource可以用于状态管理和缓存策略。你可以在getTarget()方法中加入状态管理逻辑,确保每次获取到的目标对象都是处于特定状态的。同时,也可以在这里实现缓存逻辑,将经常使用的对象缓存起来,提高系统性能。
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 来管理目标对象的池化。
- 特点: 可以灵活地控制对象池的大小和行为,适用于需要对象池管理的场景。
区别和使用场景总结:
-
PrototypeTargetSource 和 SingletonTargetSource 分别适用于需要每次请求都创建新实例和整个应用程序生命周期内共享同一个实例的场景。
-
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。