最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring AOP + 注解實現(xiàn)統(tǒng)一注解功能

 更新時間:2018年05月09日 14:01:13   作者:hry2015  
本文我們通過Spring AOP和Java的自定義注解來實現(xiàn)日志的插入功能,非常不錯,具有一定的參考借鑒價值,需要的朋友一起看看吧

1. 概述

在一般系統(tǒng)中,當我們做了一些重要的操作時,如登陸系統(tǒng),添加用戶,刪除用戶等操作時,我們需要將這些行為持久化。本文我們通過Spring AOP和Java的自定義注解來實現(xiàn)日志的插入。此方案對原有業(yè)務入侵較低,實現(xiàn)較靈活

2. 日志的相關(guān)類定義

我們將日志抽象為以下兩個類:功能模塊和操作類型

使用枚舉類定義功能模塊類型ModuleType,如學生、用戶模塊

public enum ModuleType {
  DEFAULT("1"), // 默認值
  STUDENT("2"),// 學生模塊
  TEACHER("3"); // 用戶模塊
  private ModuleType(String index){
    this.module = index;
  }
  private String module;
  public String getModule(){
    return this.module;
  }
}

使用枚舉類定義操作的類型:EventType。如登陸、添加、刪除、更新、刪除等

public enum EventType {
  DEFAULT("1", "default"), ADD("2", "add"), UPDATE("3", "update"), DELETE_SINGLE("4", "delete-single"),
  LOGIN("10","login"),LOGIN_OUT("11","login_out");

  private EventType(String index, String name){
    this.name = name;
    this.event = index;
  }
  private String event;
  private String name;
  public String getEvent(){
    return this.event;
  }

  public String getName() {
    return name;
  }
}

3. 定義日志相關(guān)的注解

3.1. @LogEnable

這里我們定義日志的開關(guān)量,類上只有這個值為true,這個類中日志功能才開啟

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface LogEnable {
  /**
   * 如果為true,則類下面的LogEvent啟作用,否則忽略
   * @return
   */
  boolean logEnable() default true;
}

3.2. @LogEvent

這里定義日志的詳細內(nèi)容。如果此注解注解在類上,則這個參數(shù)做為類全部方法的默認值。如果注解在方法上,則只對這個方法啟作用

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.METHOD, ElementType.TYPE})
public @interface LogEvent {
  ModuleType module() default ModuleType.DEFAULT; // 日志所屬的模塊
  EventType event() default EventType.DEFAULT; // 日志事件類型
  String desc() default ""; // 描述信息
}

3.3. @LogKey

此注解如果注解在方法上,則整個方法的參數(shù)以json的格式保存到日志中。如果此注解同時注解在方法和類上,則方法上的注解會覆蓋類上的值。

@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogKey {
   String keyName() default ""; // key的名稱
   boolean isUserId() default false; // 此字段是否是本次操作的userId,這里略
   boolean isLog() default true; // 是否加入到日志中
}

4. 定義日志處理類

4.1. LogAdmModel

定義保存日志信息的類

public class LogAdmModel {
  private Long id;
  private String userId; // 操作用戶
  private String userName;
  private String admModel; // 模塊
  private String admEvent; // 操作
  private Date createDate; // 操作內(nèi)容
  private String admOptContent; // 操作內(nèi)容
  private String desc; // 備注
  set/get略
}

4.2. ILogManager

定義日志處理的接口類ILogManager

我們可以將日志存入數(shù)據(jù)庫,也可以將日志發(fā)送到開中間件,如果redis, mq等等。每一種日志處理類都是此接口的實現(xiàn)類

public interface ILogManager {
  /**
   * 日志處理模塊
   * @param paramLogAdmBean
   */
  void dealLog(LogAdmModel paramLogAdmBean);
}

4.3. DBLogManager

ILogManager實現(xiàn)類,將日志入庫。這里只模擬入庫

@Service
public class DBLogManager implements ILogManager {
  @Override
  public void dealLog(LogAdmModel paramLogAdmBean) {
    System.out.println("將日志存入數(shù)據(jù)庫,日志內(nèi)容如下: " + JSON.toJSONString(paramLogAdmBean));
  }
}

5. AOP的配置

5.1. LogAspect定義AOP類

使用@Aspect注解此類

使用@Pointcut定義要攔截的包及類方法

我們使用@Around定義方法

@Component
@Aspect
public class LogAspect {
  @Autowired
  private LogInfoGeneration logInfoGeneration;

  @Autowired
  private ILogManager logManager;

  @Pointcut("execution(* com.hry.spring.mvc.aop.log.service..*.*(..))")
  public void managerLogPoint() {
  }

  @Around("managerLogPoint()")
  public Object aroundManagerLogPoint(ProceedingJoinPoint jp) throws Throwable {
  ….
  } 
}

aroundManagerLogPoint:主方法的主要業(yè)務流程

