Spring-Aop源码解析(下)

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的names
	String[] 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的类型是否是Aspect
			String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Object.class, true, false);
			for (String beanName : beanNames) {
				Class<?> beanType = this.beanFactory.getType(beanName, false);
				//是否是切面。加了@Aspect注解的Bean
				if (this.advisorFactory.isAspect(beanType)) {
					// 利用BeanFactoryAspectInstanceFactory来解析Aspect类
					MetadataAwareAspectInstanceFactory factory =
							new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
					//核心逻辑,获取切面Bean中的Advisor
					List<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)) {
            //获取Advisor
			Advisor 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.class
			methods.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合成一个Advisor
		return 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:
   // @Around
   springAdvice = 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方法的时候不需要额外的代理逻辑,所以还得再匹配一遍,更多的场景读者可以自己思考。

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它提供了一种面向切面编程的方式,可以让开发者将一些通用的、横切的关注点(如事务、安全、缓存等)从业务逻辑中剥离出来,使得业务逻辑更加清晰简洁,代码复用更加方便。 Spring AOP的实现原理主要基于Java动态代理和CGLIB动态代理两种方式,其中Java动态代理主要用于接口代理,而CGLIB动态代理则主要用于类代理。Spring AOP中的核心概念是切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)和织入(Weaving)。 在Spring AOP中,切面是一个横向的关注点,它跨越多个对象和方法,通常包含一些通用的功能,如日志记录、安全控制等。连接点则是程序中可以被切面拦截的特定点,如方法调用、异常抛出等。通知是切面在连接点执行前后所执行的动作,包括前置通知(Before)、后置通知(After)、异常通知(AfterThrowing)、返回通知(AfterReturning)和环绕通知(Around)。切点则是用来匹配连接点的规则,它可以指定哪些连接点会被切面拦截。织入则是将切面应用到目标对象中的过程,它可以在编译时、类加载时、运行时等不同的阶段进行。 Spring AOP源码解析涉及到很多细节,包括代理的生成、通知的执行、切点的匹配等,需要深入了解Spring框架的内部实现和Java的反射机制。对于初学者而言,可以先从Spring AOP的基本概念和用法入手,了解其实现原理的同时,也可以通过调试和查看源码来加深理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值