AOPのspringboot

  • AOPでのAOPのシェア、springboot

    • springboot導入AOP

      <!--aop-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
      复制代码

    上の

    spring:
      aop:
        auto: true #启动aop配置
    复制代码

    そして、@EnableAspectJAutoProxy

    2つの構成がAOPを始めているが、実際にはAOPでspringbootは、デフォルトで有効になってこれらの二つの構成を記述する必要はありませんので、されています

どのようにあなたは、動的プロキシを使用し、それにspringbootにJDKへたいそうだとすれば、春と使用ダイナミック代理店JDKでspringbootのデフォルトのプロキシCGLIBを使用しますか?この場合には、それ自体がJDKを有効ダイナミック注釈プロキシとして、クラス@EnableAspectJAutoProxy注釈プロキシはspringbootのパターンを変更できないように、構成ファイル内spring.aop.proxyターゲットクラス= FALSEを構成することができるがしかし、彼らは動作しませんでした。

  • 用語集

    • お知らせ(助言)(強化と呼ばれるいくつかの場所で):あなたは、このような事項に書き込み、ビジネスロジックに必要があるとの通知と呼ばれている実行する必要があり、ログは、最初に定義され、その後、一緒に行くための場所が必要です

    • 接続点(点に参加):スプリングはローカル通知を使用することが許可され、各メソッドは例外実質的に長手方向の接続点であってもよいです

    • カットオフ点(Poincut):実際には接続ポイントをスクリーニングし、クラスの全てのメソッドは、接続点で、すべてではないニーズが接点として接続点としていくつかを除外します。通知部は、アクションまたは実行タイミングを定義した場合、カットオフポイントは、実行の場所を定義します

    • セクション(態様):実際には、いつどこで実行すると共に、それは、行っている部分の全内容を規定する接線の点と通知の組み合わせと接線の点を通知します

    • 紹介(紹介):既存のクラスのコードを変更せずに、彼らは新しい動作や状態を持っているということを前提に、従来の缶タイプを変更することなく、クラスのプロパティとメソッドを追加します。実際には、セクション:行くために(、属性が定義された新しい通知方法である)ターゲットクラスを使用します

    • ターゲット(目標):オブジェクトが通知されます。つまり、オブジェクトが実際のビジネス・ロジックは、組織切片に織り込まれている余分なコードを追加する必要があります。

    • 製織(ウィービング):手順プログラムコードセクションが追加されます。ターゲット・オブジェクトへの接続点の指定セクションに織られ、複数の点は、対象物のライフサイクルに織ることができます。

      コンパイル:セクションは、ターゲットクラスのコンパイル時に織られ、このアプローチは、特別なコンパイラが必要です

      クラスのロード:ターゲットセクションのJVMにクラスローダが、このアプローチは、ターゲットクラスバイトコードエンハンスメントがターゲットクラスに導入される前に、適用することができる特別なクラスローダを、必要とする場合に織り込まれ

      ランタイム:セクションは、製織部が、AOPコンテナを動的対象オブジェクトとしてプロキシオブジェクトを作成し、通常の状況下で、特定の時間に実行されているアプリケーション、織り込まれている、春AOPは、このようにファセットに織り込まれています。

  • セクションを作成します。

    クラスラベルアスペクトクラスでspringbootで@Aspectが、唯一@Aspectは直接使用しない使用し、また@Componentコメントを注入するために、このクラスを使用する必要があります

  • カットポイント

    • 書き込み

      1. ノートは、各通知に書き込むことができます

        @Before(value = "@annotation(TestAnnotation)")
        复制代码
      2. あなたは接線点法に@Pointcutマーカー位置を使用してメソッドを作成することができます

        @Pointcut("@annotation(TestAnnotation)")
        public void pointCut(){
        }
        复制代码
    • エントリー・ポイント・インジケーター

      1. exeution

        execution(modifier-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
        复制代码
        • 修飾子パターン:この方法は、改質支持ワイルドカードを指定し、一部が省略されてもよいです

        • RET型パターン:指定戻り値の型、サポートワイルドカードは、すべての値の型を返すために、「*」はワイルドカードを使用することができます

        • 宣言型パターン:指定された方法は、一部が省略されてもよい、ワイルドカードをサポートするために、クラスに属します

        • 名前パターン:メソッド名に一致する、サポートワイルドカードは、すべてのメソッド名に「*」ワイルドカードを使用することができます

        • PARAMパターン:形状パラメータリスト方法、二つの支持ワイルドカードを指定し、「」と「.. 『ここで』『..』は、任意のタイプのゼロ以上のパラメータを表している」とは、任意のパラメータの型を表します。

        • スローインパターン:指定メソッド宣言スローされた例外は、サポートワイルドカードは、一部を省略することもできます

          例:

          実行(パブリック* *(..))すべてのパブリックメソッドと一致する//

          実行(*セット*(..))//セットマッチング方法で始まります

          任意のAdviceManagerの実行(* com.abc.service.AdviceManager。*(..))//マッチング方法

          実行(* com.abc.service。 (..))//パケットは、任意のクラスのcom.abc.servcieの方法のいずれかと一致します

      2. 以内

        クラスのすべてのメソッドにのみ一致させることができます

        // com.zejian.daoマッチングパケットとそのサブパッケージのすべてのクラスのすべてのメソッド(com.zejian.dao .. *)内

        //メソッドの実装に一致するすべてのサブクラスDaoUserインターフェース

        (com.zejian.dao.DaoUser +)内

        また、コメントの中に、その使用中に存在している@within

      3. 注釈方法

        @Pointcut( "@注釈(TestAnnotation)")

      4. 引数和@args

        構成パラメータ引数はすべてのメソッドであり、マッチング@argsは、このクラスの注釈付きパラメータのすべてのメソッドであります

      5. 他の

        このターゲットクラスに前述このインジケータ、豆、及び他の目標点に加えて、ターゲットは、単にプロキシクラス、マメビーンはによって一致する意味します

  • お知らせ

    • 通知の種類とそのパラメータ

      1. アドバイス周り:ProceedingJoinPoint

      2. 前のアドバイス:ジョインポイント

      3. ジョインポイント:アドバイスを返送した後、

      4. ニュースへ戻る:ジョインポイント、帰国

        戻り:対応するパラメータ・タイプ値と復帰通知通知方法のセット、またはしない実行を実行する唯一の方法復帰後目標を定義する、目標値を返すために、オブジェクトの任意のタイプと一致するように対応するパラメータ戻すための通知方法

      5. 例外通知:ジョインポイント、投げます

        スロー:唯一の例外通知方法及び対応するパラメータ例外のタイプを実行するために投げ、またはパラメータタイプのThrowableに対応する通知方法は、任意の例外が一致する投げるために、行われていないゴールポスト異常通知を定義します。

    • 実行順序の通知

      ①不自定义情况下,无异常的aop执行流程:环绕前置==》前置==》程序执行==》环绕后置==》后置==》返回
      ②不自定义情况下,有异常的aop执行流程:环绕前置==》前置==》程序执行==》环绕后置==》后置==》异常返回
      复制代码
    • ジョインポイント和ProceedingJoinPoint

    • public interface JoinPoint {  
         String toString();         //连接点所在位置的相关信息  
         
         String toShortString();     //连接点所在位置的简短相关信息 
         
         String toLongString();     //连接点所在位置的全部相关信息  
         
         Object getThis();         //返回AOP代理对象,也就是com.sun.proxy.$Proxy18
         
         Object getTarget();       //返回目标对象或者是接口(也就是定义方法的接口或类,为什么会是接口呢?这主要是在目标对象本身是动态代理的情况下,例如Mapper。所以返回的是定义方法的对象如aoptest.daoimpl.GoodDaoImpl或com.b.base.BaseMapper<T, E, PK>)
         
         Object[] getArgs();       //返回被通知方法参数列表  
         
         Signature getSignature();  //返回当前连接点签名  其getName()方法返回方法的FQN,如void aoptest.dao.GoodDao.delete()或com.b.base.BaseMapper.insert(T)(需要注意的是,很多时候我们定义了子类继承父类的时候,我们希望拿到基于子类的FQN,这直接可拿不到,要依赖于AopUtils.getTargetClass(point.getTarget())
         
         SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置  
         
         String getKind();        //连接点类型  
         
         StaticPart getStaticPart(); //返回连接点静态部分  
        }  
      复制代码

      ProceedingJoinPointが進行追加に加えて、ジョインポイントを継承し、(オブジェクト[]引数)を進行方法二

    • 通知サラウンド、復帰通知、異常通知、通知ポストと代替的に事前通知

      @Around(value ="pointCut()")
      public Object aroundCut(ProceedingJoinPoint proceedingJoinPoint) {
          logger.info("前置通知");
          Object proceed = null;
          try {
              proceed = proceedingJoinPoint.proceed();
              System.out.print(proceed);
              logger.info("后置通知");
          } catch (Throwable throwable) {
              throwable.printStackTrace();
              logger.info("异常通知");
          }finally {
              logger.info("返回通知");
          }
          return proceed;
      }
      复制代码
    • 事前通知とリターンパラメータと戻り値の変更通知

         //前置通知,在改变参数的时候,不能改变基本类型的参数,如果想要改变基本类型的参数,需要创     建一个封装类
         @Before("pointCut()")
          public void beforeCut(JoinPoint joinPoint){
              Object[] args = joinPoint.getArgs();
              for (Object o: args){
                  System.out.println(o);
                  if (o instanceof Person){
                      Person person = (Person) o;
                      person.setName("zhangsan");
                      System.out.println(person);
                  }
                  logger.info(o.toString());
              }
          }
          //后置通知
          @AfterReturning(value = "pointCut()",returning = "keys")
          public void returningCut(JoinPoint joinPoint,Object keys){
                if (keys instanceof RetKit){
                    RetKit retKit = (RetKit) keys;
                    retKit.data(222);
                }
      
          }
      复制代码
    • 周囲の通知にパラメータと戻り値を変更します

         @Around(value ="pointCut()")
          public Object aroundCut(ProceedingJoinPoint proceedingJoinPoint)  {
              logger.info("前置通知");
              Object[] args = proceedingJoinPoint.getArgs();
              int i =0;
              for (Object arg:args){
                  if (arg instanceof Integer){
                      args[i]=2;
                  }
                  i++;
              }
              Object proceed = null;
              try {
                  proceed = proceedingJoinPoint.proceed(args);
                  logger.info("后置通知");
              } catch (Throwable throwable) {
                  throwable.printStackTrace();
                  logger.info("异常通知");
              }finally {
                  RetKit retKit = (RetKit) proceed;
                  retKit.setData("修改结果");
                  logger.info(proceed.toString());
                  logger.info("返回通知");
              }
              return proceed;
          }
      复制代码
    • あなたは、通知に要求と応答を使用することができます

      @Before("pointCut()")
      public void beforeCut(JoinPoint joinPoint){
          ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
          HttpServletRequest request = attributes.getRequest();
          HttpServletResponse response = attributes.getResponse();
          //url
          logger.info("url={}",request.getRequestURI());
          //method
          logger.info("method={}", request.getMethod());
          //ip
          logger.info("ip={}", request.getRemoteAddr());
          //类方法
          logger.info("classMethod={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
          //参数
          Enumeration<String> paramter = request.getParameterNames();
          while (paramter.hasMoreElements()) {
              String str = (String) paramter.nextElement();
              logger.info(str + "={}", request.getParameter(str));
          }
           //重定向或者转发
          try {
              response.sendRedirect("/person/error");
            request.getRequestDispatcher("/person/error").forward(request,response);
          } catch (IOException e) {
              e.printStackTrace();
          }catch (ServletException e) {
              e.printStackTrace();
          }
      }
      复制代码
    • 例外通知と拡張

      AOP AfterThrowingプロセスはキャプチャ異なるキャッチと直接ターゲットメソッドの異常状態が、この取引を扱うことができますが、キャッチキャッチ手段は完全に例外を処理があれば、何のcatchブロックは、新しい例外を再スローしない、方法の月正常終了; AfterThrowing処理は、例外を処理し、それは完全に例外を処理することができないがと、例外が依然としてJVMことは、前の呼び出し元に広がるであろう。

      • 異常な取り扱い
        1. 例外を処理するJavaの道

          1. try-catch
          2. スロー
        2. 方法で、春の例外処理

          1. 使用@ExceptionHandler
          2. 使用@ ControllerAdvice + @ exceptionHandlerの
          3. HandlerExceptionResolverインタフェースを実現
            @Slf4j
            @Component
            public class GlobalExpetion implements HandlerExceptionResolver {
                @Override
                public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
               log.info("系统发生异常");
                // 统一处理异常
                ModelAndView modelAndView = new ModelAndView();
                modelAndView.addObject("message", "系统发生异常,请稍后重试");
                modelAndView.setViewName("/500");
                return modelAndView;
            }
            }
          复制代码
  • ネスティングの範囲、優先順位及び原則のセクション

    • セクションの範囲

      公共、保護された、デフォルトのスコープの方法を傍受することができます

    • 優先順位

      あなたは@order割り当ての優先順位のファセットを使用することができます。値は、INT @order 31 -12トランザクション部優先の、すなわち最大電力初期値であるデフォルトの優先順位が最も低いです

    • ネストされた原則

      。この方法は、傍受の呼び出しに条件を満たすために他の方法でブロックルールを呼び出し、このクラスを満たしていない、この時間は、CallメソッドAOPインターセプト方法bを開始しませんブロックルールを満たしていない、条件を傍受するために、他のクラスのメソッドを呼び出して、この時間を満たしていますこの方法は、AOPインターセプト方法Cを開始する。満たすリコールを満たすコールを傍受する方法のシーケンスが傍受AOPクラスの外側を開始する(クラス及び外部にクラス内)Bを傍受し、傍受法が入力します通知後の方法は、次にBに移行した後、それは、このクラス内で呼び出された場合、通知方法を設定し、後処理に通知B完了通知方法の後に、その後終了するだけAOP回

      • 原則的要因ネストされた形

        メソッド呼び出しは、このフォームを呼び出すために)(this.methodを使用する場合AOP傍受対象のクラスが、プロキシクラス注入コンテナIOCが、Javaではない実際には、その後、このポイントは、プロキシクラスではないのでしかし、クラス自体。AOPこの時期ではないインターセプト方法はそう。

      • ソリューション

        1. 自分自身の中に注入

          @Component
          public class TestAopService {
              @Resource
              private TestAopService testAopService;
              public TestAopService() {
              }
              @TestAnnotation
              public void say1(){
                  testAopService.say2();
                  System.out.println("1");
              }
              @TestAnnotation
              public void say2(){
                  System.out.println("1");
              }
          }
          复制代码
        2. 使用AopContext.currentProxy()

          @Component
          public class TestAopService {
              public TestAopService() {
              }
          
              @TestAnnotation
              public void say1(){
                  ((TestAopService)AopContext.currentProxy()).say2();
                  System.out.println("1");
              }
              @TestAnnotation
              public void say2(){
                  System.out.println("1");
              }
          }
          复制代码

          しかし、音符にAopContext豆スキャン機能はデフォルトでオフになって、彼らができる前に、手動でtrueに設定されなければならないので@EnableAspectJAutoProxy(exposeProxy = true)を使用して注釈を付けなければならないAopContext.currentProxyは()であります

        3. ApplicationContextの豆を使用した検索

          @Component
          public class TestAopService {
              @Autowired
              private ApplicationContext applicationContext;
              public TestAopService() {
              }
          
              @TestAnnotation
              public void say1(){
                  TestAopService bean = applicationContext.getBean(TestAopService.class);
                  bean.say2();
                  System.out.println("1");
              }
              @TestAnnotation
              public void say2(){
                  System.out.println("1");
              }
          }
          复制代码

おすすめ

転載: juejin.im/post/5dd73de6e51d45236665e696