最近项目上上了一个功能用到了aop。事情是这样的,本来是我们自己管理调用原生的kafka接口的。不过由于能力有限,决定把kafka交给公司的专业团队。这就有一个需求,要把kafka相关的调用换成公司团队提供的接口。为了不动之前的代码,我采用了aop的方式。对于aop原理还是懂一点点的,源码之前也看过,不过当时没有总结看过就过眼云烟了。这次再重新分析一次aop的实现,并总结记录下。
1 基本概念
spring的aop概念很多,关键的有这么几个
advice:通知,也就是增强的具体逻辑
pointcut:在某个类的某一个方法,或者某一些方法上切,这种位置信息就是pointcut
advisor:advice + pointcut
AnnotationAwareAspectJAutoProxyCreator : 一个beanPostProcessor,正是它在实例化并初始化一个bean之后进行增强,完成代理对象的创建。
总结下aop代理的流程
1 在调用getBean过程中,在实例化之前会调用beanPostProcessor的方法
位置在AbstractAutowireCapableBeanFactory的createBean中的代码段
try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } }
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } } return null; }
AnnotationAwareAspectJAutoProxyCreator 的父类 AbstractAutoProxyCreator 正好实现了 SmartInstantiationAwareBeanPostProcessor
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
所以本篇文章的重点就是,首先分析 AbstractAutoProxyCreator 的postProcessBeforeInstantiation。但是请注意,实际开发中对bean的动态代理并不是在postProcessBeforeInstantiation完成的,
而是在 postProcessAfterInitialization。但是分析postProcessBeforeInstantiation是很有必要的,因为它会缓存一些信息及完成对全部advisor的解析,并会缓存这些advisor,这些缓存在
postProcessAfterInitialization的执行时会被调用,如果不分析postProcessBeforeInstantiation,直接读postProcessAfterInitialization的代码就会有疑问,不知道这些缓存值是怎么来的。
2 AbstractAutoProxyCreator.postProcessBeforeInstantiation
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); if (beanName == null || !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; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. if (beanName != null) { TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { this.targetSourcedBeans.add(beanName); Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } } return null; }
代码不长,而且后半部分一般情况下也不会走到的。 TargetSource targetSource = getCustomTargetSource(beanClass, beanName); 一般情况下都是返回为null所以if分支走不到的。
这里最重要的就 shouldSkip这里会保存大量的重要信息,不过我们也分析下 isInfrastructureClass
protected boolean isInfrastructureClass(Class<?> beanClass) { boolean retVal = Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass); if (retVal && logger.isTraceEnabled()) { logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]"); } return retVal; }
如果一个类是 Advice,Pointcut,Advisor,AopInfrastructureBean那么一定不能够被aop增强。
现在分析 shouldSkip,实现类在 AspectJAwareAdvisorAutoProxyCreator中
protected boolean shouldSkip(Class<?> beanClass, String beanName) { // TODO: Consider optimization by caching the list of the aspect names List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor) { if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) { return true; } } } return super.shouldSkip(beanClass, beanName); }
该方法本身不难理解,如果要增强的bean的beanName恰好是一个@Aspect注解的类的名字的话,那么是应该跳过的。
之所以说 shouldSkip是因为 findCandidateAdvisors() 这个方法太重要了。
实现类在 AnnotationAwareAspectJAutoProxyCreator中
protected List<Advisor> findCandidateAdvisors() { // Add all the Spring advisors found according to superclass rules. List<Advisor> advisors = super.findCandidateAdvisors(); // Build Advisors for all AspectJ aspects in the bean factory. advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); return advisors; }
这里先把 super是 AbstractAdvisorAutoProxyCreator
protected List<Advisor> findCandidateAdvisors() { return this.advisorRetrievalHelper.findAdvisorBeans(); }
public List<Advisor> findAdvisorBeans() { // Determine list of advisor bean names, if not cached already. String[] advisorNames = null; synchronized (this) { advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } } if (advisorNames.length == 0) { return new LinkedList<Advisor>(); }
上面只是代码段,没有全部贴出来。因为
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false); 一般来说都是返回为空的。因为声明式aop不会在配置文件里明确写advisor,这里只是提一下,
如果使用配置式aop这里才能拿到配置文件里的advisor。
回到子类AnnotationAwareAspectJAutoProxyCreator的方法中继续分析。
public List<Advisor> buildAspectJAdvisors() { List<String> aspectNames = this.aspectBeanNames; if (aspectNames == null) {//aspectNames只会赋值一次,不为null后就不会再进if分支了 synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { List<Advisor> advisors = new LinkedList<Advisor>(); aspectNames = new LinkedList<String>(); String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false);//拿所有的beanName,挨个扫描 for (String beanName : beanNames) { if (!isEligibleBean(beanName)) { continue; } // We must be careful not to instantiate beans eagerly as in this case they // would be cached by the Spring container but would not have been weaved. Class<?> beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } if (this.advisorFactory.isAspect(beanType)) { //如果一个bean有注解@Aspect返回为true aspectNames.add(beanName); AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);//拿到这个被Aspect注解的所有advisor if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors);//缓存advisor 重要 } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // Per target or per this. if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton"); } MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } this.aspectBeanNames = aspectNames; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } List<Advisor> advisors = new LinkedList<Advisor>(); for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } else { MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; }
逻辑很明确就是扫描所有的@Aspect,然后解析其中的每个方法,常见的@Before @After都会在这里被转为Advisor。比如我们可以在一个Aspect中写两个@After注解的方法。那么在这里就会得到
两个Advisor。
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); validate(aspectClass); // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator // so that it will only instantiate once. MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new LinkedList<Advisor>(); for (Method method : getAdvisorMethods(aspectClass)) {//遍历每一个方法 把每一个方法解析为Advisor Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } }
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
从Advisor的构造函数就能看出来,有一个 Pointcut用来匹配对那个bean增强的, Method candidateAdviceMethod 就是advice,也就是要增强的具体逻辑。
到这里关键的advisor我们就拿到了并且进行了缓存。具体点说,有两个重要的缓存。
public class BeanFactoryAspectJAdvisorsBuilder { private final ListableBeanFactory beanFactory; private final AspectJAdvisorFactory advisorFactory; private volatile List<String> aspectBeanNames; private final Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<String, List<Advisor>>();
所有的@Aspect注解的类都会被缓存到 aspectBeanNames,而 advisorsCache的key 就是一个@Aspect 的beanName ,values就是该Aspect中包含的Advisor。
对于 postProcessBeforeInstantiation的分析就到这里了,重点是postProcessAfterInitialization
3 AbstractAutoProxyCreator.postProcessAfterInitialization
首先明确下 postProcessAfterInitialization 是在哪里被调用呢,其实是初始化一个bean的最后阶段
AbstractAutowireCapableBeanFactory的initializeBean中
try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean;
也就是说当一个bean被实例化,并且已经完成了初始化后,它才被aop增强。这是很符合逻辑的,因为要增强就必须这个bean是完整的。
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (beanName != null && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
这里 specificInterceptors 得到的就是InstantiationModelAwarePointcutAdvisor,像我在前一节说的写了两个@After的方法,那么这里就返回两个InstantiationModelAwarePointcutAdvisor
再看看是怎么拿到的Advisor
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { List<Advisor> candidateAdvisors = findCandidateAdvisors();//上节说了,所有advisor会进行缓存,这里直接从缓存拿全部的advisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);//根据切点判断哪些advisor能用来增强这个bean extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
至于这部分
if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
就纯粹是JDK动态代理和CGLib代理的内容了。拿到了所有的advisor后,把这些advidor变成责任链模式,根据是@Before还是After 还是@Round 在动态代理的逻辑中 原方法的前后,依次执行。
4 Advisor和Advice
有必要分析下获取advisor的逻辑。前面章节介绍过怎么得到advisor,本节具体展开说明下。
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
看看 InstantiationModelAwarePointcutAdvisorImpl 这个类的构造方法
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { this.declaredPointcut = declaredPointcut; this.declaringClass = aspectJAdviceMethod.getDeclaringClass(); this.methodName = aspectJAdviceMethod.getName(); this.parameterTypes = aspectJAdviceMethod.getParameterTypes(); this.aspectJAdviceMethod = aspectJAdviceMethod; this.aspectJAdvisorFactory = aspectJAdvisorFactory; this.aspectInstanceFactory = aspectInstanceFactory; this.declarationOrder = declarationOrder; this.aspectName = aspectName; if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // Static part of the pointcut is a lazy type. Pointcut preInstantiationPointcut = Pointcuts.union( aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); // Make it dynamic: must mutate from pre-instantiation to post-instantiation state. // If it's not a dynamic pointcut, it may be optimized out // by the Spring AOP infrastructure after the first evaluation. this.pointcut = new PerTargetInstantiationModelPointcut( this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); this.lazy = true; } else { // A singleton aspect. this.pointcut = this.declaredPointcut; this.lazy = false; this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); } }
注意 instantiatedAdvice是advisor的一个成员变量。 advice可以理解为对目标类中的目标方法的增强逻辑。代码在 ReflectiveAspectJAdvisorFactory.getAdvice
代码段
switch (aspectJAnnotation.getAnnotationType()) { case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfter: springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break;
可见在@Aspect的bean中的每一个方法上的注解,@Before和@After都会被解析为advice
之所以,介绍本节是为了下面要介绍的动态代理逻辑
5 动态代理
现在拿到了可以适用于某个bean的所有的advisor,动态代理的逻辑适当的讲解一下。来分析下JdkDynamicAopProxy。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } else if (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // Get the interception chain for this method. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
在这里还会进行一次过滤,因为选出来的advisor是按照类维度选出来的,该bean的某些方法是不应该被增强的,所以这里还需要按照method维度过滤一次。
就是那些advisor,但是其实这里是经过了一次转换的,
chain的元素已经不是advisor了,而是 InterceptorAndDynamicMethodMatcher。
重点要看的代码就是
// We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed();
很典型的责任链模式,其实可以好好向spring学学怎么写责任链模式。在new出来一个ReflectiveMethodInvocation时,全部的advisor作为成员变量加入其中。
@Override public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); }
dm.interceptor.invoke执行的逻辑就是每个advice的执行逻辑。我们可以看看@Before和@After的实现
AspectJAfterAdvice
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } finally { invokeAdviceMethod(getJoinPointMatch(), null, null); } }
MethodBeforeAdviceInterceptor
@Override public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); return mi.proceed(); }
强大的责任链模式。这也能解释为啥before一定在after之前执行,其实spring并没有对before和after进行排序,而是通过这种方法调用栈的顺序优雅实现了。
6 总结
分析了Spring的动态代理后,发现Spring的拓展功能都离不开BeanPostProcessor,动态代理也是基于此。
在一个bean初始化完成的最后一步,判断该bean是否应该被增强。
如果应该增强,则对该bean进行增强,同时在spring容器内beanName依旧不变,但是里面的class类型已经不再是原来的了。