溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Springboot源碼 TargetSource解析

發(fā)布時(shí)間:2020-10-18 09:34:05 來(lái)源:腳本之家 閱讀:178 作者:TheGir1 欄目:編程語(yǔ)言

摘要:

其實(shí)我第一次看見(jiàn)這個(gè)東西的時(shí)候也是不解,代理目標(biāo)源不就是一個(gè)class嘛還需要封裝干嘛。。。

其實(shí)proxy代理的不是target,而是TargetSource,這點(diǎn)非常重要,一定要分清楚?。?!

通常情況下,一個(gè)代理對(duì)象只能代理一個(gè)target,每次方法調(diào)用的目標(biāo)也是唯一固定的target。但是,如果讓proxy代理TargetSource,可以使得每次方法調(diào)用的target實(shí)例都不同(當(dāng)然也可以相同,這取決于TargetSource實(shí)現(xiàn))。這種機(jī)制使得方法調(diào)用變得靈活,可以擴(kuò)展出很多高級(jí)功能,如:單利,原型,本地線(xiàn)程,目標(biāo)對(duì)象池、運(yùn)行時(shí)目標(biāo)對(duì)象熱替換目標(biāo)源等等。

Springboot源碼 TargetSource解析

Spring內(nèi)置的TargetSource

SingletonTargetSource

 public class SingletonTargetSource implements TargetSource, Serializable {
 
 /** Target cached and invoked using reflection. */
 private final Object target;
 //省略無(wú)關(guān)代碼......
 @Override
 public Object getTarget() {
  return this.target;
 }
 //省略無(wú)關(guān)代碼......
 }

從這個(gè)目標(biāo)源取得的目標(biāo)對(duì)象是單例的,成員變量target緩存了目標(biāo)對(duì)象,每次getTarget()都是返回這個(gè)對(duì)象。

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);
 }
 //省略無(wú)關(guān)代碼......
 }

每次getTarget()將生成prototype類(lèi)型的bean,即其生成的bean并不是單例的,因而使用這個(gè)類(lèi)型的TargetSource時(shí)需要注意,封裝的目標(biāo)bean必須是prototype類(lèi)型的。

PrototypeTargetSource繼承了AbstractBeanFactoryBasedTargetSource擁有了創(chuàng)建bean的能力。

 public abstract class AbstractPrototypeBasedTargetSource extends AbstractBeanFactoryBasedTargetSource {
 
 //省略無(wú)關(guān)代碼......
 /**
 * 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);
  }
  }
 }
 
 //省略無(wú)關(guān)代碼......
 
 }

可以看到,PrototypeTargetSource的生成prototype類(lèi)型bean的方式主要是委托給BeanFactory進(jìn)行的,因?yàn)?code>BeanFactory自有一套生成prototype類(lèi)型的bean的邏輯,因而PrototypeTargetSource也就具有生成prototype類(lèi)型bean的能力,這也就是我們要生成的目標(biāo)bean必須聲明為prototype類(lèi)型的原因。

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<>();
 
 //省略無(wú)關(guān)代碼......
 /**
 * 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();
 }
 //省略無(wú)關(guān)代碼......
 }

ThreadLocalTargetSource也就是和線(xiàn)程綁定的TargetSource,可以理解,其底層實(shí)現(xiàn)必然使用的是ThreadLocal。既然使用了ThreadLocal,也就是說(shuō)我們需要注意兩個(gè)問(wèn)題:

目標(biāo)對(duì)象必須聲明為prototype類(lèi)型,因?yàn)槊總€(gè)線(xiàn)程都會(huì)持有一個(gè)不一樣的對(duì)象;
目標(biāo)對(duì)象必須是無(wú)狀態(tài)的,因?yàn)槟繕?biāo)對(duì)象是和當(dāng)前線(xiàn)程綁定的,而Spring是使用的線(xiàn)程池處理的請(qǐng)求,因而每個(gè)線(xiàn)程可能處理不同的請(qǐng)求,因而為了避免造成問(wèn)題,目標(biāo)對(duì)象必須是無(wú)狀態(tài)的。

實(shí)現(xiàn)自定義的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 {
 
 }
 }

實(shí)現(xiàn)自定義TargetSource主要有兩個(gè)點(diǎn)要注意,一個(gè)是getTarget()方法,該方法中需要實(shí)現(xiàn)獲取目標(biāo)對(duì)象的邏輯,另一個(gè)是isStatic()方法,這個(gè)方法告知Spring是否需要緩存目標(biāo)對(duì)象,在非單例的情況下一般是返回false。

小結(jié)

本文主要首先講解了Spring是如果在源碼層面支持TargetSource的,然后講解了TargetSource的使用原理,接著對(duì)Spring提供的常見(jiàn)`TargetSource`進(jìn)行了講解,最后使用一個(gè)自定義的TargetSource講解了其使用方式。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI