前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring5.x-AOP实现原理及源码分析

spring5.x-AOP实现原理及源码分析

作者头像
逍遥壮士
发布2023-09-01 17:06:46
1580
发布2023-09-01 17:06:46
举报
文章被收录于专栏:技术趋势技术趋势

什么是springAop?

Spring AOP(Aspect-Oriented Programming)是 Spring 框架的一个重要特性,它提供了一种基于横切关注点的编程范式。AOP 允许开发人员将横切关注点(如日志记录、事务管理、安全性等)与核心业务逻辑分离,从而实现更好的代码结构和可维护性。

在传统的面向对象编程中,功能通常会散布在应用程序的各个模块中,例如对数据库的访问、异常处理等。这导致了大量的重复性代码,难以维护和扩展。而 AOP 提供了一种可以在不修改原始代码的情况下,通过声明的方式来定义横切关注点,并将其应用到应用程序的多个模块中。

在 Spring AOP 中,横切关注点称为切面(Aspect),而被切入的对象称为目标对象(Target Object)。AOP 的实现依赖于代理模式和动态代理技术,Spring 提供了两种主要的 AOP 实现方式:

  1. 基于代理的 AOP:这种方式使用 JDK 动态代理或 CGLIB 动态代理来创建代理对象,并将切面织入到目标对象的方法调用中。如果目标对象实现了接口,则使用 JDK 动态代理;否则,使用 CGLIB 动态代理。基于代理的 AOP 可以在运行时将切面逻辑织入到目标对象中。
  2. 纯 Java 编译时植入的 AOP:这种方式通过编译时的字节码增强来实现 AOP,它使用 AspectJ 的注解和编译器,在编译过程中将切面织入到目标对象的字节码中。与基于代理的 AOP 不同,纯 Java 编译时植入的 AOP 在运行时不需要动态代理,因此性能更高。

Spring AOP 支持以下常见的切面功能:

  • 前置通知(Before advice):在目标方法调用之前执行的操作。
  • 后置通知(After advice):在目标方法调用之后执行的操作,无论是否发生异常。
  • 返回通知(After returning advice):在目标方法成功返回结果后执行的操作。
  • 异常通知(After throwing advice):在目标方法抛出异常后执行的操作。
  • 环绕通知(Around advice):包围目标方法调用,可以在调用前后执行自定义操作。

Spring AOP 是一种实现面向切面编程的机制,可以帮助开发人员更好地管理横切关注点,并提供了灵活的方法来处理日志、事务、安全等通用功能,提高代码的可重用性和可维护性。

spring AOP基础使用

请参考原来写过的文章:spring的AOP

spring AOP 源码学习

AOP初始化

当spring初始化到org.springframework.context.support.AbstractApplicationContext#refresh() 时里面有一个通知处理器的初始化,会调用到如下的方法进行初始化(其它忽略看前面文章)

当通过注解在自定义配置类或启动类上@EnableAspectJAutoProxy(proxyTargetClass = true)标识着该项目启用了AOP。

配置启动类注解:

代码语言:javascript
复制
@EnableAspectJAutoProxy

进入@EnableAspectJAutoProxy 发现这里通过@import导入了AspectJAutoProxyRegistrar.class

代码位置:org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions

类图:

代码语言:javascript
复制
@Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //通过反射方式进行注入

    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

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

也就是说spring在启动的时候,会通过@EnableAspectJAutoProxy注解来识别是否启动aop,如果加了默认就启动了这个aop,通过这个注解中的@import 中导入了这个类AspectJAutoProxyRegistrar进行初始化,而类中有一个registerBeanDefinitions方法进行。

然后会进入:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean中的

代码语言:javascript
复制
exposedObject = this.initializeBean(beanName, exposedObject, mbd);

注意:这里其实最顶层是AnnotationAwareAspectJAutoProxyCreator只是底层是AbstractAutowireCapableBeanFactory。看下面类图:

类位置:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#initBeanFactory

这里是初始化bean的工厂类。其实这里还是在refresh中。

代码语言:javascript
复制
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.initBeanFactory(beanFactory);
        if (this.aspectJAdvisorFactory == null) {
            this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
        }
//用于管理切面通知的构建和配置,用于实现横切关注点的处理。
        this.aspectJAdvisorsBuilder = new AnnotationAwareAspectJAutoProxyCreator.BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
    }

