摘要:
其实我第一次看见这个东西的时候也是不解, 代理目标源不就是一个 class 嘛还需要封装干嘛...
其实 proxy 代理的不是 target, 而是 TargetSource, 这点非常重要, 一定要分清楚!!!
通常情况下, 一个代理对象只能代理一个 target, 每次方法调用的目标也是唯一固定的 target. 但是, 如果让 proxy 代理 TargetSource, 可以使得每次方法调用的 target 实例都不同 (当然也可以相同, 这取决于 TargetSource 实现). 这种机制使得方法调用变得灵活, 可以扩展出很多高级功能, 如: 单利, 原型, 本地线程, 目标对象池, 运行时目标对象热替换目标源等等.
Spring 内置的 TargetSource
- SingletonTargetSource
- public class SingletonTargetSource implements TargetSource, Serializable {
- /** Target cached and invoked using reflection. */
- private final Object target;
- // 省略无关代码......
- @Override
- public Object getTarget() {
- return this.target;
- }
- // 省略无关代码......
- }
从这个目标源取得的目标对象是单例的, 成员变量 target 缓存了目标对象, 每次 getTarget() 都是返回这个对象.
- PrototypeTargetSource
- 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);
- }
- // 省略无关代码......
- }
每次 getTarget() 将生成 prototype 类型的 bean, 即其生成的 bean 并不是单例的, 因而使用这个类型的 TargetSource 时需要注意, 封装的目标 bean 必须是 prototype 类型的. PrototypeTargetSource 继承了 AbstractBeanFactoryBasedTargetSource 拥有了创建 bean 的能力.
- public abstract class AbstractPrototypeBasedTargetSource extends AbstractBeanFactoryBasedTargetSource {
- // 省略无关代码......
- /**
- * Subclasses should call this method to create a new prototype instance.
- * @throws BeansException if bean creation failed
- */
- protected Object newPrototypeInstance() throws BeansException {
- if (logger.isDebugEnabled()) {
- logger.debug("Creating new instance of bean'" + getTargetBeanName() + "'");
- }
- return getBeanFactory().getBean(getTargetBeanName());
- }
- /**
- * Subclasses should call this method to destroy an obsolete prototype instance.
- * @param target the bean instance to destroy
- */
- protected void destroyPrototypeInstance(Object target) {
- if (logger.isDebugEnabled()) {
- logger.debug("Destroying instance of bean'" + getTargetBeanName() + "'");
- }
- if (getBeanFactory() instanceof ConfigurableBeanFactory) {
- ((ConfigurableBeanFactory) getBeanFactory()).destroyBean(getTargetBeanName(), target);
- }
- else if (target instanceof DisposableBean) {
- try {
- ((DisposableBean) target).destroy();
- }
- catch (Throwable ex) {
- logger.warn("Destroy method on bean with name'" + getTargetBeanName() + "'threw an exception", ex);
- }
- }
- }
- // 省略无关代码......
- }
可以看到, PrototypeTargetSource 的生成 prototype 类型 bean 的方式主要是委托给 BeanFactory 进行的, 因为 BeanFactory 自有一套生成 prototype 类型的 bean 的逻辑, 因而 PrototypeTargetSource 也就具有生成 prototype 类型 bean 的能力, 这也就是我们要生成的目标 bean 必须声明为 prototype 类型的原因.
- ThreadLocalTargetSource
- public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
- implements ThreadLocalTargetSourceStats, DisposableBean {
- /**
- * ThreadLocal holding the target associated with the current
- * thread. Unlike most ThreadLocals, which are static, this variable
- * is meant to be per thread per instance of the ThreadLocalTargetSource class.
- */
- private final ThreadLocal<Object> targetInThread =
- new NamedThreadLocal<>("Thread-local instance of bean'" + getTargetBeanName() + "'");
- /**
- * Set of managed targets, enabling us to keep track of the targets we've created.
- */
- private final Set<Object> targetSet = new HashSet<>();
- // 省略无关代码......
- /**
- * Implementation of abstract getTarget() method.
- * We look for a target held in a ThreadLocal. If we don't find one,
- * we create one and bind it to the thread. No synchronization is required.
- */
- @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;
- }
- /**
- * Dispose of targets if necessary; clear ThreadLocal.
- * @see #destroyPrototypeInstance
- */
- @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();
- }
- // 省略无关代码......
- }
ThreadLocalTargetSource 也就是和线程绑定的 TargetSource, 可以理解, 其底层实现必然使用的是 ThreadLocal. 既然使用了 ThreadLocal, 也就是说我们需要注意两个问题:
目标对象必须声明为 prototype 类型, 因为每个线程都会持有一个不一样的对象;
目标对象必须是无状态的, 因为目标对象是和当前线程绑定的, 而 Spring 是使用的线程池处理的请求, 因而每个线程可能处理不同的请求, 因而为了避免造成问题, 目标对象必须是无状态的.
实现自定义的 TargetSource
- package com.GitHub.dqqzj.springboot.target;
- import org.springframework.aop.TargetSource;
- import org.springframework.util.Assert;
- import java.lang.reflect.Array;
- import java.util.concurrent.ThreadLocalRandom;
- import java.util.concurrent.atomic.AtomicInteger;
- /**
- * @author qinzhongjian
- * @date created in 2019-08-25 12:43
- * @description: TODO
- * @since JDK 1.8.0_212-b10z
- */
- public class DqqzjTargetSource implements TargetSource {
- private final AtomicInteger idx = new AtomicInteger();
- private final Object[] target;;
- public DqqzjTargetSource(Object[] target) {
- Assert.notNull(target, "Target object must not be null");
- this.target = target;
- }
- @Override
- public Class<?> getTargetClass() {
- return target.getClass();
- }
- @Override
- public boolean isStatic() {
- return false;
- }
- @Override
- public Object getTarget() throws Exception {
- return this.target[this.idx.getAndIncrement() & this.target.length - 1];
- }
- @Override
- public void releaseTarget(Object target) throws Exception {
- }
- }
实现自定义 TargetSource 主要有两个点要注意, 一个是 getTarget() 方法, 该方法中需要实现获取目标对象的逻辑, 另一个是 isStatic() 方法, 这个方法告知 Spring 是否需要缓存目标对象, 在非单例的情况下一般是返回 false.
小结
本文主要首先讲解了 Spring 是如果在源码层面支持 TargetSource 的, 然后讲解了 TargetSource 的使用原理, 接着对 Spring 提供的常见 `TargetSource` 进行了讲解, 最后使用一个自定义的 TargetSource 讲解了其使用方式.
来源: https://www.cnblogs.com/qinzj/p/11415057.html