1. 檢查攔截方法的類是否被@LogEnable注解,如果是,則走日志邏輯,否則執(zhí)行正常的邏輯

2. 檢查攔截方法是否被@LogEvent,如果是,則走日志邏輯,否則執(zhí)行正常的邏輯

3. 根據(jù)獲取方法上獲取@LogEvent 中值,生成日志的部分參數(shù)。其中定義在類上@LogEvent 的值做為默認值

4. 調(diào)用logInfoGeneration的processingManagerLogMessage填充日志中其它的參數(shù),做個方法我們后面再講

5. 執(zhí)行正常的業(yè)務調(diào)用

6. 如果執(zhí)行成功,則logManager執(zhí)行日志的處理(我們這里只記錄執(zhí)行成功的日志,你也可以定義記錄失敗的日志)

@Around("managerLogPoint()")
    public Object aroundManagerLogPoint(ProceedingJoinPoint jp) throws Throwable {

      Class target = jp.getTarget().getClass();
      // 獲取LogEnable
      LogEnable logEnable = (LogEnable) target.getAnnotation(LogEnable.class);
      if(logEnable == null || !logEnable.logEnable()){
        return jp.proceed();
      }

      // 獲取類上的LogEvent做為默認值
      LogEvent logEventClass = (LogEvent) target.getAnnotation(LogEvent.class);
      Method method = getInvokedMethod(jp);
      if(method == null){
        return jp.proceed();
      }

      // 獲取方法上的LogEvent
      LogEvent logEventMethod = method.getAnnotation(LogEvent.class);
      if(logEventMethod == null){
        return jp.proceed();
      }

      String optEvent = logEventMethod.event().getEvent();
      String optModel = logEventMethod.module().getModule();
      String desc = logEventMethod.desc();

      if(logEventClass != null){
        // 如果方法上的值為默認值,則使用全局的值進行替換
        optEvent = optEvent.equals(EventType.DEFAULT) ? logEventClass.event().getEvent() : optEvent;
        optModel = optModel.equals(ModuleType.DEFAULT) ? logEventClass.module().getModule() : optModel;
      }

      LogAdmModel logBean = new LogAdmModel();
      logBean.setAdmModel(optModel);
      logBean.setAdmEvent(optEvent);
      logBean.setDesc(desc);
      logBean.setCreateDate(new Date());
      logInfoGeneration.processingManagerLogMessage(jp,
          logBean, method);
      Object returnObj = jp.proceed();

      if(optEvent.equals(EventType.LOGIN)){
        //TODO 如果是登錄,還需要根據(jù)返回值進行判斷是不是成功了,如果成功了,則執(zhí)行添加日志。這里判斷比較簡單
        if(returnObj != null) {
          this.logManager.dealLog(logBean);
        }
      }else {
        this.logManager.dealLog(logBean);
      }
      return returnObj;
    }

    /**
     * 獲取請求方法
     *
     * @param jp
     * @return
     */
    public Method getInvokedMethod(JoinPoint jp) {
      // 調(diào)用方法的參數(shù)
      List classList = new ArrayList();
      for (Object obj : jp.getArgs()) {
        classList.add(obj.getClass());
      }
      Class[] argsCls = (Class[]) classList.toArray(new Class[0]);

      // 被調(diào)用方法名稱
      String methodName = jp.getSignature().getName();
      Method method = null;
      try {
        method = jp.getTarget().getClass().getMethod(methodName, argsCls);
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      }
      return method;
    }
  }

6. 將以上的方案在實際中應用的方案

這里我們模擬學生操作的業(yè)務,并使用上文注解應用到上面并攔截日志

6.1. IStudentService

業(yè)務接口類,執(zhí)行一般的CRUD

public interface IStudentService {
  void deleteById(String id, String a);
  int save(StudentModel studentModel);
  void update(StudentModel studentModel);
  void queryById(String id);
}

6.2. StudentServiceImpl:

@LogEnable : 啟動日志攔截
類上@LogEvent定義所有的模塊
方法上@LogEven定義日志的其它的信息
@Service
@LogEnable // 啟動日志攔截
@LogEvent(module = ModuleType.STUDENT)
public class StudentServiceImpl implements IStudentService {
  @Override
  @LogEvent(event = EventType.DELETE_SINGLE, desc = "刪除記錄") // 添加日志標識
  public void deleteById(@LogKey(keyName = "id") String id, String a) {
    System.out.printf(this.getClass() + "deleteById id = " + id);
  }
  @Override
  @LogEvent(event = EventType.ADD, desc = "保存記錄") // 添加日志標識
  public int save(StudentModel studentModel) {
    System.out.printf(this.getClass() + "save save = " + JSON.toJSONString(studentModel));
    return 1;
  }
  @Override
  @LogEvent(event = EventType.UPDATE, desc = "更新記錄") // 添加日志標識
  public void update(StudentModel studentModel) {
    System.out.printf(this.getClass() + "save update = " + JSON.toJSONString(studentModel));
  }
  // 沒有日志標識
  @Override
  public void queryById(String id) {
    System.out.printf(this.getClass() + "queryById id = " + id);
  }
}

