您好,登錄后才能下訂單哦!
摘要:
其實我第一次看見這個東西的時候也是不解,代理目標源不就是一個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講解了其使用方式。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。