Spring源码系列(四):AOP流程源码分析1 产生AOP代理流程分析

250 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

Spring源码系列(四):AOP流程源码分析

AOP执行过程流程图

![AOP代理流程分析.png]

AOP代理流程分析.png

测试类

  • spring-aop.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="userService" class="com.ming.aop.UserServiceImpl"/>
    
    	<!--切面对象-->
    	<bean id="myAspect" class="com.ming.aop.MyAspect"/>
    
    	<!--配置织入:告诉spring框架,哪些方法(切点)需要进行增强(前置、后置...-->
    	<aop:config>
    		<!--抽取切点表达式-->
    		<aop:pointcut id="myPointcut" expression="execution(* com.ming.aop.*.*(..))"/>
    
    		<!--声明切面-->
    		<aop:aspect ref="myAspect">
    			<!--抽取切点表达式-->
    			<!--<aop:pointcut id="myPointcut" expression="execution(* com.ming.aop.*.*(..))"/>-->
    			<!--切面:切点+通知-->
    			<aop:before method="before" pointcut="execution( * com.ming.aop.*.*(..))"/>
    			<aop:after-returning method="afterReturning" pointcut="execution( * com.ming.aop.*.*(..))"/>
    			<aop:around method="around" pointcut="execution(* com.ming.aop.*.*(..))"/>
    		 	<aop:after-throwing method="afterThrowing" pointcut="execution(* com.ming.aop.*.*(..))"/>
    		 	<aop:after method="after" pointcut="execution(* com.ming.aop.*.*(..))"/>
    			<aop:around method="around" pointcut-ref="myPointcut"/>
    			<aop:after method="after" pointcut-ref="myPointcut"/>
    		</aop:aspect>
    	</aop:config>
    </beans>
    
  • MyAspect.java 切面对象

    public class MyAspect {
    
    	public void before() {
    		System.out.println("前置增强.......");
    
    	}
    
    	public void afterReturning() {
    		System.out.println("后置增强.......");
    	}
    
    	// ProceedJoinPoint:正在执行的连接点 == 切点
    	public Object around(ProceedingJoinPoint pjp) throws Throwable {
    		System.out.println("环绕前增强.......");
    		Object proceed = pjp.proceed(); // 切点方法
    		System.out.println("环绕后增强.......");
    
    		return proceed;
    	}
    
    	public void afterThrowing() {
    		System.out.println("异常抛出增强.......");
    	}
    
    	public void after() {
    		System.out.println("最终增强.......");
    	}
    }
    
  • UserServiceImpl 被代理类,实现了UserService接口

    public class UserServiceImpl implements UserService{
    	@Override
    	public void saveUser() {
    		System.out.println("save user.....");
    	}
    }
    

产生AOP代理流程分析

我们知道在spring配置文件中,除了import、alias、bean、beans 标签外别都是属于自定义标签,spring会在执行refresh中的第二个方法obtainFreshBeanFactory() 时被解析到spring工厂中去,例如当我们在配置文件中配置了<context:component-scan />标签,spring就会找到处理这个标签的BeanDefinitionParser来进行解析。

如果不明白这一部分可以看我的另外一篇文章:juejin.cn/post/708130…

AOP的执行流程也是如此,我们只不过是在配置文件中配置了<aop:xxx/>,所以解析这个标签的时候也会找到一个BeanDefinitionParser也解析,这个类就是ConfigBeanDefinitionParser。我们找到这个类的parse方法。

  • configureAutoProxyCreator会获取产生代理对象的创造器,我们指定AOP过程中的目标类后,就会需要代理类,而这个过程就是这个方法来完成的。点进这个方法内部会发现new了一个AspectJAwareAdvisorAutoProxyCreator类。
  • 这里的for循环主要来处理aop:config下面的aop:xxx标签,aop标签冒号后面的xxx,默认有三种。
    • <aop:pointcut />
      • 根据parserContext.getDelegate().getLocalName(elt);方法获取到localName,就是aop:xxx/,就是xxx的name,然后通过if判断,这个localName归属于哪个,就执行相应的逻辑。 <aop:pointcut />:切点,这是必要的。我们在配置aop相关的参数的时候,配置pointcut的时机主要有三种。也就是说无论怎么配置,if (POINTCUT.equals(localName)) 至少为真执行逻辑一次。

        <!--第一种,和aop:aspect平级-->
        	<aop:config>
        		<aop:pointcut id="" expression=""/>
        		<aop:aspect/>
        	</aop:config>
        
        <!--第二种,在aop:aspect里配置平级-->
        	<aop:config>
        		<aop:aspect>
        			<aop:pointcut id="" expression=""/>
        		</aop:aspect>
        	</aop:config>
        
        <!--第三种,在aop:advisor里配置-->
        	<aop:config>
        		<aop:advisor advice-ref="" pointcut="" pointcut-ref="">
        		</aop:advisor>
        	</aop:config>
        
    • aop:asepectj-aspect/
      • 我们主要使用的配置方式(AspectJ),配置一个具体的切面(切点+通知)。同时会产生9个对象(见下方代码)
    • <aop:scoped-advisor />
      • spring自带的aop方式,会产生两个对象

    Advisor是Ponitcut和Advice的组合对象。一个目标对象可以被多个Advisor进行功能增强

    通过解析AOP所有配置的标签的过程(注解配置同理),我们得到了产生代理对象的代理对象创建器,得到配置的多个Advisor(Advice + Pointcut)。下一步我们就要产生具体的代理对象,并执行。
public BeanDefinition parse(Element element, ParserContext parserContext) {
		// 获取产生代理对象的代理对象创建器
		// AspectJAwareAdvisorAutoProxyCreator
		configureAutoProxyCreator(parserContext, element);

		for (Element elt: childElts) {
			// 获取的是aop标签冒号后面的标签名称(aop:pointcut,aop:advisor,aop:aspect)
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
				// 产生AspectJExpressionPointcut对象
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
				// spring自带的aop功能
				// DefaultBeanFactoryPointcutAdvisor
				// AspectJExpressionPointcut
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
				// AspectJExpressionPointcut 切点表达式

		    // MethodLocationFactoryBean
				// SimpleBeanFactoryAwareAspectInstanceFactory

				// AspectJPointcutAdvisor // 具体的Advisor,一个Advisor对应一个Advice

				// 五种通知类型
				// AspectJAfterReturningAdvice
				// AspectJMethodBeforeAdvice
				// AspectJAfterAdvice
				// AspectJAroundAdvice
				// AspectJAfterThrowingAdvice
				parseAspect(elt, parserContext);
			}
		}

}