Spring-Aop源码解析(中)
上文中解析了Aop中的匹配规则是怎样的,我定义一个Advisor,是如何可以切到我想要的方法或者Bean类从而去生成代理对象,对原生代码进行横向的逻辑插入
本文来解析Spring是如何支持Aop的,因为我们开发中常用的@Before,@After这些注解并不是Spring的东西,而是org.aspectj.lang.annotation包下的,那么Spring是如何引入并且支持这些注解的
我们通常在启动Spring项目的时候,会在启动类上加上@EnableAspectJAutoProxy注解来标明开启Aop功能,支持Aspectj模块
这个注解里面引入了一个AspectJAutoProxyRegistrar.class类,这个类实现了ImportBeanDefinitionRegistrar接口,(ImportBeanDefinitionRegistrar的注册逻辑可以参考Spring配置类源码解析(上)),在ImportBeanDefinition方法中注册了AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition,这个玩意继承了AbstractAutoProxyCreator,他的父类是一个BeanPostProcessor,在其初始化后的方法中(AbstractAutoProxyCreator.postProcessAfterInitialization),有着支持Aspectj的功能,源码如下
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 找到所有的Advisor,调用子类的方法AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()//第一步找Spring中的Advisor//第二步找切面类中和Aspectj有关的注解,转换成Advisor再加进去List<Advisor> candidateAdvisors = findCandidateAdvisors();// 进行筛选List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);// 对Advisor进行排序,按Ordered接口、@Order注解进行排序if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}
第一步,先找到所有的Advisor(包括Spring中的Advisor,以及Aspectj定义的那些注解)
第二步,对这些Advisor进行筛选,根据我们上文讲的筛选规则(类和方法匹配)去筛选合适的Advisor
第三步,排序这些Advisor,每一个Advisor都有其实现的顺序,到底该在何时插入代理逻辑
下面是查找所有Advisor的逻辑(源码中的代码只取精简部分,额外逻辑可以自己去研究下)
1.找到Spring容器中现有的Advisor(BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans())public List<Advisor> findAdvisorBeans() {//获取容器中所有的Advisor的namesString[] advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);this.cachedAdvisorBeanNames = advisorNames;List<Advisor> advisors = new ArrayList<>();for (String name : advisorNames) {advisors.add(this.beanFactory.getBean(name, Advisor.class));}return advisors;}2.找到切面类中定义的那些注解,并且将其转换成Advisor(BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors)public List<Advisor> buildAspectJAdvisors() {// aspectBeanNames是用来缓存BeanFactory中所存在的切面beanName的,第一次为null,后面就不为null了,不为null表示之前就已经找到过BeanFactory中的切面了synchronized (this) {List<Advisor> advisors = new ArrayList<>();// 把所有beanNames拿出来遍历,判断某个bean的类型是否是AspectString[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);for (String beanName : beanNames) {Class<?> beanType = this.beanFactory.getType(beanName, false);//是否是切面。加了@Aspect注解的Beanif (this.advisorFactory.isAspect(beanType)) {// 利用BeanFactoryAspectInstanceFactory来解析Aspect类MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);//核心逻辑,获取切面Bean中的AdvisorList<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);// 并且会将该切面中所对应的Advisor对象进行缓存this.advisorsCache.put(beanName, classAdvisors);advisors.addAll(classAdvisors);}}return advisors;}}
结合源码可以得知,想要获取切面中的Advisor,首先他会把Spring单例池中所有的BeanName全部拿出来,然后挨个判断是否是切面Bean,如果是,那么根据这个BeanName构造一个MetadataAwareAspectInstanceFactory ,然后调用getAdvisor方法去获取切面中的Advisor,下面是getAdvisor中的核心源码
//切面中是否含有@PointCut注解 for (Method method : getAdvisorMethods(aspectClass)) {//获取AdvisorAdvisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);if (advisor != null) {advisors.add(advisor);}}1.找到没有PointCut的方法private List<Method> getAdvisorMethods(Class<?> aspectClass) {List<Method> methods = new ArrayList<>();// 拿到切面类中所没有加@Pointcut的方法(注意,是没有加Pointcut的所有方法)//adviceMethodFilter:pointCut.class == null//然后将这些方法加到methods这个list里面ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);// 对方法进行排序,按注解和方法名字进行排序if (methods.size() > 1) {//adviceMethodComparator:Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.classmethods.sort(adviceMethodComparator);}return methods;}2.上面的那个methods中接着过滤只加了Aspectj的那些方法public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {//getPointcut里面会匹配加了Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class//上面这些注解的方法才会被匹配出来,然后生成PointCut// 拿到当前方法所对应的Pointcut对象,但是注意:如果当前方法上是这么写的@After("pointcut()"),那么此时得到的Pointcut并没有去解析pointcut()得到对应的表达式AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());// expressionPointcut是pointcut// candidateAdviceMethod承载了advice//将method和Advice合成一个Advisorreturn new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);}}
上面的代码总共分为两部分
1.getAdvisorMethods中先把没有加PointCut的注解的方法过滤出来,这时候里面就相当于把Aspectj中大部分的普通方法这些也都加进去了
2.getAdvisors中的getPointCut方法中,会将这些方法进行下一步的过滤,会将只加了Aspecj相关注解的那些方法过滤出来,然后将该方法转换成Advice,接着和PointCut拼凑成完整的Advisor,但是这里method还没有转换成Advice,在InstantiationModelAwarePointcutAdvisorImpl的构造方法中我们可以找到Aspectj注解转换成Advisor的逻辑,路径如下InstantiationModelAwarePointcutAdvisorImpl的构造方法->InstantiationModelAwarePointcutAdvisorImpl.instantiateAdvice->ReflectiveAspectJAdvisorFactory.getAdvice,我们截取部分源码即可看到,拿到这些Advisor之后,就会回到初始化后的方法中,然后根据他的class和method去匹配我需要的Advisor,得到这些Advisor之后(上文Spring-Aop源码解析(中)在方法执行的时候会留有疑惑,这些Advisor是怎么来的,本文这里就解答了),会调用ProxyFactory.getProxy去为正在生成的Bean生成一个代理对象,然后放到Spring的单例池中
case AtAround:// @AroundspringAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;
case AtBefore:springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;
总结:
Spring支持开启Aop需要在启动类上加上EnableAspectJAutoProxy注解,这个注解里面注册了一个BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator),Spring中的所有Bean在生成的时候都会经过这里面的初始化后方法,会拿着得到的Advisor去匹配我正在生成的Bean是否需要代理,如果匹配到了至少一个Advisor,那么就会生成代理对象,然后注意,在方法执行的时候,也会去匹配一遍Advisor,再次对过滤得到的Advisor进行一遍过滤,就比如我在UserService类中的A方法加了@Transactional的注解,B方法没有加,那么我的UserService就会生成一个代理对象,我在执行B方法的时候不需要事务,所以执行B方法的时候不需要额外的代理逻辑,所以还得再匹配一遍,更多的场景读者可以自己思考。