一、spring中AOP解析(注解式)

AOP介绍与使用

AOP直译为面向切面编程,解释为在不改变代码顺序的前提下,实现在一个顺序执行的逻辑代码之间插入扩展逻辑的的目的;举个例子:一个逻辑A执行顺序是X->Y,现在有另外一个逻辑C,想要在不改变逻辑A代码的前提下将逻辑C插入到X和Y之间,将逻辑A执行顺序改为X->C->Y,这就是切面编程的应用(方法增强)。

spring中已经默认实现了AOP功能,我们可以通过简单的配置就能使用AOP实现对指定方法的业务扩展。下面我们来写一个简单的AOP实例:

net.aop.AopTest.java(切面类)

@EnableAspectJAutoProxy
@Aspect
@Component
public class AopTest {
   //切点方法(代指被代替的方法)
   //这里使用了execution表达式(任意返回值 net.aop包及子包的所有类的所有方法 参数数量不限)
   @Pointcut("execution(* net.aop..*(..))")
   public void aspect() { }

   //切点方法前执行
   @Before("aspect()")
   public void before(JoinPoint joinPoint){
      System.out.println("===========Before切点前======="+joinPoint);
   }

   //环绕切点方法执行
   @Around("aspect()")
   public void Around(JoinPoint joinPoint) throws Throwable {
      System.out.println("===========Around环绕前======="+joinPoint);
      ((ProceedingJoinPoint)joinPoint).proceed();
      System.out.println("===========Around环绕后======="+joinPoint);
   }

   //切点方法后执行
   @After("aspect()")
   public void after(JoinPoint joinPoint){
      System.out.println("===========After切点后======="+joinPoint);
   }

   //切点方法返回后执行
   @AfterReturning("aspect()")
   public void afterReturning(JoinPoint joinPoint){
      System.out.println("===========afterReturning返回后======="+joinPoint);
   }

   //切点方法抛出异常后执行
   @AfterThrowing("aspect()")
   public void afterThrowing(JoinPoint joinPoint){
      System.out.println("===========AfterThrowing异常抛出后======="+joinPoint);
   }
}

net.aop.ExecutorBean.java(被扩展的bean)

@Component
public class ExecutorBean {

   private String msg = "msg";

   public void test(){
      System.out.println("========="+msg);
   }
}

通过spring调用:

@Test
public void test() {
   ApplicationContext configApplicationContext = new AnnotationConfigApplicationContext("net.aop");
   ExecutorBean bean = configApplicationContext.getBean(ExecutorBean.class);
    bean.test();
}

//执行结果:
===========Around环绕前=======execution(void net.aop.ExecutorBean.test())
===========Before切点前=======execution(void net.aop.ExecutorBean.test())
=========msg
===========Around环绕后=======execution(void net.aop.ExecutorBean.test())
===========After切点后=======execution(void net.aop.ExecutorBean.test())
===========afterReturning返回后=======execution(void net.aop.ExecutorBean.test())

上述实例在没有改变ExecutorBean的前提下,实现了对于test()方法的扩展。主要逻辑是创建了一个切面bean,将切面bean的切点指向了net.aop包下的所有类的所有方法,也就是执行net.aop包下的所有方法都会触发执行切面定义的扩展方法。


注意:spring中使用AOP,需要开启AOP解析器,上述实例全部使用注解,注解开启AOP的是@EnableAspectJAutoProxy,而XML式的配置是<aop:aspectj-autoproxy/>,都只需要配置一次就可以启用AOP功能。

spring中AOP的解析

(注意:下文内容需要知道到springIOC创建流程、spring对beanProsessor接口类的应用和JDK、Cglib动态代理相关知识,请提前了解相关知识要点)

spring创建IOC容器时,会先根据获取到bean信息创建一个BeanDefinition对象列表,之后根据BeanDefinition列表对bean进行一一实例化。而AOP的逻辑就是使用beanProsessor接口类拦截了AOP被代理类的创建,动态创建了一个织入切面方法的被代理类bean,之后将这个bean加入IOC容器提供使用。下面会通过spring源码了解这个过程:

  1. 上文我们通过一个切面类AopTest和**@EnableAspectJAutoProxy就完成了AOP的配置,这里我们从AOP开关@EnableAspectJAutoProxy**开始看起:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

   boolean proxyTargetClass() default false;

   boolean exposeProxy() default false;

}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

   @Override
   public void registerBeanDefinitions(
         AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		//注册AnnotationAwareAspectJAutoProxyCreator.class到spring中
      AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		//代码省略...	
   }

}

其中@EnableAspectJAutoProxy中的关键是@Import(AspectJAutoProxyRegistrar.class) @Import注解的作用是将参数class封装成BeanDefinition注册到spring中;但是AspectJAutoProxyRegistrar.class是一个注册器对象(ImportBeanDefinitionRegistrar接口对象),spring不会直接将注册器对象直接注册到spring中,而是在之后会执行ImportBeanDefinitionRegistrar接口对象的registerBeanDefinitions方法,将注册器中指定的对象注册到spring中。AspectJAutoProxyRegistrar.class的接口方法注册的是AnnotationAwareAspectJAutoProxyCreator.class对象,这个对象是一个BeanPostProcessor接口对象,也是AOP的功能实现核心对象。继承链如下图所示:

spring aop注解传参 spring aop注解详解_实例化

AnnotationAwareAspectJAutoProxyCreator对象会和其他BeanPostProcessor接口对象一样,会提前实例化并加入spring的BeanPostProcessors列表中,在之后的spring bean预实例化中会循环这个BeanPostProcessors列表执行拦截方法作为创建其他bean时的扩展拦截器;主要拦截方法有两个,都在父类AbstractAutoProxyCreator中,一个是postProcessBeforeInstantiation()在bean实例化前执行,另一个是postProcessAfterInitialization在bean初始化完成后执行,代码逻辑如下:

