Spring AOPの概要


1.AOPの概要
1.1 AOPとは?
AOP(Aspect Orient Programming)は設計思想であり、ソフトウェア設計分野におけるフェース向けプログラミングであり、オブジェクト向けプログラミング(OOP)の補完と完備である.プリコンパイル方式とランタイム動的エージェント方式により、ソースコードを変更せずにプログラムに動的に統一的に追加機能を追加する技術を実現します.図-1に示すように、
AOPとOOPは文字通り意味が近いが、実は両者はまったく異なる分野向けの設計思想である.実際のプロジェクトでは、通常、オブジェクト向けを静的プロセス(システムに何個のモジュールがあるか、モジュールにどのオブジェクトがあるか、オブジェクトにどの属性があるかなど)と理解し、断面向けのランタイムエージェント方式を理解し、オブジェクトの実行時に拡張機能や制御オブジェクトの実行を動的に織り込むことができる動的プロセスと理解します.
AOP応用シーン分析?
AOPはOCP(開閉原則)に基づいて、既存のシステムのコアビジネスコードを変更することなく、いくつかの拡張機能を動的に追加し、オブジェクトの実行を「制御」することができる.たとえば、AOPは、プロジェクトのログ処理、トランザクション処理、パーミッション処理、キャッシュ処理などに適用されます.**
Spring AOP応用原理分析
Spring AOP下位層はエージェントメカニズム(ダイナミック方式)に基づいて機能拡張を実現する:
  • ターゲットオブジェクト(被エージェントオブジェクト)がインタフェースを実装する場合、下位層はJDK動的エージェントメカニズムを使用してターゲットオブジェクトのエージェントオブジェクトを作成することができる(ターゲットクラスとエージェントクラスは共通インタフェースを実装する).
  • ターゲットオブジェクト(被エージェントオブジェクト)にインタフェースが実装されていない場合、最下位レベルでは、CGLIBエージェントメカニズムを使用してターゲットオブジェクトのエージェントオブジェクトを作成できます(デフォルトで作成されたエージェントクラスはターゲットオブジェクトタイプを継承します).

  • 説明:Spring boot 2.xでAOPがデフォルトで使用されているCGLIBエージェントです.JDKダイナミックエージェントを使用する必要がある場合は、プロファイル(applicatiion.properties)で次のように構成できます.spring.aop.proxy-target-class=false1.2 SpringにおけるAOP関連用語の分析
  • 接面(aspect):横断面オブジェクト.一般的には特定のクラスオブジェクトです(@Aspectで宣言できます).
  • 通知(Advice):around,before,afterなど、断面の特定の接続点で実行される動作(拡張機能).
  • 接続ポイント(joinpoint):プログラム実行中に特定のポイントがあり、一般的にブロックされたターゲットメソッドを指します.
  • 接点(pointcut):複数の接続点(Joinpoint)の定義であり、一般に複数の接続点の集合として理解される.

  • 接続点と切り込み点の定義を図に示します.
    2 Spring AOP快速実践
    2.1業務説明
    プロジェクト内のコアビジネスに基づいて、簡単なログ操作を追加し、SLF 4 JログAPIを利用してターゲットメソッドの実行時間を出力します.(前提として、ターゲットメソッドコードを変更することはできません-OCPの原則に従います)
    2.2プロジェクトの作成と構成
    mavenプロジェクトを作成するか、既存のプロジェクトにAOP起動依存を追加します.
    
     org.springframework.boot
     spring-boot-starter-aop
    

    説明:この依存springに基づいて、AspectJフレームワークを統合してAOPの基本的な実装を迅速に完了することができます.AspectJは接面向けのフレームワークで、AOPのいくつかの構文を定義し、java仕様を遵守するclassデバイスを生成するために専門のバイトコードジェネレータがあります.
    2.3業務分析と実現の拡張
    1)ログカットクラスオブジェクトの作成
    /**
     * @Aspect        spring aop        ,       :
     * 1)   (PointCut)  (     ):           
     * 2)  (Advice)  (     ):            (                 )
     */
    //@Order(1)
    @Slf4j
    @Aspect
    @Component
    public class SysLogAspect {
         /**
          * @Pointcut              ,               (       )
          * 1)bean(bean  )       ,          spring        bean   
          * 2)bean                ,             bean      
                 *                  (    )
                 *        ,sysUserServiceImpl       bean             
          */
        @Pontcut("bean(sysUserServiceImpl)")
         public void doLogPointCut() {}//         ,           
        
    
         /**
          *     @Around               ,             ,   
          *                     。                    
          *       。
          * @param jp      ,                
          * @return        
          * @throws               。
          */
         @Around("doLogPointCut()")
         public Object doLogAround(ProceedingJoinPoint jp)throws Throwable {
             long t1=System.currentTimeMillis();
             try {
             Object result=jp.proceed();//     @Before   @Before,          ,          
             long t2=System.currentTimeMillis();
             log.info("        :{}",t2-t1);
             //             
             saveUserLog(jp,(t2-t1));
             return result;//         
             }catch(Throwable e) {
             log.error("              ,   {}",e.getMessage());
             throw e;
             }
         }

    説明:ProceedingJoinPointクラスは、実行するターゲットメソッドに関するいくつかの情報をカプセル化するための接続ポイントタイプです.@Around注記でのみ使用できるメソッドパラメータ
    2)アプリケーション総括分析は、ターゲットオブジェクト(被エージェントオブジェクト)に対して、JDKエージェントを指す可能性がある、CGLIBエージェントを指す可能性がある、具体的にどのようなタイプのエージェントオブジェクトなのか、アプリケーション.ymlプロファイルの構成を見る.
    2.4拡張業務織り込み強化分析
    1)JDKエージェント方式による実現
    ターゲットオブジェクトに実装インタフェースがある場合は、JDKに基づいてターゲットオブジェクトのプロキシオブジェクトを作成し、ターゲットオブジェクトの機能拡張を行うことができます.
    2)CGLIBエージェント方式による実現
    ターゲットオブジェクトがインタフェースを実装していない場合(もちろんインタフェースを実装してもよい)、CGLIBエージェント方式に基づいてターゲットオブジェクトの織り込み機能を拡張することができる
    3 Spring AOPプログラミングの強化
    3.1切面通知アプリケーションの強化
    1)通知タイプ
    Spring AOPベースのプログラミングでは、AspectJフレームワーク規格に基づいて、springでは、5つのタイプの通知(拡張ビジネスを記述する通知)が定義されています.
  • @Before.
  • @AfterReturning.
  • @AfterThrowing.
  • @After.
  • @Around.重点的に把握(優先度が最も高い)説明:切面類でどのような通知を使用するかは、業務によって決定され、切面ですべての通知を書くというわけではありません.

  • 2)通知実行手順
    これらの通知がすべて1つの接面オブジェクトに書き込まれた場合、その実行順序とプロセス
    3)通知実践過程分析
    @Aspect
    @Component
    public class SysTimeAspect {
    
        @Pointcut("bean(sysUserServiceImpl)")
        public void doTime() {}
        
        /**before             */
        @Before("doTime()")
        public void doBefore(JoinPoint jp) {
            System.out.println("@Before");
        }
        /**after           (return throw  )  */
        @After("doTime()")
        public void doAfter() {
            System.out.println("@After");
        }
        /**after                */
        @AfterReturning("doTime()")
        public void doAfterTurning() {
            System.out.println("@AfterReturning");
        }
        /**after              */
        @AfterThrowing("doTime()")
        public void AfterThrowing() {
            System.out.println("@AfterThrowing");
        }
        //ProceedingJoinPoint                 
        @Around("doTime()")
        public Object doAround(ProceedingJoinPoint jp) throws Throwable{
            System.out.println("@Around.Before");
            try {
            Object result=jp.proceed();
            System.out.println("@Around.after");
            return result;
            }catch(Throwable e) {
            System.out.println("@Around.error");
            throw e;
            }
        }
    }

    @AfterThrowing通知は、例外が発生した場合にのみ実行されるため、いくつかの例外モニタリングとしてこの方法でコード実装できます.
    @Slf4j
    @Aspect
    @Component
    public class SysExceptionAspect {
        /**               */
        @AfterThrowing(pointcut = "bean(*ServiceImpl)",throwing = "ex")
        public void handleException(JoinPoint jp,Throwable ex) {
            //             
            Class> targetClass=jp.getTarget().getClass();
            String className=targetClass.getName();
            //             
            MethodSignature s=(MethodSignature)jp.getSignature();
            String methodName=s.getName();//       
            String targetClassMethod=className+"."+methodName;
            log.error("{}'exception msg is  {}",targetClassMethod,ex.getMessage());
            
            //  ?
            //1)         
            //2)                   (email),  QQ  
            //3)              (    )
            //4)  (         )
        }
    }

    3.2接点式の強化
    Springでは、AOPで使用される切り込み点式の定義と説明を使用して、切り込み点式によって特定の切り込み点を定義します.
    ひょうしき
    さぎょう
    bean
    指定したbeanオブジェクトに一致するすべての方法
    within
    指定したパッケージの下のすべてのクラス内のすべてのメソッドを一致させるには
    execution
    指定した構文規則に従って特定のメソッドに一致させるには
    @annotation
    指定した注記の修飾に一致する方法
    1)bean式(重点)bean式は一般的にクラスレベルに適用され、粗粒度の切り込み点定義を実現し、ケーススタディ:
  • bean(「userServiceImpl」)は、userServiceImplクラス内のすべてのメソッドを指定します.
  • bean(*ServiceImpl)は、ServiceImplとして接尾辞が付けられたすべてのクラス内のすべてのメソッドを指定します.

  • 説明:bean式の内部のオブジェクトはspringコンテナによって管理されるbeanオブジェクトで、式の内部の名前はspringコンテナのbeanのnameであるべきです.
    2)within式(了解)
    within式はクラスレベルに適用され、粗粒度の切り込み点式定義を実現し、ケーススタディ:
  • within(「aop.service.UserServiceImpl」)は、現在のパッケージ内のクラス内のすべてのメソッドを指定します.
  • within(「aop.service.*」)は、現在のディレクトリの下にあるすべてのクラスのすべてのメソッドを指定します.
  • within(「aop.service...*」)は、現在のディレクトリおよびサブディレクトリ内のクラスのすべてのメソッドを指定します.

  • within式適用シーン解析:1)すべてのビジネスbeanに対して機能強化を行うが,bean名にはルールがない.2)beanオブジェクトをビジネスモジュール(異なるパッケージのビジネス)別にビジネス機能を強化する.
    3)execution式(了解)
    execution式はメソッドレベルに適用され、細粒度の切り込み点式定義を実現し、ケーススタディ:構文:execution(戻り値タイプパッケージ名.クラス名.メソッド名(パラメータリスト))
  • execution(void aop.service.UserServiceImpl.addUser()はaddUserメソッドに一致します.
  • execution(void aop.service.PersonServiceImpl.addUser(String))メソッドパラメータは、StringのaddUserメソッドである必要があります.
  • execution(aop.service...*(.))万能構成.4)@annotation式(アクセント)
  • @annotaion式をメソッドレベルに適用し、細粒度の切り込み点式定義、ケーススタディを実現
  • @annotation(anno.RequiredLog)は、この注釈記述の方法と一致する.
  • @annotation(anno.RequiredCache)は、この注釈記述の方法と一致する.

  • ここで、RequiredLogは独自に定義された注釈です.@RequiredLog注釈を使用してビジネス層メソッドを修飾すると、システムの最下位レベルでは、このメソッドを実行するときに日拡張操作が行われます.
    練習:Cache関連断面を定義し、注記式を使用して切り込み点を定義し、cacheを使用する必要があるビジネス・メソッドについて説明します.
    ステップ1:注釈RequiredCacheの定義
    /**
     *      ,      ,         Annotation  
     */
     @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface RequiredCache {
    
    }
    
    //           
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface ClearCache {
    
    }

    ステップ2:SysCacheAspect接面オブジェクトを定義します.
    @Aspect
    @Component
    public class SysCacheAspect {
         //                 
         private Map cache=new ConcurrentHashMap<>();//     hashmap
        
         //              (       )
         @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredCache)")
         public void doCache() {}
         @Pointcut("@annotation(com.cy.pj.common.annotation.ClearCache)")
         public void doClear(){}
         
         //????          cache  
         @AfterReturning("doClear()")
         public void doClearCache() {
             cache.clear();
             //cache.remove("deptKey");
         }
         @Around("doCache()")
         public Object doAround(ProceedingJoinPoint jp)throws Throwable{
             System.out.println("Get Data from cache");
             Object result=cache.get("deptKey");//   deptKey        
             if(result!=null)return result;
             result=jp.proceed();
             System.out.println("Put data to cache");
             cache.put("deptKey", result);
             return result;
         }

    ステップ3:@RequiredCache注記を使用して、特定のビジネス・ターゲット・オブジェクトのクエリー・メソッドについて説明します.@ClearCacheを使用して、削除変更サービスの説明をキャッシュします.
    @Service
    public class SysDeptServiceImpl implements SysDeptService {
        @Autowired
        private SysDeptDao sysDeptDao;
        /**@RequiredCache                  ,             cache  */
        @RequiredCache
        @Override
        public List> findObjects() {
            List> list=
            sysDeptDao.findObjects();
            if(list==null||list.size()==0)
            throw new ServiceException("      ");
            return list;
        }
        @ClearCache
        @Override
        public int updateObject(SysDept entity) {
        。。。。。
        }
        
        @ClearCache
        @Override
        public int deleteObject(Integer id) {
        。。。。。
        }
        @ClearCache
        @Override
        public int saveObject(SysDept entity) {
            。。。。
        }
    

    3.3接面優先度設定の実装
    切断面の優先度は@Order注記で説明する必要があります.数値が小さいほど優先度が高くなり、デフォルトの優先度は低くなります.例:
    ログスライスを定義し、優先度を指定します.
    @Order(1)
    @Aspect
    @Component
    public class SysLogAspect {
     …
    }
    

    キャッシュ断面を定義し、優先度を指定します.
    @Order(2)
    @Aspect
    @Component
    public class SysCacheAspect {
     …
    }

    説明:複数のフェースが同じターゲットオブジェクトメソッドに作用する場合、これらのフェースは、フィルタチェーン、ブロッカーチェーンのようなフェースチェーンとして構築され、図のように分析されます.
    3.4キーオブジェクトと用語のまとめ