• SpringBoot(四)原理剖析:AOP原理


      AOP(Aspect Oriented Programming)是基于切面编程的,可无侵入的在原本功能的切面层添加自定义代码,一般用于日志收集、权限认证等场景。

    AOP基本概念

      通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理;

    Before :前置通知,在连接点方法前调用;对应Spring中@Before注解;
    After :后置通知,在连接点方法后调用;对应Spring中的@After注解;
    AfterReturning:返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常;对应Spring中的@AfterReturning注解;
    AfterThrowing:异常通知,当连接点方法异常时调用;对应Spring中的@AfterThrowing注解;
    Around:环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法;对应Spring中的@Around注解;

      连接点(Join Point): 连接点表示应用执行过程中能够插入切面的一个点。在 Spring AOP 中,连接点总是方法的调用,可以说目标对象中的方法就是一个连接点;
      切点(Pointcut): 就是连接点的集合;对应Spring中的@Pointcut注解;

    目前Spring支持的切点匹配表达式主要有以下几种:
    execution:可以定义到的最小粒度是方法,修饰符,包名,类名,方法名,Spring AOP主要也是使用这个匹配表达式;
    within:只能定义到类;例如@Pointcut(within(com.jnu.example.*))
    this:当前生成的代理对象的类型匹配;
    target:目标对象类型匹配;
    annotation:针对注解;
    args:只针对参数;
    
    例如: execution (* com.sample.service..*. *(..)) 整个表达式可以分为五个部分:
    1、execution()::表达式主体;
    2、第一个*号:表示返回类型, *号表示所有的类型;
    3、包名:表示需要拦截的包名,包名后面的..,表明com.sample.service包、及其子包;
    4、第二个*号:表示类名,*号表示所有的类;
    5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个点表示任何参数; 

      切面(Aspect): 切面是通知和切点的结合;对应Spring中的注解@Aspect修饰的一个类;
      目标对象(Target object):即被代理的对象;    
      代理对象(AOP proxy):包含了目标对象的代码和增强后的代码的那个对象;

       自定义注解实例可以参见:注解

    @EnableAspectJAutoProxy分析

      在spring-boot-autoconfigure-**.jar中,有个配置类,来控制AOP功能的开启和关闭:

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
    public class AopAutoConfiguration {
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnClass(Advice.class)
        static class AspectJAutoProxyingConfiguration {
    
            @Configuration(proxyBeanMethods = false)
            @EnableAspectJAutoProxy(proxyTargetClass = false)
            @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
                    matchIfMissing = false)
            static class JdkDynamicAutoProxyConfiguration {
    
            }
    
            @Configuration(proxyBeanMethods = false)
            @EnableAspectJAutoProxy(proxyTargetClass = true)
            @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
                    matchIfMissing = true)
            static class CglibAutoProxyConfiguration {
    
            }
    
        }
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnMissingClass("org.aspectj.weaver.Advice")
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
                matchIfMissing = true)
        static class ClassProxyingConfiguration {
    
            ClassProxyingConfiguration(BeanFactory beanFactory) {
                if (beanFactory instanceof BeanDefinitionRegistry) {
                    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
            }
    
        }
    
    }
    AopAutoConfiguration
    • 使用@ConditionalOnProperty注解,默认开启AOP功能;
    • spring.aop.proxy-target-class没有配置时,默认开启Cglib动态代理;
    • @EnableAspectJAutoProxy注解,声明开启AOP功能。

       @EnableAspectJAutoProxy注解的核心是引入了AspectJAutoProxyRegistrar,然后向容器中注册一个AnnotationAwareAspectJAutoProxyCreator:

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
        /**
         * Register, escalate, and configure the AspectJ auto proxy creator based on the value
         * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
         * {@code @Configuration} class.
         */
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
            AnnotationAttributes enableAspectJAutoProxy =
                    AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            if (enableAspectJAutoProxy != null) {
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
        }
    
    }
    registerBeanDefinitions

      此时下面源码中的第三个方法会进入if代码块内,然后返回null:

    @Nullable
        public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
            return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
        }
    
        @Nullable
        public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                BeanDefinitionRegistry registry, @Nullable Object source) {
    
            return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
        }
    
    @Nullable
        private static BeanDefinition registerOrEscalateApcAsRequired(
                Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    
            Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    
            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;
            }
    
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
            return beanDefinition;
        }
    AopConfigUtils

    AnnotationAwareAspectJAutoProxyCreator分析

      在AnnotationAwareAspectJAutoProxyCreator的继承关系中,有一个AbstractAutoProxyCreator类。该类定义了下面两个方法,它们分别拦截目标bean创建,和初始化流程:
        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            Object cacheKey = getCacheKey(beanClass, beanName);
            if (beanName == null || !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;
                }
            }
            // Create proxy here if we have a custom TargetSource.
            // Suppresses unnecessary default instantiation of the target bean:
            // The TargetSource will handle target instances in a custom fashion.
            if (beanName != null) {
                TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
                if (targetSource != null) {
                    this.targetSourcedBeans.add(beanName);
                    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                    Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
                    this.proxyTypes.put(cacheKey, proxy.getClass());
                    return proxy;
                }
            }
    
            return null;
        }
    
      //在创建其他bean时,调用该方法,创建代理的bean,从而将AOP方式注入的方法,注入到目标bean中
      @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;
      postProcessAfterInitialization方法中的wrapIfNecessary方法,会为目标对象动态地创建JdkDynamicAopProxy或者ObjenesisCglibAopProxy的代理对象。
     

    AOP实战

      日期代理:

     1 @Aspect
     2 @Component("logAspect")
     3 public class LogAspect {
     4 
     5     private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
     6 
     7     // 配置织入点
     8     @Pointcut("@annotation(com.ryj.annotation.Log)")
     9     public void logPointCut() {
    10     }
    11 
    12     /**
    13      * 是否存在注解,如果存在就获取
    14      */
    15     private static Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
    16         Signature signature = joinPoint.getSignature();
    17         MethodSignature methodSignature = (MethodSignature) signature;
    18         Method method = methodSignature.getMethod();
    19         if (method != null) {
    20             return method.getAnnotation(Log.class);
    21         }
    22         return null;
    23     }
    24 
    25 
    26     @Around("logPointCut()")
    27     public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    28         long  startTimeMillis = System.currentTimeMillis();
    29         Log controllerLog = getAnnotationLog(joinPoint);
    30         if (controllerLog == null) {
    31             return null;
    32         }
    33         // 获得方法名称
    34         String className = joinPoint.getTarget().getClass().getSimpleName();
    35         String methodName = joinPoint.getSignature().getName();
    36         String action = controllerLog.action();
    37         String title = controllerLog.title();
    38         Object[] args = joinPoint.getArgs();
    39         //序列化时过滤掉request和response
    40         List<Object> logArgs = Arrays.stream(args)
    41                 .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
    42                 .collect(Collectors.toList());
    43         String argStr = JSON.toJSONString(logArgs);
    44         //打印日志,如有需要还可以存入数据库
    45         log.info(">>>>>模块[{}]:{} 操作[{}]:{} 参数:{}",className,title,methodName,action,argStr);
    46         //log.info("访问的接口 ==> {} 请求参数 ==> {}" ,requestPath, argStr);
    47         Object result = joinPoint.proceed() ;
    48         log.info(">>>>>耗时 ==> {}ms" ,System.currentTimeMillis() - startTimeMillis);
    49         return result ;
    50     }
    51 
    52 }
    LogAspect
  • 相关阅读:
    【HTML】CSS中的margin、border、padding区别
    kill -9 和kill -15,区别,python可以自定义at_exit钩子
    Prometheus 监控Windows机器
    Prometheus下的 promQL
    (4.46)sql server中的rule
    【sql server alwayson】sql servrer alwayson的实例升级
    prometheus+grafana+Alertmanager基本实践
    prometheus+grafana安装部署(入门初探)
    运维监控概述
    mysql基础原理大全
  • 原文地址:https://www.cnblogs.com/ryjJava/p/14427303.html
Copyright © 2020-2023  润新知