Springboot源码 TargetSource解析
摘要:
其实我第一次看见这个东西的时候也是不解,代理目标源不就是一个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讲解了其使用方式。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。