Spring AOP TargetSource解析
Spring AOP TargetSource解析
Spring AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要模块,它允许开发者通过切面(Aspect)来实现横切关注点(Cross-cutting Concerns),如事务管理、日志记录、安全等。而TargetSource
是Spring AOP中的一个接口,用于确定AOP代理的目标对象(或称为目标Bean)。
TargetSource 介绍
TargetSource
接口定义了获取AOP代理的目标对象的方法。它的主要作用是提供对目标对象的访问,这个目标对象通常是被代理的实际业务对象。TargetSource
接口的定义如下:
public interface TargetSource {
Class<?> getTargetClass();
boolean isStatic();
Object getTarget() throws Exception;
void releaseTarget(Object target) throws Exception;
}
方法解析
-
getTargetClass()
- 返回目标对象的类型(Class)。
- 例如,如果代理的是一个
UserService
对象,这个方法会返回UserService.class
。
-
isStatic()
- 返回一个布尔值,指示目标对象是否是静态的。
- 如果目标对象是静态的(即每次调用都返回同一个实例),返回
true
;否则返回false
。
-
getTarget()
- 返回目标对象的实例。
- 每次调用此方法时,可能会返回一个新的实例(如果
isStatic()
返回false
),或者返回同一个实例(如果isStatic()
返回true
)。
-
releaseTarget(Object target)
- 释放目标对象。
- 当目标对象不再需要时调用,以便进行资源清理。
TargetSource的实现
Spring提供了几种TargetSource
的实现:
-
SingletonTargetSource
- 这是最常见的实现,表示目标对象是一个单例,即每次调用
getTarget()
时返回同一个实例。
- 这是最常见的实现,表示目标对象是一个单例,即每次调用
-
PrototypeTargetSource
- 每次调用
getTarget()
时返回一个新的目标对象实例,适用于目标对象是原型(Prototype)作用域的情况。
- 每次调用
-
ThreadLocalTargetSource
- 为每个线程提供一个独立的目标对象实例,使用
ThreadLocal
来存储目标对象。
- 为每个线程提供一个独立的目标对象实例,使用
-
CommonsPool2TargetSource
- 使用Apache Commons Pool 2来管理目标对象的池,支持对象池的各种特性,如对象的创建、销毁、验证等。
-
HotSwappableTargetSource
使用场景
TargetSource
的主要使用场景包括:
-
动态代理:在运行时动态决定目标对象,例如根据某些条件选择不同的实现。
-
资源管理:通过对象池管理资源,例如数据库连接池。
-
多实例管理:为每个线程或每个请求分配独立的对象实例,避免线程安全问题。
SingletonTargetSource示例
示例代码
以下是一个简单的示例,展示如何使用SingletonTargetSource
:
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.target.SingletonTargetSource;
public class TargetSourceExample {
public static void main(String[] args) {
// 创建目标对象
MyService target = new MyService();
// 创建SingletonTargetSource
TargetSource targetSource = new SingletonTargetSource(target);
// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(targetSource);
// 获取代理对象
MyService proxy = (MyService) proxyFactory.getProxy();
// 调用代理对象的方法
proxy.doSomething();
}
}
class MyService {
public void doSomething() {
System.out.println("Doing something...");
}
}
在这个示例中,SingletonTargetSource
确保每次调用代理对象的方法时都会使用同一个MyService
实例。
HotSwappableTargetSource示例
HotSwappableTargetSource
是Spring AOP中的一个特殊实现,允许在运行时动态地替换目标对象。这对于需要在不停止应用程序的情况下更新某些组件或服务的场景非常有用。典型的使用场景包括:
- 动态配置更新:在运行时替换配置对象,以便应用程序能够立即反映新的配置。
- 热部署:在不重启应用的情况下更新某个服务或组件的实现。
- A/B测试:在不同的实现之间切换,以便进行A/B测试或逐步发布新功能。
示例代码
以下是一个具体的示例,展示如何使用HotSwappableTargetSource
来动态替换目标对象:
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.target.HotSwappableTargetSource;
public class HotSwappableTargetSourceExample {
public static void main(String[] args) {
// 创建原始目标对象
MyService originalService = new MyServiceImpl1();
// 创建HotSwappableTargetSource并设置初始目标对象
HotSwappableTargetSource hotSwappableTargetSource = new HotSwappableTargetSource(originalService);
// 创建代理工厂并设置TargetSource
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(hotSwappableTargetSource);
// 获取代理对象
MyService proxy = (MyService) proxyFactory.getProxy();
// 调用代理对象的方法,使用原始目标对象
proxy.doSomething();
// 创建新的目标对象
MyService newService = new MyServiceImpl2();
// 动态替换目标对象
hotSwappableTargetSource.swap(newService);
// 获取新的代理对象
MyServiceInterface proxy2 = (MyServiceInterface) proxyFactory.getProxy();
// 调用代理对象的方法,使用新的目标对象
proxy2.doSomething();
}
}
interface MyService {
void doSomething();
}
class MyServiceImpl1 implements MyService {
@Override
public void doSomething() {
System.out.println("MyServiceImpl1 is doing something...");
}
}
class MyServiceImpl2 implements MyService {
@Override
public void doSomething() {
System.out.println("MyServiceImpl2 is doing something...");
}
}
解释
- 创建初始目标对象:
MyServiceImpl1
是初始的目标对象。 - 创建HotSwappableTargetSource:使用初始目标对象创建
HotSwappableTargetSource
。 - 创建代理对象:通过
ProxyFactory
创建代理对象,并设置TargetSource
为HotSwappableTargetSource
。 - 调用代理对象的方法:此时,代理对象的方法调用会委托给
MyServiceImpl1
。 - 替换目标对象:创建新的目标对象
MyServiceImpl2
,并使用hotSwappableTargetSource.swap(newService)
动态替换目标对象。 - 再次调用代理对象的方法:此时,代理对象的方法调用会委托给新的目标对象
MyServiceImpl2
。
通过这种方式,可以在运行时动态地替换目标对象,而无需重新创建代理对象或重启应用程序。这对于需要高可用性和动态更新的应用场景非常有用。
CommonsPool2TargetSource示例
CommonsPool2TargetSource
是Spring AOP中用于对象池管理的一个实现。它使用Apache Commons Pool 2库来管理目标对象的池化,适用于需要频繁创建和销毁对象的场景,通过对象池来提高性能和资源利用率。
示例代码
以下是一个使用CommonsPool2TargetSource
的示例,展示如何配置和使用对象池:
public class CommonsPool2TargetSourceExample {
public static void main(String[] args) {
// 使用 Spring 上下文加载配置
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取代理对象
MyServiceInterface2 proxy = (MyServiceInterface2) context.getBean("myServiceProxy");
// 调用代理对象的方法
for (int i = 0; i < 10; i++) {
proxy.doSomething();
}
// 关闭上下文
context.close();
}
}
@Configuration
class AppConfig {
@Bean
public CommonsPool2TargetSource commonsPool2TargetSource() {
CommonsPool2TargetSource targetSource = new CommonsPool2TargetSource();
targetSource.setTargetBeanName("myService");
targetSource.setMaxSize(5); // 设置池中最大对象数
return targetSource;
}
@Bean
@Scope("prototype")
public MyServiceInterface2 myService() {
// 此处应该是实例化比较消耗的资源的Bean
return new MyServiceImpl();
}
@Bean
public MyServiceInterface2 myServiceProxy(CommonsPool2TargetSource commonsPool2TargetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(commonsPool2TargetSource);
return (MyServiceInterface2) proxyFactory.getProxy();
}
}
// 目标对象接口
interface MyServiceInterface2 {
void doSomething();
}
// 目标对象实现
class MyServiceImpl implements MyServiceInterface2 {
@Override
public void doSomething() {
System.out.println("MyServiceImpl is doing something...");
}
}
ThreadLocalTargetSource示例
ThreadLocalTargetSource
是 Spring AOP 提供的一个 TargetSource
实现,它为每个线程提供一个独立的目标对象实例。这对于需要线程隔离的场景非常有用,例如每个线程都需要一个独立的会话或上下文对象。
使用场景
- 线程隔离的会话管理:每个线程有独立的会话对象,不会相互干扰。
- 线程本地变量:每个线程有独立的变量实例,避免线程安全问题。
- 线程独立的服务实例:每个线程有独立的服务实例,适用于需要在每个线程中保持独立状态的服务。
示例代码
以下是一个使用 ThreadLocalTargetSource
的示例,展示如何为每个线程提供独立的目标对象实例:
public class ThreadLocalTargetSourceExample {
public static void main(String[] args) {
// 使用 Spring 上下文加载配置
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig3.class);
// 获取代理对象
MyServiceInterface3 proxy = (MyServiceInterface3) context.getBean("myServiceProxy");
// 创建多个线程,验证每个线程有独立的目标对象实例
Runnable task = () -> {
System.out.println(Thread.currentThread().getName() + ": " + proxy.doSomething());
};
Thread thread1 = new Thread(task, "Thread-1");
Thread thread2 = new Thread(task, "Thread-2");
thread1.start();
thread2.start();
// 等待线程执行完毕
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 关闭上下文
context.close();
}
}
@Configuration
class AppConfig3 {
@Bean
public ThreadLocalTargetSource threadLocalTargetSource() {
ThreadLocalTargetSource targetSource = new ThreadLocalTargetSource();
targetSource.setTargetBeanName("myService");
targetSource.setTargetClass(MyServiceInterface3.class);
return targetSource;
}
@Bean
@Scope("prototype")
public MyServiceInterface3 myService() {
return new MyServiceImpl3();
}
@Bean
public MyServiceInterface3 myServiceProxy(ThreadLocalTargetSource threadLocalTargetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(threadLocalTargetSource);
proxyFactory.setInterfaces(MyServiceInterface3.class); // 设置代理接口
return (MyServiceInterface3) proxyFactory.getProxy();
}
}
// 目标对象接口
interface MyServiceInterface3 {
String doSomething();
}
// 目标对象实现
class MyServiceImpl3 implements MyServiceInterface3 {
private final String instanceId;
public MyServiceImpl3() {
this.instanceId = "Instance-" + System.currentTimeMillis();
}
@Override
public String doSomething() {
return "MyServiceImpl is doing something with " + instanceId;
}
}
解释
- 创建
ThreadLocalTargetSource
:使用ThreadLocalTargetSource
并设置目标类MyServiceImpl
。setTargetBeanName
用于指定目标 Bean 的名称(如果使用 Spring 容器管理 Bean),setTargetClass
用于指定目标类。 - 创建代理工厂:通过
ProxyFactory
创建代理对象,并设置TargetSource
为ThreadLocalTargetSource
。 - 获取代理对象:代理对象
proxy
会在每个线程中拥有独立的目标对象实例。 - 创建多个线程:通过创建多个线程来验证每个线程是否有独立的目标对象实例。每个线程调用
proxy.doSomething()
方法时,会使用独立的MyServiceImpl
实例。
线程独立的目标对象
在这个示例中,每个线程都会有一个独立的 MyServiceImpl
实例。通过 ThreadLocalTargetSource
,Spring AOP 确保了每个线程在调用代理对象方法时,使用的是属于该线程的目标对象实例,从而避免了线程安全问题。
此方式特别适合需要在每个线程中保持独立状态的服务,例如用户会话管理、线程本地缓存等应用场景。
总结
TargetSource
在Spring AOP中扮演着重要的角色,它决定了代理对象如何访问目标对象。通过不同的TargetSource
实现,可以灵活地管理目标对象的生命周期和实例化策略,从而满足各种复杂的应用需求。
转载自:https://juejin.cn/post/7375086929404575770