到这里就要注意了,后面就是去调IOC(原来有写过不清楚可以去看看原来的文章)。

代码位置:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation

代码语言:javascript
复制
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    //缓存获取key
        Object cacheKey = this.getCacheKey(beanClass, beanName);
    //为空或不包含
        if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
      //如果有有解析直接返回
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
      //如果不是基础bean就直接跳过
            if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
    //如果beanName不是空
        if (beanName != null) {
            TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
      //获取目标路劲不为空 通过工理方式获取并放到代理缓存中
            if (targetSource != null) {
                this.targetSourcedBeans.add(beanName);
                Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
        }

        return null;
    }

接着进入到shouldSkip,这个方法主要是用来判断是否跳过特定的bean

代码位置:org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#shouldSkip

代码语言:javascript
复制
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        Iterator var4 = candidateAdvisors.iterator();

        Advisor advisor;
        do {
            if (!var4.hasNext()) {
                return super.shouldSkip(beanClass, beanName);
            }

            advisor = (Advisor)var4.next();
        } while(!(advisor instanceof AspectJPointcutAdvisor) || !((AbstractAspectJAdvice)advisor.getAdvice()).getAspectName().equals(beanName));

        return true;
    }

通过上面的findCandidateAdvisors找到findAdvisorBeans这个方法用来构建切面的vdisors。

代码位置:org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans

代码语言:javascript
复制
//获取所有通知者
public List<Advisor> findAdvisorBeans() {
    //通知者名称列表
        String[] advisorNames = null;
    //同步锁
        synchronized(this) {
      //获取缓存列表
            advisorNames = this.cachedAdvisorBeanNames;
      //如果为空
            if (advisorNames == null) {
        //通过从工厂类中去获取(这里不深入前端ioc类似)
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
        //然后进行赋值给缓存
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
    //长度为空直接返回空
        if (advisorNames.length == 0) {
            return new LinkedList();
        } else {
      //初始化列表
            List<Advisor> advisors = new LinkedList();
            String[] var3 = advisorNames;
            int var4 = advisorNames.length;
      //从ioc容器中获取所有的bean名称
            for(int var5 = 0; var5 < var4; ++var5) {
                String name = var3[var5];
                if (this.isEligibleBean(name)) {
                    if (this.beanFactory.isCurrentlyInCreation(name)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Skipping currently created advisor '" + name + "'");
                        }
                    } else {
                        try {
                            advisors.add(this.beanFactory.getBean(name, Advisor.class));
                        } catch (BeanCreationException var11) {
                            Throwable rootCause = var11.getMostSpecificCause();
                            if (rootCause instanceof BeanCurrentlyInCreationException) {
                                BeanCreationException bce = (BeanCreationException)rootCause;
                                String bceBeanName = bce.getBeanName();
                                if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                                    if (logger.isDebugEnabled()) {
                                        logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + var11.getMessage());
                                    }
                                    continue;
                                }
                            }

                            throw var11;
                        }
                    }
                }
            }

            return advisors;
        }
    }

接着进入isAspect方法。

代码位置:org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspect

代码语言:javascript
复制
public boolean isAspect(Class<?> clazz) {
        return this.hasAspectAnnotation(clazz) && !this.compiledByAjc(clazz);
    }

    private boolean hasAspectAnnotation(Class<?> clazz) {
        return AnnotationUtils.findAnnotation(clazz, Aspect.class) != null;
    }

代码位置:org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors

代码语言:javascript
复制
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    //获取aspect类
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    //获取切面类的名称
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    //校验切面类
        this.validate(aspectClass);
    //通过包装类包装
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
        List<Advisor> advisors = new ArrayList();
    //获取所有切面类 @PointCut注解的方法
        Iterator var6 = this.getAdvisorMethods(aspectClass).iterator();

        while(var6.hasNext()) {
            Method method = (Method)var6.next();
      //循环解析切面中的方法
            Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }

        Field[] var12 = aspectClass.getDeclaredFields();
        int var13 = var12.length;

        for(int var14 = 0; var14 < var13; ++var14) {
            Field field = var12[var14];
            Advisor advisor = this.getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
    }

下面就是通过这个来获取切面类,当然这个是有顺序的。

代码位置:org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorMethods

