SpringBootに基づいてAOP技術を使って操作ログ管理を実現する。


操作ログは、プログラマーまたは管理者にとって、システムに関連する操作を迅速に位置決めできます。操作ログの管理の実現は正常業務の実現に影響を与えません。単一の原則を満たしていないと、後続のコードの維持が困難になるので、AOPカット技術を使ってログ管理の実現を実現することを考えています。
文章の大まかな内容:
1、基本概念
2、基本的な応用
3、ログ管理の実戦
これらの部分を理解したら、AOPの応用はとても楽です。
一、基本概念
アイテム
説明
Asppect(うどん)
複数のカテゴリーの注目点を越えたモジュール化は、通知と接点の結合である。通知と接点は、うどんの全部の内容を定義しています。何ですか?いつと何処でその機能を完成しますか?事務処理とログ処理は、うどんとして理解できる。
ジョインポイント(接続点)
プログラム実行中の一つの点は、方法の実行や異常な処理などです。
Advice(お知らせ)
切断面が特定の接続点で取る動作
Pointcut(接点)
接続点に合致するとの断言。この通知は、切り込み点式に関連して、切り込み点に一致する任意の接続点で実行されます。点式マッチングの接続点概念はAOPの核心であり、SpringはデフォルトでAsppectJを使用して点式言語に切り込みます。
Introduction(引用)
タイプのために他の方法またはフィールドを宣言します。Spring AOPでは、提案されたオブジェクトに新しいインターフェース(および対応する実装)を導入することができます。例えば、紹介を使ってIsModifiedインターフェースを実現してキャッシュを簡単にすることができます。
ターゲット
1つ以上の切断面で通知されるオブジェクト。「通知対象」ともいう。Spring AOPは、運転時の代理を使用して実現されるため、このオブジェクトは常に代理対象となります。
AOP proxy(代理)
AOPフレームは、接面契約(通知方法の実行など)を実現するために作成されたオブジェクトです。Springフレームワークにおいて、AOPエージェントはJDKダイナミックエージェントまたはCGLOIBエージェントである。
Weaving(織り込み)
編入は、ターゲットクラスに対して具体的な接続点を追加することを通知するプロセスであり、コンパイル時(例えば、AsppectJコンパイラを使用する)、ロード時または実行時に完了することができます。
Springカットは5種類の通知を適用できます。
  • 事前通知(Before):ターゲット方法が起動される前に通知
  • を呼び出す。
  • バックエンド通知(After):目標方法が完了したら、通知を呼び出す(正常であろうと、異常であろうと)
  • 戻る通知(After-returning):ターゲット方法が成功した後に通知
  • を呼び出す。
  • 異常通知(After-throwing):ターゲットメソッドに異常が発生したら、呼び出し通知
  • サラウンド通知(Asound):通知された小包が通知された方法を通知し、通知された方法が起動される前と呼び出し後にカスタム挙動
  • を実行する。
    その実行順序は以下の通りです。
    在这里插入图片描述
    在这里插入图片描述
    後続の基本アプリケーションでは、折り返し通知、前置き通知、後置通知、戻り通知、異常通知を実施し、その実行手順を示します。
    二、基本的な応用
    通知を発する
    下のコードをコピーして、上の手順を確認してください。
    
    @Aspect
    public class Test {
     private static int step = 0;
    
     @Pointcut("@annotation(com.chenyanwu.erp.erpframework.annotation.Log)") // the pointcut expression
     private void operation() {}
    
     @Before("operation()")
     public void doBeforeTask() {
      System.out.println(++step + "     ");
     }
    
     @After("operation()")
     public void doAfterTask() {
      System.out.println(++step + "     ");
     }
    
     @AfterReturning(pointcut = "operation()", returning = "retVal")
     public void doAfterReturnningTask(Object retVal) {
      System.out.println(++step + "     ,    :" + retVal.toString());
     }
    
     @AfterThrowing(pointcut = "operation()", throwing = "ex")
     public void doAfterThrowingTask(Exception ex) {
      System.out.println(++step + "     ,     :" + ex.getMessage());
     }
    
     /**
      *         ProceedingJoinPoint      
      *                ProceedingJoinPoint                  
      *            ,            
      */
     //@Around("operation()")
     public Object doAroundTask(ProceedingJoinPoint pjp) {
      String methodname = pjp.getSignature().getName();
      Object result = null;
      try {
       //     
       System.out.println("    " + methodname + "  ,   " + Arrays.asList(pjp.getArgs()));
       //       
       result = pjp.proceed();
       //     
       System.out.println("    " + methodname + "    ,  " + result);
      } catch (Throwable e) {
       //     
       System.out.println("    " + methodname + "    : " + e.getMessage());
      }
      //     
      System.out.println("    " + methodname + "  ");
      return result;
     }
    }
    ここで注意したいのは、切り込みポイント@Pointcutの表現です。
    書式:
    execution(modifiers-pattern?ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)throws-pattern?)
    括弧の中の各patternはそれぞれ表しています。
  • 修繕子整合(modifier-pattern?)
  • 戻り値マッチング(ret-type-pattern)は、*のいずれかの戻り値、全パスのクラス名などを表しても良いです。
  • 類経路マッチング(declaring-type-pattern?)
  • メソッド名マッチングは、方法名を指定することができ、またはすべてを表すことができ、setはsetで始まるすべての方法
  • を表す。
  • パラメータマッチング(param-pattern)は、特定のパラメータタイプを指定することができ、複数のパラメータ間を「、」で区切って、各パラメータも「」で一つのStringパラメータに適合する方法を表してもよい。Stringは、2つのパラメータをマッチングする方法を表し、1番目のパラメータは任意のタイプであってもよく、2番目のパラメータはStringタイプである。ゼロまたは複数の任意のパラメータを(…)で表すことができる
  • 異常タイプマッチング(throws-pattern?)
  • のうち、後ろに「?」がついています。オプション
  • 例:
    1)execution(*(…)
    //はすべての方法にマッチすることを表します。
    2)execution(public*com.savage.service.UserService.(...)
    //comp.savage.server.UserServiceに一致するすべての公有方法を表します。
    3)execution(*comp.savage.server...(…)
    //comp.savage.serverカバンとそのサブパッケージにマッチするすべての方法を表します。
    三、ログ管理の実戦
    上の基本的な応用の理解ができました。今直接コードを貼ります。
    1、依頼のjarバッグ
    
    <!-- aop   -->
    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    2、カスタムコメント
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Log {
     String value() default "";
    }
    
    3、うどんを実現する
    
    @Aspect
    @Order(5)
    @Component
    public class LogAspect {
    
     private Logger logger = LoggerFactory.getLogger(LogAspect.class);
    
     @Autowired
     private ErpLogService logService;
    
     @Autowired
     ObjectMapper objectMapper;
    
     private ThreadLocal<Date> startTime = new ThreadLocal<Date>();
    
     @Pointcut("@annotation(com.chenyanwu.erp.erpframework.annotation.Log)")
     public void pointcut() {
    
     }
    
     /**
      *     , Controller      
      *
      * @param joinPoint    
      */
     @Before("pointcut()")
     public void doBefore(JoinPoint joinPoint) {
      //         
      startTime.set(new Date());
     }
    
     /**
      *       
      *
      * @param joinPoint    
      * @param rvt      
      */
     @AfterReturning(pointcut = "pointcut()", returning = "rvt")
     public void doAfter(JoinPoint joinPoint, Object rvt) throws Exception {
      handleLog(joinPoint, null, rvt);
     }
    
     /**
      *       
      *
      * @param joinPoint
      * @param e
      */
     @AfterThrowing(pointcut = "pointcut()", throwing = "e")
     public void doAfter(JoinPoint joinPoint, Exception e) throws Exception {
      handleLog(joinPoint, e, null);
     }
    
     @Async
     private void handleLog(final JoinPoint joinPoint, final Exception e, Object rvt) throws Exception{
      //     
      Method method = getMethod(joinPoint);
      Log log = getAnnotationLog(method);
      if (log == null) {
       return;
      }
      Date now = new Date();
      //         
      ErpLog erpLog = new ErpLog();
      erpLog.setErrorCode(0);
      erpLog.setIsDeleted(0);
      //     
      HttpServletRequest request = ToolUtil.getRequest();
      erpLog.setType(ToolUtil.isAjaxRequest(request) ? "Ajax  " : "    ");
      erpLog.setTitle(log.value());
      erpLog.setHost(request.getRemoteHost());
      erpLog.setUri(request.getRequestURI().toString());
    //  erpLog.setHeader(request.getHeader(HttpHeaders.USER_AGENT));
      erpLog.setHttpMethod(request.getMethod());
      erpLog.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
      //         
      Object[] args = joinPoint.getArgs();
      //          
      LocalVariableTableParameterNameDiscoverer u
        = new LocalVariableTableParameterNameDiscoverer();
      String[] paramNames = u.getParameterNames(method);
      if (args != null && paramNames != null) {
       StringBuilder params = new StringBuilder();
       params = handleParams(params, args, Arrays.asList(paramNames));
       erpLog.setParams(params.toString());
      }
      String retString = JsonUtil.bean2Json(rvt);
      erpLog.setResponseValue(retString.length() > 5000 ? JsonUtil.bean2Json("            ") : retString);
      if (e != null) {
       erpLog.setErrorCode(1);
       erpLog.setErrorMessage(e.getMessage());
      }
      Date stime = startTime.get();
      erpLog.setStartTime(stime);
      erpLog.setEndTime(now);
      erpLog.setExecuteTime(now.getTime() - stime.getTime());
      erpLog.setUsername(MySysUser.loginName());
      HashMap<String, String> browserMap = ToolUtil.getOsAndBrowserInfo(request);
      erpLog.setOperatingSystem(browserMap.get("os"));
      erpLog.setBrower(browserMap.get("browser"));
      erpLog.setId(IdUtil.simpleUUID());
      logService.insertSelective(erpLog);
     }
    
     /**
      *       ,       
      */
     private Log getAnnotationLog(Method method) {
      if (method != null) {
       return method.getAnnotation(Log.class);
      }
      return null;
     }
    
     private Method getMethod(JoinPoint joinPoint) {
      Signature signature = joinPoint.getSignature();
      MethodSignature methodSignature = (MethodSignature) signature;
      Method method = methodSignature.getMethod();
      if (method != null) {
       return method;
      }
      return null;
     }
    
     private StringBuilder handleParams(StringBuilder params, Object[] args, List paramNames) throws JsonProcessingException {
      for (int i = 0; i < args.length; i++) {
       if (args[i] instanceof Map) {
        Set set = ((Map) args[i]).keySet();
        List list = new ArrayList();
        List paramList = new ArrayList<>();
        for (Object key : set) {
         list.add(((Map) args[i]).get(key));
         paramList.add(key);
        }
        return handleParams(params, list.toArray(), paramList);
       } else {
        if (args[i] instanceof Serializable) {
         Class<?> aClass = args[i].getClass();
         try {
          aClass.getDeclaredMethod("toString", new Class[]{null});
          //      NoSuchMethodException       toString    ,   writeValueAsString ,     Object  toString  
          params.append(" ").append(paramNames.get(i)).append(": ").append(objectMapper.writeValueAsString(args[i]));
         } catch (NoSuchMethodException e) {
          params.append(" ").append(paramNames.get(i)).append(": ").append(objectMapper.writeValueAsString(args[i].toString()));
         }
        } else if (args[i] instanceof MultipartFile) {
         MultipartFile file = (MultipartFile) args[i];
         params.append(" ").append(paramNames.get(i)).append(": ").append(file.getName());
        } else {
         params.append(" ").append(paramNames.get(i)).append(": ").append(args[i]);
        }
       }
      }
      return params;
     }
    }
    4、対応コードにコメントを追加する
    
    @Log("    ")
     @RequestMapping(value = "/create", method = RequestMethod.POST)
     @ResponseBody
     public ResultBean<String> create(@RequestBody @Validated ErpStudent item) {
      if(service.insertSelective(item) == 1) {
       //   
       insertErpSFamilyMember(item);
       return new ResultBean<String>("");
      }
    
      return new ResultBean<String>(ExceptionEnum.BUSINESS_ERROR, "      !", "    !", "");
     }
    業務を操作すると、データベース、インターフェースクエリに書き込みます。
    在这里插入图片描述
    ログ管理の完全なコードは、gitから取得できます。
    https://github.com/chyanwu/erp-framework
    以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。