国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > Java > 正文

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

2024-07-14 08:40:44
字體:
供稿:網(wǎng)友

1. 概述

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

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

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

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

public enum ModuleType {  DEFAULT("1"), // 默認值  STUDENT("2"),// 學(xué)生模塊  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)@Documentedpublic @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)類,將日志入庫。這里只模擬入庫

@Servicepublic 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@Aspectpublic 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è)務(wù)流程

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

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

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

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

5. 執(zhí)行正常的業(yè)務(wù)調(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īng)用的方案

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

6.1. IStudentService

業(yè)務(wù)接口類,執(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)教程知識閱讀請移步到JAVA教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 台山市| 枣阳市| 武威市| 紫云| 龙里县| 克拉玛依市| 阿巴嘎旗| 高要市| 青浦区| 茶陵县| 保定市| 玛曲县| 巢湖市| 铅山县| 宁波市| 宝丰县| 格尔木市| 五华县| 咸丰县| 北流市| 鞍山市| 建昌县| 桐城市| 香格里拉县| 内黄县| 铁岭市| 梁河县| 灵宝市| 琼海市| 东乌珠穆沁旗| 海晏县| 沙洋县| 昂仁县| 偃师市| 京山县| 沙坪坝区| 贡山| 古蔺县| 怀柔区| 洛南县| 赤城县|