執(zhí)行測試類,打印如下信息,說明我們?nèi)罩咀⒔馀渲脝⒆饔昧耍?/p>

將日志存入數(shù)據(jù)庫,日志內(nèi)容如下:

{"admEvent":"4","admModel":"1","admOptContent":"{\"id\":\"1\"}","createDate":1525779738111,"desc":"刪除記錄"}

7. 代碼

以上的詳細的代碼見下面

github代碼,請盡量使用tag v0.21,不要使用master,因為我不能保證master代碼一直不變

相關(guān)文章

  • Mybatis?plus多租戶方案的實戰(zhàn)踩坑記錄

    Mybatis?plus多租戶方案的實戰(zhàn)踩坑記錄

    MybaitsPlus多租戶處理器是一個對于多租戶問題的解決方案,下面這篇文章主要給大家介紹了關(guān)于Mybatis?plus多租戶方案踩坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-12-12
  • Java對象轉(zhuǎn)json的方法過程解析

    Java對象轉(zhuǎn)json的方法過程解析

    這篇文章主要介紹了Java對象轉(zhuǎn)json的方法過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09
  • Logback MDCAdapter日志跟蹤及自定義效果源碼解讀

    Logback MDCAdapter日志跟蹤及自定義效果源碼解讀

    這篇文章主要為大家介紹了Logback MDCAdapter日志跟蹤及自定義效果源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • java如何用遞歸生成樹形結(jié)構(gòu)

    java如何用遞歸生成樹形結(jié)構(gòu)

    作者分享了自己在使用腳本之家資源進行編程時的經(jīng)驗,包括準備實體對象、測試數(shù)據(jù)、構(gòu)造樹形結(jié)構(gòu)遞歸函數(shù)、測試以及輸出結(jié)果等步驟,作者希望這些經(jīng)驗能對大家有所幫助,并鼓勵大家支持腳本之家
    2025-03-03
  • 使用JPA中@Query 注解實現(xiàn)update 操作方法(必看)

    使用JPA中@Query 注解實現(xiàn)update 操作方法(必看)

    下面小編就為大家?guī)硪黄褂肑PA中@Query 注解實現(xiàn)update 操作方法(必看)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Java編寫通用的導出任何對象列表數(shù)據(jù)到excel的工具類

    Java編寫通用的導出任何對象列表數(shù)據(jù)到excel的工具類

    在工作中經(jīng)常會遇到列表數(shù)據(jù)的導出,每次需要的時候都要去開發(fā)一次,且數(shù)據(jù)不斷在變化,所以本文將利用Java編寫一個工具類可以導出任何對象列表數(shù)據(jù)到excel,希望對大家有所幫助
    2024-12-12
  • Java線程池ThreadPoolExecutor的使用及其原理詳細解讀

    Java線程池ThreadPoolExecutor的使用及其原理詳細解讀

    這篇文章主要介紹了Java線程池ThreadPoolExecutor的使用及其原理詳細解讀,線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然后在創(chuàng)建線程后自動啟動這些任務,線程池線程都是后臺線程,需要的朋友可以參考下
    2023-12-12
  • java設計模式之橋接模式(Bridge)

    java設計模式之橋接模式(Bridge)

    這篇文章主要為大家詳細介紹了java設計模式之橋接模式Bridge,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • mybatis于xml方式和注解方式實現(xiàn)多表查詢的操作方法

    mybatis于xml方式和注解方式實現(xiàn)多表查詢的操作方法

    在數(shù)據(jù)庫中,單表的操作是最簡單的,但是在實際業(yè)務中最少也有十幾張表,并且表與表之間常常相互間聯(lián)系,本文給大家介紹mybatis于xml方式和注解方式實現(xiàn)多表查詢的操作方法,感興趣的朋友一起看看吧
    2023-12-12
  • springboot?vue前后端接口測試樹結(jié)點添加功能

    springboot?vue前后端接口測試樹結(jié)點添加功能

    這篇文章主要為大家介紹了springboot?vue前后端接口測試樹結(jié)點添加功能,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05

最新評論

江安县| 镇沅| 高尔夫| 常熟市| 璧山县| 宁国市| 佛山市| 新平| 武平县| 抚顺市| 贡嘎县| 溆浦县| 洪湖市| 海安县| 金堂县| 江陵县| 双牌县| 乌拉特后旗| 马公市| 临海市| 莫力| 古田县| 株洲市| 安西县| 桃园县| 东乡| 天全县| 南城县| 曲沃县| 云林县| 盐亭县| 百色市| 定结县| 广饶县| 岐山县| 汕头市| 安丘市| 从江县| 莱芜市| 马公市| 富锦市|