返回

Spring5 AOP——TargetSource

发布时间:2023-01-10 18:50:23 280
# html# java# spring# java# 容器

TargetSource

TargetSource(目标源)是被代理的target(目标对象)实例的来源。

public interface TargetSource extends TargetClassAware {

	/**
	 * 返回当前目标源的目标类型
	 * 可以返回null值,如:EmptyTargetSource(未知类会使用这个目标源)
	 */
	@Override
	@Nullable
	Class getTargetClass();

	/**
	 * 当前目标源是否是静态的。
	 * 如果为false,则每次方法调用结束后会调用releaseTarget()释放目标对象.
	 * 如果为true,则目标对象不可变,也就没必要释放了。
	 */
	boolean isStatic();

	/**
	 * 获取一个目标对象。
	 * 在每次MethodInvocation方法调用执行之前获取。
	 */
	@Nullable
	Object getTarget() throws Exception;

	/**
	 * 释放指定的目标对象。
	 */
	void releaseTarget(Object target) throws Exception;

}

TargetSource(目标源)是被代理的target(目标对象)实例的来源。TargetSource被用于获取当前MethodInvocation(方法调用)所需要的target(目标对象),这个target通过反射的方式被调用(如:method.invode(target,args))。换句话说,proxy(代理对象)代理的不是target,而是TargetSource,这点非常重要!!!

 

那么问题来了:为什么SpringAOP代理不直接代理target,而需要通过代理TargetSource(target的来源,其内部持有target),间接代理target呢?

 

通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。

 

TargetSource组件本身与SpringIoC容器无关,换句话说,target的生命周期不一定是受spring容器管理的,我们以往的XML中的AOP配置,只是对受容器管理的bean而言的,我们当然可以手动创建一个target,同时使用Spring的AOP框架(而不使用IoC容器)

TargetSource实现类图

Spring内置的TargetSource

SingletonTargetSource

从这个目标源取得的目标对象是单例的,成员变量target缓存了目标对象,每次getTarget()都是返回这个对象。

public class SingletonTargetSource implements TargetSource, Serializable {

	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;
	}
}

PrototypeTargetSource

每次getTarget()将生成prototype类型的bean,即其生成的bean并不是单例的,因而使用这个类型的TargetSource时需要注意,封装的目标bean必须是prototype类型的。PrototypeTargetSource继承了AbstractBeanFactoryBasedTargetSource拥有了创建bean的能力。

public class PrototypeTargetSource extends AbstractPrototypeBasedTargetSource {

	/**
	 * Obtain a new prototype instance for every call.
	 * @see #newPrototypeInstance()
	 */
	@Override
	public Object getTarget() throws BeansException {
		return newPrototypeInstance();
	}

	/**
	 * Destroy the given independent instance.
	 * @see #destroyPrototypeInstance
	 */
	@Override
	public void releaseTarget(Object target) {
		destroyPrototypeInstance(target);
	}

	@Override
	public String toString() {
		return "PrototypeTargetSource for target bean with name '" + getTargetBeanName() + "'";
	}

}

可以看到,PrototypeTargetSource的生成prototype类型bean的方式主要是委托给BeanFactory进行的,因为BeanFactory自有一套生成prototype类型的bean的逻辑,因而PrototypeTargetSource也就具有生成prototype类型bean的能力,这也就是我们要生成的目标bean必须声明为prototype类型的原因。

CommonsPool2TargetSource

这里CommonsPool2TargetSource也就是池化的TargetSource,其基本具有平常所使用的“池”的概念的所有属性,比如:最小空闲数,最大空闲数,最大等待时间等等。实际上,CommonsPool2TargetSource的实现是将其委托给了ObjectPool进行,具体的也就是GenericObjectPool,其实现了ObjectPool接口。

public class CommonsPool2TargetSource extends AbstractPoolingTargetSource implements PooledObjectFactory {

	private int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;

	private int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;

	private long maxWait = GenericObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS;

	private long timeBetweenEvictionRunsMillis = GenericObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;

	private long minEvictableIdleTimeMillis = GenericObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;

	private boolean blockWhenExhausted = GenericObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED;


	@Nullable
	private ObjectPool pool;


	public CommonsPool2TargetSource() {
		setMaxSize(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL);
	}

	......
}

ThreadLocalTargetSource

ThreadLocalTargetSource也就是和线程绑定的TargetSource,可以理解,其底层实现必然使用的是ThreadLocal。既然使用了ThreadLocal,也就是说我们需要注意两个问题:

  • 目标对象必须声明为prototype类型,因为每个线程都会持有一个不一样的对象;
  • 目标对象必须是无状态的,因为目标对象是和当前线程绑定的,而Spring是使用的线程池处理的请求,因而每个线程可能处理不同的请求,因而为了避免造成问题,目标对象必须是无状态的。