代码语言:javascript
复制
//获取一个切面类(aspectClass)中的方法列表
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
        final List<Method> methods = new ArrayList();
        ReflectionUtils.doWithMethods(aspectClass, new MethodCallback() {
            public void doWith(Method method) throws IllegalArgumentException {
                if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
                    methods.add(method);
                }

            }
        });
        Collections.sort(methods, METHOD_COMPARATOR);
        return methods;
    }

顺序从头到属,在初始化的时候就确定的,也就是我们执行时候的顺序:Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}

代码语言:javascript
复制
static {
        CompoundComparator<Method> comparator = new CompoundComparator();
        comparator.addComparator(new ConvertingComparator(new InstanceComparator(new Class[]{Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}), new Converter<Method, Annotation>() {
            public Annotation convert(Method method) {
                AspectJAnnotation<?> annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
                return annotation != null ? annotation.getAnnotation() : null;
            }
        }));
        comparator.addComparator(new ConvertingComparator(new Converter<Method, String>() {
            public String convert(Method method) {
                return method.getName();
            }
        }));
        METHOD_COMPARATOR = comparator;
    }

通过前面的逻辑最后到这个InstantiationModelAwarePointcutAdvisorImpl 来创建aop的具体实现。

代码语言:javascript
复制
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    //切面定义切点
        this.declaredPointcut = declaredPointcut;
    //切面对象
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    //切面方法名称
        this.methodName = aspectJAdviceMethod.getName();
    //切面参数类型
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    //切面方法对象
        this.aspectJAdviceMethod = aspectJAdviceMethod;
    //asperctj通知工厂
        this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    //aspect实例工厂
        this.aspectInstanceFactory = aspectInstanceFactory;
    //切面具体顺序
        this.declarationOrder = declarationOrder;
    //切面名称
        this.aspectName = aspectName;
    //判断是否需要延迟加载
        if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
            this.pointcut = new InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
            this.lazy = true;
        } else {
            this.pointcut = this.declaredPointcut;
            this.lazy = false;
            this.instantiatedAdvice = this.instantiateAdvice(this.declaredPointcut);
        }

    }

上面最终就是通是实现bean的创建。

光初始化就这么一大陀代码(部分还省略了~),真是~

动态代理

Spring默认的代理方式是JDK动态代理,但如果目标对象没有实现接口,则会自动切换到CGLIB动态代理。这样可以确保在大多数情况下都能使用AOP功能,无需手动配置代理方式。

通过前面的初始化后,那么当调用方法时,spring默认会有一个拦截链将代理和切面连接起来,通过invoke反射的方式将这个代理进行匹配。

代码位置:org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

代码语言:javascript
复制
//AOP 拦截器链可以按照顺序依次执行拦截器或拦截器通知的逻辑
public Object proceed() throws Throwable {
    //最后一个拦截器会进入
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      //返回切点
            return this.invokeJoinpoint();
        } else {
      //获取需要运行的拦截器
            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
                return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
            } else {
        //执行拦截器方法
                return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    }

上面这个方法一直是递归执行。

然后进入:invoke方法

代码位置:org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke

代码语言:javascript
复制
public Object invoke(MethodInvocation mi) throws Throwable {
        MethodInvocation oldInvocation = (MethodInvocation)invocation.get();
        invocation.set(mi);

        Object var3;
        try {
            var3 = mi.proceed();
        } finally {
            invocation.set(oldInvocation);
        }

        return var3;
    }

可以看到这里就是最终执行的拦截器,这里使用责任链设计模式进行递归调用。顺序就是:Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class} 跟初始化顺序是一致的。

至于责任链设计模式不懂的同学请看:设计模式-责任链模式

最后

spring aop相对来说很不好去调试,特别各种代理,若看不懂本文同学请看下面参考文章多参考借鉴。当然如果看得懂,建议可以看看一些开源框架中的日志或者被spring、springboot、springcloud整合进来的一些中间件或相关组件值得看看这些实现逻辑和原理其实都是一样的。当然本人在公作中看得比较多的还是用于全局的日志、自定义拦截器比如加密等这些工具的实现。

参考:

https://cloud.tencent.com/developer/article/1512235

https://www.cnblogs.com/FatalFlower/p/15572344.html

https://blog.csdn.net/pengjianglilive/article/details/109608986

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 技术趋势 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是springAop?
  • spring AOP基础使用
    • AOP初始化
    相关产品与服务
    云顾问
    云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档