在上文中(Spring Aop标签解析原理详解)咱们讲解了Spring是如何解析<aop:aspectj-autoproxy/>
标签,而且生成了一个AnnotationAwareAspectJAutoProxyCreator
的BeanDefinition
的。在Spring代理目标bean的时候,其并非直接建立一个目标bean的对象实例的,而是经过一个TargetSource
类型的对象将目标bean进行封装,Spring Aop获取目标对象始终是经过TargetSource.getTarget()
方法进行的。本文首先会讲解Spring Aop是如何封装目标对象到TargetSource
中的,而后会讲解TargetSource
各个方法的使用原理,接着会对Spring提供的常见的TargetSource
的实现类进行讲解,最后会讲解如何实现自定义的TargetSource
。java
咱们知道,Spring Aop标签解析的最终结果就是生成了一个AnnotationAwareAspectJAutoProxyCreator
的BeanDefinition
,咱们查看这个类的继承结构能够发现其实现了InstantiationAwareBeanPostProcessor
和BeanPostProcessor
两个接口,而且分别实现了下面两个方法:spring
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { @Nullable default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { return null; } }
public interface BeanPostProcessor { @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
这里省略了其他的不相关方法。上述第一个方法会在Spring实例化一个bean以前执行,若是这里第一个方法可以返回目标bean对象,那么这里就直接使用该对象,Spring不会继续生成目标bean对象,这种方式能够实现自定义的bean对象;而第二个方法会在Spring实例化一个bean以后执行,主要做用是对已经生成的bean进行必定的处理。这里AnnotationAwareAspectJAutoProxyCreator
对这两个方法都进行了重写,对于重写的第一个方法,其主要目的在于若是用户使用了自定义的TargetSource
对象,则直接使用该对象生成目标对象,而不会使用Spring的默认逻辑生成目标对象,而且这里会判断各个切面逻辑是否能够应用到当前bean上,若是能够,则直接应用,也就是说TargetSource
为使用者在Aop中提供了一个自定义生成目标bean逻辑的方式,而且会应用相应的切面逻辑。对于第二个方法,其主要做用在于Spring生成某个bean以后,将相关的切面逻辑应用到该bean上,这个方法在后续将会详细讲解。这里主要讲解第一方法的原理,以下是其实现源码:缓存
@Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); // 判断TargetSource缓存中是否包含当前bean,若是不包含,则判断当前bean是不是已经被代理的bean, // 若是代理过,则不对当前传入的bean进行处理,若是没代理过,则判断当前bean是否为系统bean,或者是 // 切面逻辑不会包含的bean,若是是,则将当前bean缓存到advisedBeans中,不然继续往下执行。 // 通过这一步的处理以后,只有在TargetSource中没有进行缓存,而且应该被切面逻辑环绕,可是目前还未 // 生成代理对象的bean才会经过此方法。 if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { return null; } if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } // 获取封装当前bean的TargetSource对象,若是不存在,则直接退出当前方法,不然从TargetSource // 中获取当前bean对象,而且判断是否须要将切面逻辑应用在当前bean上。 TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } // 获取可以应用当前bean的切面逻辑 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); // 根据切面逻辑为当前bean生成代理对象 Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); // 对生成的代理对象进行缓存 this.proxyTypes.put(cacheKey, proxy.getClass()); // 直接返回生成的代理对象,从而使后续bean的建立工做短路 return proxy; } return null; }
以下是TargetSource
接口的声明:多线程
public interface TargetSource extends TargetClassAware { // 本方法主要用于返回目标bean的Class类型 @Override @Nullable Class<?> getTargetClass(); // 这个方法用户返回当前bean是否为静态的,好比常见的单例bean就是静态的,而prototype就是动态的, // 这里这个方法的主要做用是,对于静态的bean,spring是会对其进行缓存的,在屡次使用TargetSource // 获取目标bean对象的时候,其获取的老是同一个对象,经过这种方式提升效率 boolean isStatic(); // 获取目标bean对象,这里能够根据业务须要进行自行定制 @Nullable Object getTarget() throws Exception; // Spring在完目标bean以后会调用这个方法释放目标bean对象,对于一些须要池化的对象,这个方法是必须 // 要实现的,这个方法默认不进行任何处理 void releaseTarget(Object target) throws Exception; }
经过第二节对TargetSource
的声明和使用原理讲解,咱们能够看到,TargetSource接口的设计几乎为咱们使用该接口实现自定义的对象实现了各类可能性:单例,多例,池化对象等等。下面咱们看看Spring为咱们提供了哪些常见的TargetSource
实现类:app
SingletonTargetSource
,顾名思义,即为单例的TargetSource
,其只是对目标bean进行了简单的封装。以下是其实现源码:dom
public class SingletonTargetSource implements TargetSource, Serializable { private static final long serialVersionUID = 9031246629662423738L; private final Object target; public SingletonTargetSource(Object target) { Assert.notNull(target, "Target object must not be null"); this.target = target; } @Override public Class<?> getTargetClass() { return this.target.getClass(); } @Override public Object getTarget() { return this.target; } @Override public void releaseTarget(Object target) {} @Override public boolean isStatic() { return true; } }
能够看到SingletonTargetSource
经过构造方法传入一个目标bean对象,在使用getTarget()
方法时,也只是将该对象直接返回;而且这里isStatic()
方法返回的是true,也就是说,Spring是能够缓存SingletonTargetSource
的。ide
与SingletonTargetSource
相似,PrototypeTargetSource
表示其将生成prototype类型的bean,即其生成的bean并非单例的,于是使用这个类型的TargetSource
时须要注意,封装的目标bean必须是prototype类型的。以下是其实现源码:post
public class PrototypeTargetSource extends AbstractPrototypeBasedTargetSource { @Override public Object getTarget() throws BeansException { return newPrototypeInstance(); } @Override public void releaseTarget(Object target) { destroyPrototypeInstance(target); } }
能够看到PrototypeTargetSource
主要重写了getTarget()
和releaseTarget()
方法,而且委托给newPrototypeInstance()
和destroyPrototypeInstance()
执行。咱们这里看看AbstractPrototypeBasedTargetSource
的源码:学习
public abstract class AbstractPrototypeBasedTargetSource extends AbstractBeanFactoryBasedTargetSource { // 继承自BeanFactoryAware接口,将当前Spring使用的BeanFactory传进来 @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { super.setBeanFactory(beanFactory); if (!beanFactory.isPrototype(getTargetBeanName())) { throw new BeanDefinitionStoreException( "Cannot use prototype-based TargetSource + "against non-prototype bean with name '" + getTargetBeanName() + "': instances would not be independent"); } } // 使用BeanFactory获取目标bean的对象,getTargetBeanName()方法将返回目标bean的名称, // 因为目标bean是prototype类型的,于是这里也就能够经过BeanFactory获取prototype类型的bean // 这也是PrototypeTargetSource可以生成prototype类型的bean的根本缘由 protected Object newPrototypeInstance() throws BeansException { if (logger.isDebugEnabled()) { logger.debug("Creating new instance of bean '" + getTargetBeanName() + "'"); } return getBeanFactory().getBean(getTargetBeanName()); } // 若是生成的bean使用完成,则会调用当前方法销毁目标bean,因为目标bean可能实现了DisposableBean // 接口,于是这里销毁bean的方式就是调用其实现的该接口的方法,从而销毁目标bean protected void destroyPrototypeInstance(Object target) { if (this.logger.isDebugEnabled()) { this.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.error("Couldn't invoke destroy method of bean with name '" + getTargetBeanName() + "'", ex); } } } }
能够看到,PrototypeTargetSource
的生成prototype类型bean的方式主要是委托给BeanFactory
进行的,由于BeanFactory
自有一套生成prototype类型的bean的逻辑,于是PrototypeTargetSource
也就具备生成prototype类型bean的能力,这也就是咱们要生成的目标bean必须声明为prototype类型的缘由。this
这里CommonsPool2TargetSource
也就是池化的TargetSource
,其基本具备日常所使用的“池”的概念的全部属性,好比:最小空闲数,最大空闲数,最大等待时间等等。实际上,CommonsPool2TargetSource
的实现是将其委托给了ObjectPool
进行,具体的也就是GenericObjectPool
,其实现了ObjectPool
接口。以下是CommonsPool2TargetSource
的主要实现:
public class CommonsPool2TargetSource extends AbstractPoolingTargetSource implements PooledObjectFactory<Object> { // 保存池化对象的池 @Nullable private ObjectPool pool; public CommonsPool2TargetSource() { setMaxSize(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL); } @Override protected final void createPool() { logger.debug("Creating Commons object pool"); // 建立池化对象 this.pool = createObjectPool(); } // 设置池化对象的基本属性 protected ObjectPool createObjectPool() { GenericObjectPoolConfig config = new GenericObjectPoolConfig(); config.setMaxTotal(getMaxSize()); config.setMaxIdle(getMaxIdle()); config.setMinIdle(getMinIdle()); config.setMaxWaitMillis(getMaxWait()); config.setTimeBetweenEvictionRunsMillis(getTimeBetweenEvictionRunsMillis()); config.setMinEvictableIdleTimeMillis(getMinEvictableIdleTimeMillis()); config.setBlockWhenExhausted(isBlockWhenExhausted()); return new GenericObjectPool(this, config); } // 从池中请求目标对象 @Override public Object getTarget() throws Exception { Assert.state(this.pool != null, "No Commons ObjectPool available"); return this.pool.borrowObject(); } // 将目标对象归还到池中 @Override public void releaseTarget(Object target) throws Exception { if (this.pool != null) { this.pool.returnObject(target); } } }
能够看到CommonsPool2TargetSource
实现是很是简单的,其将主要功能都委托给了对象池进行,这里的对象池实现也比较简单,其主要使用LinkedBlockingDeque
,也就是可阻塞的双端队列实现对象池的功能。这里关于队列锁的使用并非本文的研究范畴,读者可阅读本人前面的文章进行多线程的学习。
ThreadLocalTargetSource
也就是和线程绑定的TargetSource
,能够理解,其底层实现必然使用的是ThreadLocal
。既然使用了ThreadLocal
,也就是说咱们须要注意两个问题:
以下是ThreadLocalTargetSource
的源码:
public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource implements ThreadLocalTargetSourceStats, DisposableBean { // 保存目标对象的ThreadLocal对象 private final ThreadLocal<Object> targetInThread = new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'"); // 将生成过的目标对象保存起来,以便于后续进行统一销毁 private final Set<Object> targetSet = new HashSet<>(); // 生成目标对象,这里的生成方式是ThreadLocal很典型的一种使用策略,即首先从ThreadLocal中取, // 若是取到了,则直接返回,若是没取到,则使用“消耗“大一些的方式获取,并缓存到ThreadLocal中 @Override public Object getTarget() throws BeansException { // 记录目标对象的获取次数 ++this.invocationCount; // 从ThreadLocal中获取 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() + "'"); } // 若是ThreadLocal中不存在,则经过最基本的方式获取目标对象, // 并将生成的对象保存到ThreadLocal中 target = newPrototypeInstance(); this.targetInThread.set(target); // 将生成的对象进行缓存 synchronized (this.targetSet) { this.targetSet.add(target); } } else { ++this.hitCount; } return target; } // 销毁当前TargetSource对象和生成的目标对象 @Override public void destroy() { logger.debug("Destroying ThreadLocalTargetSource bindings"); synchronized (this.targetSet) { for (Object target : this.targetSet) { // 销毁生成的目标对象 destroyPrototypeInstance(target); } this.targetSet.clear(); } // 清除ThreadLocal中的缓存 this.targetInThread.remove(); } }
这里ThreadLocalTargetSource
主要集成了AbstractPrototypeBasedTargetSource
和DisposableBean
。关于AbstractPrototypeBasedTargetSource
前面已经讲过了,读者能够到前面翻看;而DisposableBean
的做用主要是提供一个方法,以供给Spring在销毁当前对象的时候调用。也就是说Spring在销毁当前TargetSource
对象的时候会首先销毁其生成的各个目标对象。这里须要注意的是,TargetSource
和生成的目标对象是两个对象,前面讲的TargetSouce
都是单例的,只是生成的目标对象多是单例的,也多是多例的。
对前面各个TargetSource
掌握以后,要实现自定义的TargetSource
实际上也很是的简单,假设咱们这里要生成两个对象进行访问均衡,此时就可使用自定义的TargetSource
。以下是咱们要生成的目标对象的声明:
public class Apple { private int id; public Apple(int id) { this.id = id; } public void eat() { System.out.println("eat apple, id: " + id); } }
这里Apple对象使用id属性进行当前对象的标识,并在eat()
方法中将id打印出来了。以下是自定义TargetSource
实现:
public class AppleTargetSource implements TargetSource { private Apple apple1; private Apple apple2; public AppleTargetSource() { this.apple1 = new Apple(1); this.apple2 = new Apple(2); } @Override public Class<?> getTargetClass() { return Apple.class; } @Override public boolean isStatic() { return false; } @Override public Object getTarget() throws Exception { ThreadLocalRandom random = ThreadLocalRandom.current(); int index = random.nextInt(2); return index % 2 == 0 ? apple1 : apple2; } @Override public void releaseTarget(Object target) throws Exception {} }
实现自定义TargetSource
主要有两个点要注意,一个是getTarget()
方法,该方法中须要实现获取目标对象的逻辑,另外一个是isStatic()
方法,这个方法告知Spring是否须要缓存目标对象,在非单例的状况下通常是返回false。以下是xml文件配置和驱动类的实现:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="targetSource" class="chapter7.eg10.AppleTargetSource"/> <aop:aspectj-autoproxy/> </beans>
public class CustomTargetSourceApp { public static void main(String[] args) throws Exception { ApplicationContext context = new ClassPathXmlApplicationContext("chapter7/eg10/applicationContext.xml"); TargetSource targetSource = (TargetSource) context.getBean("targetSource"); for (int i = 0; i < 10; i++) { Apple apple = (Apple) targetSource.getTarget(); apple.eat(); } } }
执行结果以下:
eat apple, id: 1 eat apple, id: 1 eat apple, id: 2 eat apple, id: 1 eat apple, id: 1 eat apple, id: 2 eat apple, id: 1 eat apple, id: 1 eat apple, id: 1 eat apple, id: 1
从执行结果来看,自定义TargetSource
的random特性是实现了,只是这里使用id为1的Apple执行次数要多一些,这主要是因为多线程执行会更倾向于使用当前已经得到锁的线程执行锁定代码。
本文主要首先讲解了Spring是若是在源码层面支持TargetSource
的,而后讲解了TargetSource
的使用原理,接着对Spring提供的常见TargetSource
进行了讲解,最后使用一个自定义的TargetSource
讲解了其使用方式。