likes
comments
collection
share

彻底搞懂Spring AOP

作者站长头像
站长
· 阅读数 13

AOP(Aspect Oriented Programming)是一种面向切面的编程思想

Spring AOP 作为Spring最核心的能力之一,基于动态代理,允许开发者定义切面并插入横切关注点,通过AOP我们可以将一些通用代码(如日志记录、权限判断等)和业务代码分离开,使得我们的业务代码更加专注于业务逻辑的处理

Spring AOP实现

AOP的使用方式相对简单(这里基于注解方式)

自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OptStatus {

  //自定义注解

}

在需要增强的方法上加入我们自定义的注解

    @OptStatus
    public void queryList(String OrderQuery) {
         
         //业务代码
         ...
         ...
    }

创建其对应的切面

@Slf4j
@Aspect
@Component
public class PageStatusAspect {
    
    //声明要进行代理的注解
    @Pointcut("@annotation(com.apec.order.aop.annotation.OptStatus)")
    public void pointCut() {
    }

    /**
     *
     * @param joinPoint
     * @return
     */
    @SneakyThrows
    @Around("pointCut()")
    public Object PageStatusDecoration(ProceedingJoinPoint joinPoint) {
      
      //业务代码
      ...
      ...
    }
   }

实现不是本文的重点(网上有很多),在这里就随便提一下(ps:如果需要具体的案例,后面再出相应的文章)

Spring AOP源码解析

spring aop基于动态代理,那么spring会在bean初始化的时候,为目标对象创建代理对象放到容器中,后续我们在调用目标方法的时候,我们就会拿到代理对象,从而通过代理对象调用目标对象方法

创建代理对象

initializeBean() bean初始化方法,主要包含以下内容:

  • Bean Aware方法回调
  • BeanPostProcessor 初始化前回调
  • 执行初始化方法
  • BeanPostProcessor 初始化后回调
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    // 如果存在SecurityManager,执行相应的安全代码
    if (System.getSecurityManager() != null) {
        // ... 
    }
    else {
        // 如果bean实现了特定的Aware接口(如BeanNameAware, BeanFactoryAware等),则调用相应的方法
        invokeAwareMethods(beanName, bean);
    }

    // 初始化前的预处理,调用所有BeanPostProcessors的postProcessBeforeInitialization方法
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // 调用bean的初始化方法,例如afterPropertiesSet和custom init-method
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        // ... 
    }

    // 初始化后的后处理,调用所有BeanPostProcessors的postProcessAfterInitialization方法(包含了AOP代理后置处理器)
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    // 返回最终的bean实例,可能被AOP代理等包装
    return wrappedBean;
}

先看一下applyBeanPostProcessorsAfterInitialization()方法源码

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {

   // 保存原始Bean对象
   Object result = existingBean;
   
   // 如果有回调函数 返回null,使用原始的Bean对象,否则使用返回值作为当前Bean对象
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      Object current = processor.postProcessBeforeInitialization(result, beanName);
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}

为什么说applyBeanPostProcessorsAfterInitialization()方法完成了AOP动态代理?

这里我们先了解一下这个类BeanPostProcessor,它提供 Spring Bean 初始化前和初始化后的生命周期回调,允许对关心的 Bean 进行扩展,甚至是替换

在后置处理器中有一个AbstractAutoProxyCreator后置处理器 彻底搞懂Spring AOP

顾名思义这个类就是用来构建spring bean的代理对象的,它里面有一个createProxy()方法,在这个方法中会通过代理工厂proxyFactory.getProxy(classLoader)来创建代理对象,主要看createAopProxy()getProxy()方法

public Object getProxy(@Nullable ClassLoader classLoader) {
   return createAopProxy().getProxy(classLoader);
}

我们都知道spring aop提供了两种代理方式JDK动态代理和CGLIB代理,bean 使用哪一种代理方式呢?就由createAopProxy()方法决定

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   if (!NativeDetector.inNativeImage() &&
         (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) || ClassUtils.isLambdaClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      // 否则使用CGLIB代理
      return new ObjenesisCglibAopProxy(config);
   }
   else {
   // 根据配置使用JDK动态代理 配置项spring.aop.proxy-target-class
      return new JdkDynamicAopProxy(config);
   }
}

前面已经确定了使用哪一种代理模式,getProxy()方法就是获取对应的代理对象(基本上就是JDK动态代理和CGLIB代理的原理,这里就不多说了)

调用目标对象

当用户调用目标对象的某个方法时,实际会调用代理对象的invoke()方法,在这个方法中会调用MethodInvocationproceed()方法(按顺序执行符合所有AOP拦截规则的拦截器链),每一种Aspect(前置、后置、环绕、异常)都是一个MethodInvocation类。

彻底搞懂Spring AOP

MethodInterceptorinvoke()方法中会触发对目标对象方法的调用,也就是(ReflectiveMethodInvocation)反射调用目标对象的方法

彻底搞懂Spring AOP

总结

Spring AOP 提供了两种代理方式JDK动态代理和CGLIB代理(根据目标类来选择其中一种)

在通过使用@EnableAspectJAutoProxy注解开启AOP后,会向spring容器中注入一个后置处理器AbstractAutoProxyCreator,在bean初始化后,对需要代理的目标对象,生成代理对象。 当用户调用目标对象的某个方法时,通过MethodInterceptorinvoke()方法中会触发对目标对象方法的调用