public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
		implements ThreadLocalTargetSourceStats, DisposableBean {

	private final ThreadLocal targetInThread =
			new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");

	private final Set targetSet = new HashSet<>();

	private int invocationCount;

	private int hitCount;


	@Override
	public Object getTarget() throws BeansException {
		++this.invocationCount;
		Object target = this.targetInThread.get();
		if (target == null) {
			if (logger.isDebugEnabled()) {
				logger.debug("No target for prototype '" + getTargetBeanName() + "' bound to thread: " +
						"creating one and binding it to thread '" + Thread.currentThread().getName() + "'");
			}
			// Associate target with ThreadLocal.
			target = newPrototypeInstance();
			this.targetInThread.set(target);
			synchronized (this.targetSet) {
				this.targetSet.add(target);
			}
		}
		else {
			++this.hitCount;
		}
		return target;
	}

	@Override
	public void destroy() {
		logger.debug("Destroying ThreadLocalTargetSource bindings");
		synchronized (this.targetSet) {
			for (Object target : this.targetSet) {
				destroyPrototypeInstance(target);
			}
			this.targetSet.clear();
		}
		// Clear ThreadLocal, just in case.
		this.targetInThread.remove();
	}


	@Override
	public int getInvocationCount() {
		return this.invocationCount;
	}

	@Override
	public int getHitCount() {
		return this.hitCount;
	}

	@Override
	public int getObjectCount() {
		synchronized (this.targetSet) {
			return this.targetSet.size();
		}
	}

	public IntroductionAdvisor getStatsMixin() {
		DelegatingIntroductionInterceptor dii = new DelegatingIntroductionInterceptor(this);
		return new DefaultIntroductionAdvisor(dii, ThreadLocalTargetSourceStats.class);
	}

}

这里ThreadLocalTargetSource主要集成了AbstractPrototypeBasedTargetSource和DisposableBean。关于AbstractPrototypeBasedTargetSource前面已经讲过了,读者可以到前面翻看;而DisposableBean的作用主要是提供一个方法,以供给Spring在销毁当前对象的时候调用。也就是说Spring在销毁当前TargetSource对象的时候会首先销毁其生成的各个目标对象。这里需要注意的是,TargetSource和生成的目标对象是两个对象,前面讲的TargetSouce都是单例的,只是生成的目标对象可能是单例的,也可能是多例的。

HotSwappableTargetSource

HotSwappableTargetSource使用户可以以线程安全的方式切换目标对象,提供所谓的热交换功能。这个特性是很有用的,尽管它的开启需要AOP应用进行显式的配置,但配置并不复杂,在使用时,只需要把 HotSwappableargetSource配置到ProxyFactoryBean的Target属性就可以了,在需要更换真正的目标对象时,调用HotSwappableTargetSource的swap方法就可以完成。由此可见,对HotSwappableTargetSource的热交换功能的使用,是需要触发swap方法调用的。这个swap方法的实现很简单,它完成 target对象的替换,也就是说,它使用新的 target对象来替换原有的 target对象。为了保证线程安全,需要把这个替换方法设为 synchronized方法。

public class HotSwappableTargetSource implements TargetSource, Serializable {

	private static final long serialVersionUID = 7497929212653839187L;


	private Object target;


	public HotSwappableTargetSource(Object initialTarget) {
		Assert.notNull(initialTarget, "Target object must not be null");
		this.target = initialTarget;
	}


	@Override
	public synchronized Class getTargetClass() {
		return this.target.getClass();
	}

	@Override
	public final boolean isStatic() {
		return false;
	}

	@Override
	public synchronized Object getTarget() {
		return this.target;
	}

	@Override
	public void releaseTarget(Object target) {
		// nothing to do
	}


	public synchronized Object swap(Object newTarget) throws IllegalArgumentException {
		Assert.notNull(newTarget, "Target object must not be null");
		Object old = this.target;
		this.target = newTarget;
		return old;
	}


	@Override
	public boolean equals(Object other) {
		return (this == other || (other instanceof HotSwappableTargetSource &&
				this.target.equals(((HotSwappableTargetSource) other).target)));
	}

	@Override
	public int hashCode() {
		return HotSwappableTargetSource.class.hashCode();
	}

	@Override
	public String toString() {
		return "HotSwappableTargetSource for target: " + this.target;
	}
}

参考: https://www.cnblogs.com/nizuimeiabc1/p/12178235.html

https://blog.csdn.net/shenchaohao12321/article/details/85538163

https://www.cnblogs.com/qinzj/p/11415057.html

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线
下一篇
Spring5 AOP——@EnableAspectJAutoProxy 2023-01-10 18:07:50