spring5.x-AOPの実装原理とソースコード分析

上: spring5.x-listener の原理とソース コードの実装


このシリーズの記事:

                spring5.x-listener の原理とソースコードの実装

                spring5.x-solve 循環依存関係解析

                spring5.x-IOCモジュールのソースコード学習

                spring5.x とそれに対応する Spring ソースコード読み取り環境の紹介


SpringAopとは何ですか?

Spring AOP (アスペクト指向プログラミング) は Spring フレームワークの重要な機能であり、横断的な関心事に基づいたプログラミング パラダイムを提供します。AOP を使用すると、開発者は横断的な懸念事項 (ロギング、トランザクション管理、セキュリティなど) をコア ビジネス ロジックから分離できるため、コード構造と保守性が向上します。

従来のオブジェクト指向プログラミングでは、通常、データベースへのアクセス、例外処理などの機能がアプリケーションのさまざまなモジュールに分散されています。これにより、コードの繰り返しが多くなり、保守や拡張が困難になります。AOP は、宣言的な方法で横断的な関心事を定義し、元のコードを変更せずにアプリケーションの複数のモジュールに適用する方法を提供します。

Spring AOP では、横断的な関心事はアスペクトと呼ばれ、切り取られるオブジェクトはターゲット オブジェクトと呼ばれます。AOP の実装はプロキシ モードと動的プロキシ テクノロジに依存しており、Spring では 2 つの主要な AOP 実装メソッドが提供されています。

  1. プロキシベースの AOP: このアプローチでは、JDK 動的プロキシまたは CGLIB 動的プロキシを使用してプロキシ オブジェクトを作成し、アスペクトをターゲット オブジェクトのメソッド呼び出しに織り込みます。ターゲット オブジェクトがインターフェイスを実装している場合は、JDK 動的プロキシが使用され、それ以外の場合は CGLIB 動的プロキシが使用されます。プロキシベースの AOP は、実行時にアスペクト ロジックをターゲット オブジェクトに組み込むことができます。

  2. Pure Java コンパイル時に埋め込まれた AOP: このメソッドは、コンパイル時のバイトコード拡張を通じて AOP を実装し、AspectJ のアノテーションとコンパイラを使用して、コンパイル プロセス中にターゲット オブジェクトのバイトコードにアスペクトを織り込みます。プロキシベースの AOP とは異なり、純粋な Java コンパイル時の組み込み AOP は実行時に動的プロキシを必要としないため、パフォーマンスが高くなります。

Spring AOP は、次の共通アスペクト関数をサポートします。

  • アドバイス前: ターゲット メソッドが呼び出される前に実行されるアクション。

  • アドバイス後: 例外が発生したかどうかに関係なく、ターゲット メソッドが呼び出された後に実行されるアクション。

  • アドバイスを返した後: ターゲット メソッドが結果を正常に返した後に実行するアクション。

  • スロー後のアドバイス: ターゲット メソッドが例外をスローした後に実行するアクション。

  • アドバイスの周囲: ターゲットのメソッド呼び出しを囲み、呼び出しの前後にカスタム操作を実行できます。

Spring AOP はアスペクト指向プログラミングを実装するためのメカニズムであり、開発者が横断的な問題をより適切に管理できるようにするとともに、ロギング、トランザクション、セキュリティなどの一般的な機能を処理する柔軟な方法を提供して、コードの再利用性と可用性を向上させます。

Spring AOPの基本的な使い方

私が最初に書いた記事を参照してください: spring's AOP

Spring AOP ソースコードの学習

73725ee6b5bf7a7f3aa9e1c9d5cf9ef8.png

AOPの初期化

d2951792ca3bf582edfe146fd4364dde.png

Springが org.springframework.context.support.AbstractApplicationContext#refresh() に初期化されると、初期化通知プロセッサがあり、初期化のために次のメソッドが呼び出されます(その他は無視され、前の記事を読んでください)

6ae398dcd4be66d4aa0d0d6daee7b17c.png

カスタム構成クラスまたはスタートアップ クラスに @EnableAspectJAutoProxy(proxyTargetClass = true) の注釈が付けられている場合、プロジェクトが AOP に対して有効であることを示します。

6bb2bd46d69ec1b1a2fd944ac1777b91.png

スタートアップ クラスのアノテーションを構成します。

@EnableAspectJAutoProxy

@EnableAspectJAutoProxy と入力すると、AspectJAutoProxyRegistrar.class が @import を通じてインポートされていることを確認します。

14216ca2e0f96590ddb69d66f7bde529.png

コードの場所: org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions

クラス図:bc4eac3aef8a3606539faba83358f5c8.png

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

b85d3086524b4b9bca19a269e53f290e.png

つまり、Spring が開始すると、@EnableAspectJAutoProxy アノテーションを使用して aop を開始するかどうかを識別します。デフォルトで追加されている場合、aop が開始されます。クラス AspectJAutoProxyRegistrar は、初期化のためにこのアノテーションの @import を通じてインポートされます、クラスには registerBeanDefinitions メソッドが実行されます。

次に、 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean を入力します。

exposedObject = this.initializeBean(beanName, exposedObject, mbd);

注: 実際、ここの最上位レイヤーは AnnotationAwareAspectJAutoProxyCreator ですが、最下位レイヤーは AbstractAutowireCapableBeanFactory です。以下のクラス図を見てください。

9e9352e4a7776620d7321e97ebdd9e89.png

クラスの場所: org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#initBeanFactory

以下は Bean を初期化するファクトリ クラスです。実際、ここではまだ更新中です。

6a6aa7f745722b26e011d13fca0cacdc.png

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

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

b3845d9c1e387fe415a3aed38a831d28.png

次に、「 shouldSkip 」と入力します。このメソッドは主に、特定の Bean をスキップするかどうかを決定するために使用されます。

番号の位置:org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#ShouldSkip

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 を通じて、アスペクトの vdisor の構築に使用される findAdvisorBeans メソッドを見つけます。

コードの場所: org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans

//获取所有通知者
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

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

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

//获取一个切面类(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}

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 の特定の実装を作成します。

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 の作成になります。

564b845659ead900113a275d01bc6ecc.png

初期化だけでもこんなに大掛かりなコード(一部省略~)、ホントに~

動的プロキシ

e29c6ceb366f425e768d619082cbb7e3.png

Spring のデフォルトのプロキシ メソッドは JDK ダイナミック プロキシですが、ターゲット オブジェクトがインターフェイスを実装していない場合は、自動的に CGLIB ダイナミック プロキシに切り替わります。これにより、ほとんどの場合、プロキシ方式を手動で構成しなくても AOP 機能を使用できるようになります。

3492c461555ae83ee5b39a4344e1d4f1.png

前回の初期化の後、メソッドが呼び出されるとき、Spring にはデフォルトでインターセプト チェーンがあり、エージェントとアスペクトを接続し、リフレクションの呼び出しを通じてエージェントを照合します。

コードの場所: org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

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

上記のメソッドは常に再帰的に実行されます。ec39a62d91120810516859d1dc800c98.png

次に、「メソッドの呼び出し」と入力します。

コードの場所: org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke

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

4cc800cccc03edba5f32c0cc9f13db04.png

これが最終的に実行されるインターセプターであり、再帰呼び出しを行うために責任連鎖設計パターンが使用されていることがわかります。順序は、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/FatalFflower/p/15572344.html

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

おすすめ

転載: blog.csdn.net/qq_16498553/article/details/132267836