当前位置: 移动技术网 > IT编程>开发语言>Java > Springboot源码 TargetSource解析

Springboot源码 TargetSource解析

2019年09月07日  | 移动技术网IT编程  | 我要评论

燕莎奥特莱斯折扣店,布鲁塞尔天气,三级片片名

摘要:

其实我第一次看见这个东西的时候也是不解,代理目标源不就是一个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讲解了其使用方式。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网