Spring AOP之aspectj切面增强过程理解

aspectj下主要有@Before@After@AfterReturning@AfterThrowing@Around几个通知的方式。在实际的开发过程中也是经常要用到的,之前只能停留在会用的层面上,为了更深入的了解这些方法执行的时机和原理,只能学习源码,这里分享出来,希望和大家共同学习进步。下面直接上代码:

需要实现的功能

调用AspectCenter类中的doit()方法,切面需要切到这个方法并按照切面规则执行增强方法和业务逻辑方法。

  • 引入依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
  • 启动配置类的代码:
@Configuration
@ComponentScan(basePackages = {"com.zdydoit.core.aop"})
@EnableAspectJAutoProxy //开启aspectj自动代理功能
public class AOPInit {

    // 创建AspectCenter的bean实例
    @Bean
    public AspectCenter aspectCenter(){
        return new AspectCenter();
    }
}
  • AspectCenter类代码:
public class AspectCenter {

    @PointCutAnno // 切点注解
    public void doit(){
        System.out.println("==>业务逻辑代码执行!");
    }
}
  • AspectHandle切面代码:
@Component
@Aspect
public class AspectHandle {
	// 基于注解的起点定义
    @Pointcut("@annotation(com.zdydoit.core.aop.PointCutAnno)")
    public void pointcut(){}

    @Before("pointcut()")
    public void doBefore(){
        System.out.println("==>before Handle");
    }

    @After("pointcut()")
    public void doAfter(){
        System.out.println("==>after Handle");
    }

    @AfterReturning("pointcut()")
    public void doAfterReturning(){
        System.out.println("==>afterReturning Handle");
    }

    @AfterThrowing("pointcut()")
    public void doAfterThrowing(){
        System.out.println("==>afterThrowing Handle");
    }

    @Around("pointcut()")
    public void doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("==>doAround Handle Before");
            joinPoint.proceed();
        System.out.println("==>doAround Handle After");
    }
}
  • 切点注解PointCutAnno
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PointCutAnno {
}
  • 测试代码:
@Test
public void testAop(){
    // 初始化IOC容器
    ApplicationContext app = new AnnotationConfigApplicationContext(AOPInit.class);
    // 从IOC容器中获取bean
    AspectCenter center  = (AspectCenter) app.getBean("aspectCenter");
    //执行目标方法
    center.doit();
}

这里的代码其实很简单,但是涉及到的注意点还是有的,下面依次来说明。

重点说明
  • @EnableAspectJAutoProxy

@EnableAspectJAutoProxy是aspectj的自动代理功能开启注解,这个注解的上又加了一个@Import注解,在注解上导入了AspectJAutoProxyRegistrar类,如果对IOC容器初始化有所了解的可以知道,在bean的注册和初始化,通过@Import也是一种方式。具体内容不错,可以参考之前的博客(Spring IOC初始化bean对象创建的N种实现方式理解)。

  • IOC处理器

在Spring IOC容器初始化的时候,首先会注册很多处理器,容器在创建bean的时候,这些处理器会对bean进行前置增强和后置增强操作。可以简单理解成创建bean的时候,这一批处理器在之前做了点事,在之后做了点事。上一步说的到AspectJAutoProxyRegistrar类中registerBeanDefinitions执行后,会将一个处理器放到Spring IOC中。处理器的名称是org.springframework.aop.config.internalAutoProxyCreator,类型是AnnotationAwareAspectJAutoProxyCreator。下面看一下源码。

AspectJAutoProxyRegistrar类中registerBeanDefinitions方法源码:

@Override
public void registerBeanDefinitions(
		AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    // 注册处理器AspectJAnnotationAutoProxyCreator
	AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

	AnnotationAttributes enableAspectJAutoProxy =
			AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
	if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
		AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
	}
	if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
		AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
	}
}

