Spring基于注解形式的 AOP的原理流程及源码解析(二)
来源:互联网 发布:淘宝店铺销售策划方案 编辑:程序博客网 时间:2024/05/22 15:40
在阅读此博客的同时打开源码,边看博客边看源码,博客上对源码的关键位置做了注释,能很大程度上降低源码的阅读难度。
概括 : 切面Advisor由切入点pointcut及通知advice组成,切入点指明被代理的对象,通知指明被织入的方法,pointcut一般在注解上定义,通知一般就是注解标识的方法
AnnotationAwareAspectJAutoProxyCreator的类层级结构图:
AnnotationAwareAspectJAutoProxyCreator间接实现了SmartInstantiationAwareBeanPostProcessor,因此其算是一个Bean的后处理器,在实例化每个Bean的时候都会调用其父类相应的方法,此类通过重写父类的方法对每个Bean的实例化流程加入AOP的逻辑。
BeanPostProcessor对Bean的实例化过程中执行的第一个方法是:postProcessBeforeInstantiation(),
在每一个Bean实例化之前就调用,如果返回的不是null,则就不会再创建Bean,直接使用此方法的返回值作为Bean实例。AnnotationAwareAspectJAutoProxyCreator最开始执行postProcessBeforeInstantiation方法,此方法在其父类AbstractAutoProxyCreator中:
代码块1
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { 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; } //重点关注shouldSkip这个方法,shouldSkip一般返回的都是false,但是执行了很多其他的步骤 if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } //此步一般不会触发,除非我们自定义了customTargetSourceCreators属性 // 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; }}
AspectJAwareAdvisorAutoProxyCreator对shouldSkip方法做了重载:
代码块2
public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {protected boolean shouldSkip(Class<?> beanClass, String beanName) { // TODO: Consider optimization by caching the list of the aspect names //在这一步对Spring容器内所有的BeanDefinition的标识了@Aspect的类的相应切面信息解析缓存起来 List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor) { //实际返回的类型均不会是AspectJPointcutAdvisor,因此不会直接返回true if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) { return true; } } } return super.shouldSkip(beanClass, beanName); } //AnnotationAwareAspectJAutoProxyCreator对此方法做了重载,先执行此方法,然后执行其自定义的流程 protected List<Advisor> findCandidateAdvisors() { //获取Spring容器内所有的实现了Advisor的Bean return this.advisorRetrievalHelper.findAdvisorBeans(); }}
AnnotationAwareAspectJAutoProxyCreator重载findCandidateAdvisors方法,从方法字面理解就是获取所有合适的Advisor,AOP切面:
代码块3
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator { 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. //重点是buildAspectJAdvisors这个方法 advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); return advisors; }}
AnnotationAwareAspectJAutoProxyCreator在initBeanFactory方法内对aspectJAdvisorsBuilder属性初始化为BeanFactoryAspectJAdvisorsBuilderAdapter类型的对象,接下来看其buildAspectJAdvisors方法:
代码块4
public class BeanFactoryAspectJAdvisorsBuilder { private volatile List<String> aspectBeanNames; private final Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<String, List<Advisor>>(); private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache = new ConcurrentHashMap<String, MetadataAwareAspectInstanceFactory>(); /** * Look for AspectJ-annotated aspect beans in the current bean factory, * and return to a list of Spring AOP Advisors representing them. * <p>Creates a Spring Advisor for each AspectJ advice method. * @return the list of {@link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List<Advisor> buildAspectJAdvisors() { List<String> aspectNames = this.aspectBeanNames; //在Spring容器的BeanPostProcessor实例化之后,初始化第一个普通Bean时,还未对容器内的普通的BeanDefinitions解析 //在触发实例化第一个Bean时解析所有的BeanDefinition,获取所有的切面类名,仅解析一次 if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { List<Advisor> advisors = new LinkedList<Advisor>(); aspectNames = new LinkedList<String>(); //获取所有Bean名称,普通的Bean均未实例化 String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); for (String beanName : beanNames) { //判断是否符合条件,BeanFactoryAspectJAdvisorsBuilderAdapter对此方法做了重载 //如果AnnotationAwareAspectJAutoProxyCreator未指定includePatterns时均合格。 //可以指定includePatterns,设置包名及类路径等对被代理类做筛选 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; } //判断Bean的Class上是否标识@Aspect注解 if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); AspectMetadata amd = new AspectMetadata(beanType, beanName); //如果@Aspect的value未赋值,默认就是PerClauseKind.SINGLETON,这些类型是动态代理生成Bean的形式 if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); //重点看这步,此方法会花费很大篇幅,请看下一块代码(代码块5) List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); if (this.beanFactory.isSingleton(beanName)) { //将解析的Bean名称及类上的切面缓存起来,每个Bean只解析一次 this.advisorsCache.put(beanName, classAdvisors); } 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) { //从缓存中获取当前Bean的切面实例,如果不为空,则指明当前Bean的Class标识了@Aspect,且有切面方法 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; }}
ReflectiveAspectJAdvisorFactory反射式的切面工厂类,从@Aspect标识的类上获取@Before,@Pointcut等注解的信息及其标识的方法的信息,生成切面:
代码块5
public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable { private static final Comparator<Method> METHOD_COMPARATOR; 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>(); //获取标识了切面注解的方法,排除@Pointcut的方法 for (Method method : getAdvisorMethods(aspectClass)) { //通过此方法,切面实例工厂,方法排序序号,及类名生成切面实例,代码详解看下一块代码(代码块6) Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } // If it's a per target aspect, emit the dummy instantiating aspect. if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // Find introduction fields. for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; } //获取标识了切面注解的方法 private List<Method> getAdvisorMethods(Class<?> aspectClass) { final List<Method> methods = new LinkedList<Method>(); ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() { @Override public void doWith(Method method) throws IllegalArgumentException { // Exclude pointcuts //在@Aspect标识的类内部排除@Pointcut标识的方法,得到的方法集合包括继承自父类的方法,包括继承自Object的方法 if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { methods.add(method); } } }); //对得到的所有方法排序, //如果方法标识了切面注解,则按@Around, @Before, @After, @AfterReturning, @AfterThrowing的顺序排序 //如果没有标识这些注解,则按方法名称的字符串排序, //有注解的方法排在无注解的方法之前 Collections.sort(methods, METHOD_COMPARATOR); return methods; }}
通过切面注解及其方法生成切面实例:
代码块6
public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable { 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; } //根据方法,切入点,AOP实例工厂,类名及序号生成切面实例,在调用此构造函数时,生成通知Advice //此时切入点和通知均生成,构成一个完整的AOP切面,根据切入点匹配Bean,匹配生就用CGLIB生成代理对象,织入切面 //此构造函数的代码解析请看下一块代码(代码块7) return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); } private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) { //查询方法上的切面注解,根据注解生成相应类型的AspectJAnnotation,在调用AspectJAnnotation的构造函数的同时 //根据注解value或pointcut属性得到切入点表达式,有argNames则设置参数名称 AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); //过滤那些不含@Before, @Around, @After, @AfterReturning, @AfterThrowing注解的方法 if (aspectJAnnotation == null) { return null; } //生成带表达式的切面切入点,设置其切入点表达式 AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]); ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); ajexp.setBeanFactory(this.beanFactory); return ajexp; }}
根据切面注解标识的方法,切入点,AOP实例工厂,类名及序号生成切面实例,在调用此构造函数时,生成通知Advice,重点看如何生成通知
代码块7
public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable { public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass); AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } // If we get here, we know we have an AspectJ method. // Check that it's an AspectJ-annotated class if (!isAspect(candidateAspectClass)) { throw new AopConfigException("Advice must be declared inside an aspect type: " + "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]"); } if (logger.isDebugEnabled()) { logger.debug("Found AspectJ method: " + candidateAdviceMethod); } AbstractAspectJAdvice springAdvice; //根据AspectJAnnotationType的类型及注解类型生成不同的通知实例 //同时有参数比如@AfterReturning的返回值,@AfterThrowing的异常,设置入通知中 switch (aspectJAnnotation.getAnnotationType()) { case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfter: springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; case AtAfterThrowing: springAdvice = new AspectJAfterThrowingAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; case AtAround: springAdvice = new AspectJAroundAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtPointcut: if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } return null; default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); } // Now to configure the advice... //设置通知方法所属的类 springAdvice.setAspectName(aspectName); //设置通知的序号,同一个类中有多个切面注解标识的方法时,按@Around, @Before, @After, @AfterReturning, @AfterThrowing的顺序排序, //其序号就是此方法在列表中的序号,第一个就是0 springAdvice.setDeclarationOrder(declarationOrder); //获取通知方法的所有参数 String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); //将通知方法上的参数设置到通知中 if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } //计算参数绑定工作,此方法详解请看下一块代码(代码块8) springAdvice.calculateArgumentBindings(); return springAdvice; }}
切面注解标识的方法的参数有一定要求,需要和注解的类型匹配,和注解的参数名称匹配,代码详解如下:
代码块8
public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, Serializable { public synchronized final void calculateArgumentBindings() { // The simple case... nothing to bind. if (this.argumentsIntrospected || this.parameterTypes.length == 0) { return; } int numUnboundArgs = this.parameterTypes.length; Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes(); //切面注解标识的方法第一个参数要求是JoinPoint,或StaticPart,若是@Around注解则也可以是ProceedingJoinPoint if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) { numUnboundArgs--; } else if (maybeBindJoinPointStaticPart(parameterTypes[0])) { numUnboundArgs--; } if (numUnboundArgs > 0) { // need to bind arguments by name as returned from the pointcut match bindArgumentsByName(numUnboundArgs); } this.argumentsIntrospected = true; } //根据方法参数,注解上的属性匹配绑定,numArgumentsExpectingToBind代表还有多少个参数需要绑定 private void bindArgumentsByName(int numArgumentsExpectingToBind) { if (this.argumentNames == null) { //获取方法参数的名称 this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod); } if (this.argumentNames != null) { // We have been able to determine the arg names. bindExplicitArguments(numArgumentsExpectingToBind); } else { throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " + "requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " + "the argument names were not specified and could not be discovered."); } } private void bindExplicitArguments(int numArgumentsLeftToBind) { //此属性用来存储方法未绑定的参数名称,及参数的序号 this.argumentBindings = new HashMap<String, Integer>(); int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length; if (this.argumentNames.length != numExpectedArgumentNames) { throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames + " arguments to bind by name in advice, but actually found " + this.argumentNames.length + " arguments."); } // So we match in number...,argumentIndexOffset代表第一个未绑定参数的顺序 int argumentIndexOffset = this.parameterTypes.length - numArgumentsLeftToBind; for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) { //存储未绑定的参数名称及其顺序的映射关系 this.argumentBindings.put(this.argumentNames[i], i); } // Check that returning and throwing were in the argument names list if // specified, and find the discovered argument types. //如果是@AfterReturning注解的returningName 有值,验证,解析,同时得到定义返回值的类型 if (this.returningName != null) { if (!this.argumentBindings.containsKey(this.returningName)) { throw new IllegalStateException("Returning argument name '" + this.returningName + "' was not bound in advice arguments"); } else { Integer index = this.argumentBindings.get(this.returningName); this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index]; this.discoveredReturningGenericType = this.aspectJAdviceMethod.getGenericParameterTypes()[index]; } } //如果是@AfterThrowing注解的throwingName 有值,验证,解析,同时得到抛出异常的类型 if (this.throwingName != null) { if (!this.argumentBindings.containsKey(this.throwingName)) { throw new IllegalStateException("Throwing argument name '" + this.throwingName + "' was not bound in advice arguments"); } else { Integer index = this.argumentBindings.get(this.throwingName); this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index]; } } // configure the pointcut expression accordingly. configurePointcutParameters(argumentIndexOffset); } private void configurePointcutParameters(int argumentIndexOffset) { int numParametersToRemove = argumentIndexOffset; if (this.returningName != null) { numParametersToRemove++; } if (this.throwingName != null) { numParametersToRemove++; } String[] pointcutParameterNames = new String[this.argumentNames.length - numParametersToRemove]; Class<?>[] pointcutParameterTypes = new Class<?>[pointcutParameterNames.length]; Class<?>[] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes(); int index = 0; for (int i = 0; i < this.argumentNames.length; i++) { if (i < argumentIndexOffset) { continue; } if (this.argumentNames[i].equals(this.returningName) || this.argumentNames[i].equals(this.throwingName)) { continue; } pointcutParameterNames[index] = this.argumentNames[i]; pointcutParameterTypes[index] = methodParameterTypes[i]; index++; } //剩余的未绑定的参数会赋值给AspectJExpressionPointcut(表达式形式的切入点)的属性,以备后续使用 this.pointcut.setParameterNames(pointcutParameterNames); this.pointcut.setParameterTypes(pointcutParameterTypes); }}
- Spring基于注解形式的 AOP的原理流程及源码解析(二)
- Spring基于注解形式的 AOP的原理流程及源码解析(一)
- Spring基于注解形式的 AOP的原理流程及源码解析(三)
- Spring基于注解形式的 AOP的原理流程及源码解析(四)
- 基于注解的Spring Security原理解析
- (八)Spring核心框架 - AOP的原理及源码解析
- AOP的实现:Spring注解形式拦截
- Spring Aop(二)——基于Aspectj注解的Aop简单实现
- Spring-AOP:基于AspectJ注解的AOP
- (二)Spring AOP:基于注解
- Spring-Aop注解形式案例<二>
- Spring AOP(二)(Spring中基于AOP的@AspectJ)
- [Spring]基于注解的形式配置Bean
- Spring基于注解@AspectJ的AOP
- Spring基于注解@AspectJ的AOP
- 基于注解的Spring AOP例子
- spring框架基于注解aop的通知
- 基于注解的spring AOP简单实现
- 简单设置让EasyUI显示中文,日期选择框格式为yyyy-MM-dd格式
- 钩子学习总结
- Fiddler模拟web请求的四种方法
- 合理不合理的都要认真分析
- Java开发练习4,接口
- Spring基于注解形式的 AOP的原理流程及源码解析(二)
- 医学序列图像定位线绘制基本方法介绍
- eclipse反编译插件
- MATLAB numel 用法
- Windows下使用VisualSFM + CMVS/PMVS + MeshLab进行三维重建
- Java基础------static关键字
- 用css做的各种三角形
- ubantu安装软件时报错: E:无法获得锁
- 使用fiddler做web的压力测试