//org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    	Object cacheKey = getCacheKey(beanClass, beanName);
    
    	if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
    		if (this.advisedBeans.containsKey(cacheKey)) {
    			return null;
    		}
    		if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
    			this.advisedBeans.put(cacheKey, Boolean.FALSE);
    			return null;
    		}
    	}
    
    	//省略...
    
    	return null;
    }
    
    //AspectJAwareAdvisorAutoProxyCreator#shouldSkip
    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    	// TODO: Consider optimization by caching the list of the aspect names
    	List<Advisor> candidateAdvisors = findCandidateAdvisors(); //查询切面bean列表
    	for (Advisor advisor : candidateAdvisors) {
    		if (advisor instanceof AspectJPointcutAdvisor &&
    				((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
    			return true;
    		}
    	}
    	return super.shouldSkip(beanClass, beanName);
    }
    
    //AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
    @Override
    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.
    	if (this.aspectJAdvisorsBuilder != null) {
            //构建有@Aspect注解的切面bean信息及对应的通知方法列表
    		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    	}
    	return advisors;
    }

我们以上文的ExecutorBean为例,在spring预实例化时,要通过ExecutorBean的BeanDefinition对象去实例化ExecutorBean,在实例化前会执行postProcessBeforeInstantiation()方法,此方法的主要逻辑是循环spring中bean列表,找到有@Aspect注解的切面bean即AopTest,将有AOP中通知注解(@Before、@After等)的方法封装成一个个Advisor接口对象(实际是InstantiationModelAwarePointcutAdvisorImpl对象),这个对象中创建了和通知注解对应的回调对象(MethodInterceptor接口对象,如:AspectJMethodBeforeAdvice),在循环完AopTest的方法后将封装的Advisor接口对象组织成list,放入advisorsCache<beanName, advisorList> Map缓存中。以上逻辑存在于postProcessBeforeInstantiation()中的shouldSkip方法中(shouldSkip主要判断是否要跳过当前bean),只有在实例化用户自定义的第一个bean时才会执行完成,在之后实例化其他bean时会直接返回advisorsCache缓存数据。


postProcessBeforeInstantiation()执行完后开始bean的创建和初始化,初始化完后执行postProcessAfterInitialization()方法,代码逻辑如下:

//org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
     @Override
     public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
     	if (bean != null) {
     		Object cacheKey = getCacheKey(bean.getClass(), beanName);
     		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
     			return wrapIfNecessary(bean, beanName, cacheKey);
     		}
     	}
     	return bean;
     }
     
     //AbstractAutoProxyCreator#wrapIfNecessary
     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
     	if (StringUtils.hasLength(beanName) && 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.(判断当前bean是否匹配到某个切面切点表达式)
     	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;
     }
     
     //org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
     @Override
     public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
     	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
     		Class<?> targetClass = config.getTargetClass();
     		if (targetClass == null) {
     			throw new AopConfigException("TargetSource cannot determine target class: " +
     					"Either an interface or a target is required for proxy creation.");
     		}
             //JDK代理
     		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
     			return new JdkDynamicAopProxy(config);
     		}
             //Cglib代理
     		return new ObjenesisCglibAopProxy(config);
     	}
     	else {
             //JDK代理
     		return new JdkDynamicAopProxy(config);
     	}
     }

postProcessAfterInitialization()的主要逻辑主要在方法wrapIfNecessary()中,该方法这主要逻辑是获取之前的切面Map缓存advisorsCache,通过切面的切点表达式来匹配当前bean路径,若匹配成功则返回对应切面的Advisor接口对象列表,进而执行createProxy方法创建bean的代理对象,创建代理对象会选择是JDK方式创建还是Cglib方式创建,创建时会将Advisor接口对象列表设置到代理对象中。代理对象创建完成后会替换创建的原实例化对象放入IOC容器中,当调用代理对象方法时会将Advisor接口对象列表设置到代理对象的回调方法中,并按顺序一一执行切面的通知方法逻辑,以此完成AOP的代理调用。


总结逻辑
  1. 通过**@EnableAspectJAutoProxy获取AspectJAutoProxyRegistrar注册器,AspectJAutoProxyRegistrar会执行注册器方法registerBeanDefinitions注册AnnotationAwareAspectJAutoProxyCreator**(继承BeanPostProcessor接口)到spring的 beanDefinition列表中
  2. AnnotationAwareAspectJAutoProxyCreator在实例化后加入BeanPostProcessors列表中
  3. ExecutorBean预实例化时,在createBean()方法中循环BeanPostProcessors列表执行AnnotationAwareAspectJAutoProxyCreatorpostProcessBeforeInstantiation获取切面方法列表(MethodInterceptor接口对象)
  4. 之后在doCreateBean()#initializeBean()方法中循环BeanPostProcessors列表执行postProcessAfterInitialization方法—>通过wrapIfNecessary方法判断是否需求创建代理对象(若点面方法的execution表达式匹配到了当前bean才会创建代理对象)选择用JDK或Cglib创建bean代理对象(插入切面bean的方法),将之前的切面方法列表赋值得到代理对象的回调列表中,之后返回代理对象完成实例化
  5. 调用ExecutorBean的test方法会调用Cglib代理对象的invoke方法触发切面方法

  1. 附录:BeanPostProcessor接口在spring中创建bean的关键位置图:

spring aop注解传参 spring aop注解详解_spring_02