从上面源码中的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法进入,依次经过registerAspectJAnnotationAutoProxyCreatorIfNecessary()registerOrEscalateApcAsRequired()。进入AopConfigUtils类中看registerOrEscalateApcAsRequired()方法源码如下。

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 判断IOC容器中是否已经注册了当前的这个bean
	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
		BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
		if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
			int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
			int requiredPriority = findPriorityForClass(cls);
			if (currentPriority < requiredPriority) {
				apcDefinition.setBeanClassName(cls.getName());
			}
		}
		return null;
	}
    /**
     * 定义处理器bean
     * cls:AnnotationAwareAspectJAutoProxyCreator.class
     */
	RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
	beanDefinition.setSource(source);
	beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
	beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //注册处理器bean
	registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
	return beanDefinition;
}
  • 代理生成时机

经过这里的了解可以知道,当Spring IOC初始化的时候AnnotationAwareAspectJAutoProxyCreator处理器会被创建,并在被切到的对象创建时做后置增强。具体怎么增强,下面继续。

IOC后置增强代理过程

IOC容器初始化创建bean的时候有很多步骤,这里就拿处理器的后置处理来说,之前有博客写过相关IOC容器初始化过程,可以借鉴(Spring IOC容器初始化基础过程源码了解)。

处理器的后置处理执行的位置是在AbstractAutowireCapableBeanFactory类中的initializeBean方法中,源码如下:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
	// ……

	Object wrappedBean = bean;
	if (mbd == null || !mbd.isSynthetic()) {
        // 处理器的前置处理
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}

	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;
}

在IOC容器初始化AspectCenter的时候,会进入到后置处理逻辑,在后置处理逻辑中会遍历所有的处理器,因为AspectCenter内的方法被切面切到,所以这里会进入到AnnotationAwareAspectJAutoProxyCreator的后置处理方法中。

源码:postProcessAfterInitialization()

AnnotationAwareAspectJAutoProxyCreator处理器中首先进入的是AbstractAutoProxyCreator类中的postProcessAfterInitialization()方法。(AbstractAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator父类)

@Override
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;
}
源码:wrapIfNecessary()

进入到wrapIfNecessary()方法后,这个方法很重要,也是后续要说的执行链的关键。

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;
}

原文上的注释也很清楚,如果有通知就创建代理对象。那接下来就是去获取通知方法,再去创建代理对象。

源码:findEligibleAdvisors()

getAdvicesAndAdvisorsForBean()方法依次进入到findEligibleAdvisors()方法中,这个方法中具体如下:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	// 获取通知方法
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
        // 将通知方法排序
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
	return eligibleAdvisors;
}

这里的逻辑并不复杂,获取当前类被增强的所有通知方法,然后将通知方法排序,然后返回。这里涉及到排序,可以知道在通知方法的执行过程是有先后顺序的。具体如何获取通知方法,如何排序这里不多做赘述。

源码:createProxy()

上面看完了通知方法的获取,通知方法获取后,带上通知方法进入到创建代理的方法中。看createProxy()方法源码:

protected Object createProxy(
		Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

	if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
		AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
	}
	// 创建代理工厂对象
	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);

	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}
	// 获取当前需要代理对象的通知信息,并将通知信息放入到代理工厂内
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	for (Advisor advisor : advisors) {
		proxyFactory.addAdvisor(advisor);
	}
	/**
   	 * 将targetSource设置到代理工厂内
   	 * targetSource:就是需要被增强的目标,就是AspectCenter
	 */
	proxyFactory.setTargetSource(targetSource);
	customizeProxyFactory(proxyFactory);
	
	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}
	// 调用代理工厂生成代理对象
	return proxyFactory.getProxy(getProxyClassLoader());
}

这个方法实现的基本过程有一下几步:

  • 创建代理工厂,用于生成代理对象;
  • 将生成代理对象所需要的信息设置到代理工厂内,如被代理的目标类,增强的方法等;
  • 上面两步准备工作结束,就可以调用代理工厂生成代理对象。

然后后面的创建过程就可以忽略了。

总结

下面来对上面的内容做个总结。

先看图:

在这里插入图片描述

图转文解释:

  • IOC容器初始化过程,通过@EnableAspectJAutoProxy注解创建aspectj相关的处理器;
  • IOC容器在初始化AspectCenter时,发现这个类需要被代理增强,上一步的处理器生效执行增强逻辑;
  • 增强的过程会获取对AspectCenter增强的通知方法集合,并将集合排序,形成一个执行链;
  • 生成代理对象,拥有一个执行链的代理对象。

到这里执行链大家可以猜的出来就是先执行前置增强方法,然后执行目标方法,在执行后置增强方法。事实也的确是这样的,知道实现思路,看源码就稍微轻松点。

执行目标方法过程

代码中虽然直接调用的是目标方法doit(),但是如果debug进入的话,会发现其实先走代理中的方法。

源码: intercept()

这里生成的代理对象使用的是CGLB方式生成,通多doit()被调用的位置debug进入。此时进入的方法就是intercept()方法,在CiglbAopProxy类中。

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
	Object oldProxy = null;
	boolean setProxyContext = false;
	Class<?> targetClass = null;
	Object target = null;
	try {
		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 = getTarget();
		if (target != null) {
			targetClass = target.getClass();
		}
        // 获取执行链
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		Object retVal;
		// Check whether we only have one InvokerInterceptor: that is,
		// no real advice, but just reflective invocation of the target.
		if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
			// 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 = methodProxy.invoke(target, argsToUse);
		}
		else {
			// We need to create a method invocation...
			// 创建CglibMethodInvocation对象,然后执行proceed方法
			retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
		}
		retVal = processReturnType(proxy, target, method, retVal);
		return retVal;
	}
	finally {
		if (target != null) {
			releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}

根据目标方法、参数、目标类等参数创建CglibMethodInvocation实例,然后执行实例内的proceed()方法。

源码:proeed()
@Override
public Object proceed() throws Throwable {
	//	We start with an index of -1 and increment early.
    // 每次将currentInterceptorIndex递增1,然后和执行链长度比较
	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);
		}
		else {
			// Dynamic matching failed.
			// Skip this interceptor and invoke the next in the chain.
			return proceed();
		}
	}
	else {
		// It's an interceptor, so we just invoke it: The pointcut will have
		// been evaluated statically before this object was constructed.
        //将包装通知方法的类转换成MethodInterceptor类型,并调用invoke方法
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

接着来看invoke()方法里面干了什么事。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	MethodInvocation oldInvocation = invocation.get();
	invocation.set(mi);
	try {
        // 注意这里又调用了proceed方法
		return mi.proceed();
	}
	finally {
		invocation.set(oldInvocation);
	}
}

这个方法里面有调用了上一步的proceed()方法,这个让我们想起了递归,对的,这里就是递归的思想,将执行链里面的类依次遍历出来,然后进行压栈,待执行链全部压栈后就会弹栈。使整个执行过程是有序的。

其他的invoke()方法就不看了,只是对proceed()方法调用的位置不同罢了。

总结
  • 首先调用到CglibAopProxy类的intercept()方法,创建Invocation对象,执行proceed()方法;
  • proceed()方法中依次获取通知方法封装类,然后通过invoke()方法执行通知方法;
  • invoke()方法内又会去调用proceed()方法,这里就形成了递归,直到执行链内的所有通知方法都被压栈后开始弹栈;
  • 依次弹栈,执行通知方法和目标方法。

Aspectj的通知方法执行顺序

如果想知道执行顺序其实很简单,这里就不再去跟源码,看压栈和弹栈的顺序。直接给出下面的结果。

在这里插入图片描述

到这里整个过程都说明清楚了,从代理对象的创建、执行链的形成到目标方法执行过程。整个内容中没有把源码都贴出来,也没有将源码一行一行的解释。感觉没有必要,博客的要旨是提供思想,主干思路。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿洞晓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值