天天看點

關于自定義注解的使用和場景

關于注解的作用、以及自定義注解的使用和場景

  • ​​一、注解的作用​​
  • ​​二、建立自定義注解​​
  • ​​2.1 基本定義​​
  • ​​2.2 示例 -- 類級别的注解​​
  • ​​2.3 示例 -- 方法級别的注解​​
  • ​​2.4 示例 -- 字段屬性級别的注解​​
  • ​​三、注解的使用場景​​
  • ​​3.1 JDK中的常用注解​​
  • ​​3.2 Spring中的常用注解​​
  • ​​3.3 自定義注解的使用場景​​
  • ​​3.3.1 AOP + @CusLog,通過自定義注解實作 >>記錄檔 --> DB資料庫​​
  • ​​四、[Spring下所有的注解,及其用法,持續更補](javascript:void(0))​​

一、注解的作用

  • 注解是一種中繼資料形式。即注解是屬于java的一種資料類型,和類、接口、數組、枚舉類似。
  • 注解用來修飾,類、方法、變量、參數、包。
  • 注解不會對所修飾的代碼産生直接的影響。

二、建立自定義注解

2.1 基本定義

  • 首先使用 @interface聲明注解名稱
  • 然後,使用@Retention,@Target等元注解标注注解的生命周期和作用元素
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.Type)
public @interface FirstAnnotation{
}      

2.2 示例 – 類級别的注解

定義注解

@Target(ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Custom1 {
    String[] values();
}      

測試注解

@Custom1(values = {"V1","V2"})
public class MainTest {

    @Test
    public void t1(){

        MainTest t = new MainTest();

        boolean present = t.getClass().isAnnotationPresent(Custom1.class);

        if (present) {

            Custom1 ca = t.getClass().getAnnotation(Custom1.class);
            if (ca != null) {
                for (String value : ca.values()){
                    System.out.println(value);
                }
            }
        }

    }

}      

控制台列印

V1
V2      

2.3 示例 – 方法級别的注解

定義注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CanRun {

}      

使用注解,方法之上

public class AnnotationRunner {

    public void method1() {
        System.out.println("method1");
    }

    @CanRun
    public void method2() {
        System.out.println("method2");
    }

    public void method3() {
        System.out.println("method3");
    }

    @CanRun
    public void method5() {
        System.out.println("method4");
    }

}      

測試注解

public class MainTest {

    @Test
    public void t1(){

        AnnotationRunner runner = new AnnotationRunner();
        Method[] methods = runner.getClass().getMethods();

        for (Method method : methods) {
            CanRun annos = method.getAnnotation(CanRun.class);
            if (annos != null) {
                try {
                    method.invoke(runner);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}      

控制台列印

method2
method4      

2.4 示例 – 字段屬性級别的注解

定義注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CanRun {

}      

使用注解,方法之上

public class AnnotationRunner {

    public void method1() {
        System.out.println("method1");
    }

    @CanRun
    public void method2() {
        System.out.println("method2");
    }

    public void method3() {
        System.out.println("method3");
    }

    @CanRun
    public void method5() {
        System.out.println("method4");
    }

}      

測試注解

public class MainTest {

    @Test
    public void t1(){

        AnnotationRunner runner = new AnnotationRunner();
        Method[] methods = runner.getClass().getMethods();

        for (Method method : methods) {
            CanRun annos = method.getAnnotation(CanRun.class);
            if (annos != null) {
                try {
                    method.invoke(runner);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}      

控制台列印

method2
method4      

三、注解的使用場景

3.1 JDK中的常用注解

@Override: 表示注解修飾的方法必須滿足重寫的規則

@Deprecated: 表示成員過時,編譯器可以在程式運作的時候擷取到該注解

@SupressWarnings: 表示忽略編譯器的警告

@FunctionalInterface: 表示該接口是一個函數式接口,并且可以作為Lambda表達式參數傳入

還有,幾個元注解,用于定義注解的

@Retention: 表示對它所标記的元素的生命周期(參考的範圍看RetentionPolicy枚舉類)

@Target: 表示标記定義的注解可以和什麼目标元素綁定

@Inherited: 表示該注解可以被繼承

@Document: 表示該注解可以被生成API文檔

3.2 Spring中的常用注解

@RestController

@RequestMapping

@PostMapping

@RequestBody

​​​這裡是Spring中所有的注解連結​​

3.3 自定義注解的使用場景

3.3.1 AOP + @CusLog,通過自定義注解實作 >>記錄檔 --> DB資料庫

(1) - 首先是有日志的Model定義,Dao層的save實作,這裡不介紹太多了…

@Data
@Entity
@Table(name = "operate_log")
public class OperateLog {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

   private String recordId; // 操作資料id


   private String module;// 子產品名稱

   private String business;// 業務方法描述

   private String opType;// 操作類型

   private Long userId;// 操作人

   private String userName;// 操作人姓名

   private String params;// 操作資料

   /**
    * 建立時間
    */
   @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
   @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
   private Date createTime;      
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface SystemLog {
   /**
    * 操作描述 業務名稱business
    *
    * @return
    */
   String description() default "";

   /**
    * 操作子產品
    *
    * @return
    */
   OperateModule module();

   /**
    * 操作類型 create modify delete
    *
    * @return
    */
   OperateType opType();

   /**
    * 主鍵入參參數名稱,入參中的哪個參數為主鍵
    *
    * @return
    */
   String primaryKeyName() default "";

   /**
    * 主鍵在參數中的順序,從0開始,預設0
    */
   int primaryKeySort() default 0;

   /**
    * 主鍵所屬類
    *
    * @return
    */
   Class<?> primaryKeyBelongClass();

}      
@Aspect
@Component
@Slf4j
public class WebLogAspect {
   @Autowired
   private OperateLogRepository logRepository;

   @Pointcut("execution(public * demo1.controller..*.*(..)) "
           + " && @annotation(demo1.log.SystemLog)")
   public void webLog() {
   }

   @Around("webLog()")
   public Object round(ProceedingJoinPoint joinPoint) throws Throwable {
       // log.info("環繞通知開始........");
//        String username = SecurityUtils.getCurrentUserName();

       String username = "ss";
       // 入參 value
       Object[] args = joinPoint.getArgs();
       // 入參名稱
       String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
       Map<String, Object> params = new HashMap<>();
       // 擷取所有參數對象
       for (int i = 0; i < args.length; i++) {
           if (null != args[i]) {
               if (args[i] instanceof BindingResult) {
                   params.put(paramNames[i], "bindingResult");
               } else {
                   params.put(paramNames[i], args[i]);
               }
           } else {
               params.put(paramNames[i], "無");
           }
       }
       Map<String, Object> values = getControllerAnnotationValue(joinPoint);
       String opType = values.get("opType").toString();
       String module = values.get("module").toString();
       String business = values.get("business").toString();
       String primaryKeyName = values.get("primaryKeyName").toString();
       int primaryKeySort = Integer.parseInt(values.get("primaryKeySort").toString());
       Class<?> primaryKeyBelongClass = (Class<?>) values.get("primaryKeyBelongClass");

       Object primaryKeyValue = null;
       if (StringUtils.isNotBlank(primaryKeyName) && OperateType.valueOf(opType) == OperateType.delete) {
           primaryKeyValue = args[primaryKeySort];
       }
       // 切面傳回值
       Object returnValue = joinPoint.proceed();
       if (OperateType.valueOf(opType) != OperateType.delete) {
           // 主要目的是為了擷取方法儲存成功的傳回資料格式,以擷取儲存成功的資料id
           // 此處要限制新增傳回成功的格式,return ok("具體操作資訊", new MapBean("此處為實體主鍵屬性名稱",
           // primaryKeyValue));
           // 不然自己也可定義格式,進行拆分擷取主鍵
           primaryKeyName = getPrimaryKeyName(primaryKeyBelongClass).toString();
           primaryKeyValue = ReflectUtils.dynamicGet(returnValue, primaryKeyName);
           if (primaryKeyValue == null || primaryKeyValue.toString().equals("")) {// 處理service層傳回ResultBean
               Object result = ReflectUtils.dynamicGet(returnValue, "result");
               if (result != null) {
                   primaryKeyValue = ReflectUtils.dynamicGet(result, primaryKeyName);
               } else {
                   primaryKeyValue = args[primaryKeySort];
               }
           }
       }

       OperateLog operateLog = new OperateLog();

       //
       if (JSONUtil.toJsonStr(params).length() <= 2000) {
           operateLog.setData(JSONUtil.toJsonStr(params));
       }

       operateLog.setUserId(1L);
       operateLog.setUserName(username == null ? "系統" : username);
       operateLog.setModule(module);
       operateLog.setOpType(opType);
       operateLog.setBusiness(business);

       String recordId = null;
       if (primaryKeyValue instanceof Object[]) {
           recordId = Arrays.toString((Object[]) primaryKeyValue);
       } else {
           recordId = primaryKeyValue.toString();
       }
       operateLog.setRecordId(recordId);
       operateLog.setCreateTime(new Date());
       logRepository.save(operateLog);
       log.info(">>>記錄檔:{}", operateLog);
       return returnValue;
   }

   /**
    * 擷取class的主鍵字段名 主鍵值
    */
   @SuppressWarnings({"rawtypes", "unchecked"})
   private static Object getPrimaryKeyName(Class<?> clazz) throws Exception {
       Object param = null;
       // 遞歸擷取父子類所有的field
       Class tempClass = clazz;
       // 當父類為null的時候說明到達了最上層的父類(Object類
       while (tempClass != null && !StringUtils.equals(tempClass.getName().toLowerCase(), "java.lang.object")) {
           Field[] fields = tempClass.getDeclaredFields();
           for (Field field : fields) {
               String fieldName = field.getName();
               // boolean類型不必判斷,因實體裡包含boolean類型的屬性,getter方法是以is開頭,不是get開頭
               if (field.getType().equals(Boolean.class) || field.getType().getName().equals("boolean")) {
                   continue;
               }
               if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
                   continue;
               }
               String getterMethod = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
               Method method = tempClass.getDeclaredMethod(getterMethod);

               // 字段上是否存在@Id注解
               Object primaryAnnotation = field.getAnnotation(Id.class);// for hibernate
               if (primaryAnnotation == null)
                   primaryAnnotation = field.getAnnotation(org.springframework.data.annotation.Id.class);// for spring
               // data
               // getter方法上是否存在@Id注解
               if (primaryAnnotation == null)
                   primaryAnnotation = method.getAnnotation(Id.class);
               if (primaryAnnotation == null)
                   primaryAnnotation = method.getAnnotation(org.springframework.data.annotation.Id.class);
               // 存在@Id注解,則說明該字段為主鍵
               if (primaryAnnotation != null) {
                   /* String primaryKeyName = field.getName(); */
                   param = field.getName();
                   break;
               }
           }
           if (param != null && StringUtils.isNotBlank(param.toString())) {
               break;
           }
           // 得到父類指派給tempClass
           tempClass = tempClass.getSuperclass();
       }
       if (param == null) {
           throw new Exception(clazz.getName() + "實體,未設定主鍵");
       }
       return param;
   }

   /**
    * 擷取@SystemLog 注解上資訊
    *
    * @param joinPoint
    * @return map
    * @throws Exception
    */
   @SuppressWarnings("rawtypes")
   public static Map<String, Object> getControllerAnnotationValue(JoinPoint joinPoint) throws Exception {
       String targetName = joinPoint.getTarget().getClass().getName();
       String methodName = joinPoint.getSignature().getName();
       Object[] arguments = joinPoint.getArgs();
       Class targetClass = Class.forName(targetName);
       Method[] methods = targetClass.getMethods();
       Map<String, Object> map = new HashMap<>();
       for (Method method : methods) {
           if (method.getName().equals(methodName)) {
               Class[] classes = method.getParameterTypes();
               if (classes.length == arguments.length) {
                   // 取入參資料
                   String description = method.getAnnotation(SystemLog.class).description();
                   String module = method.getAnnotation(SystemLog.class).module().name();
                   String opType = method.getAnnotation(SystemLog.class).opType().name();
                   String primaryKeyName = method.getAnnotation(SystemLog.class).primaryKeyName();
                   int primaryKeySort = method.getAnnotation(SystemLog.class).primaryKeySort();
                   Class<?> clazz = method.getAnnotation(SystemLog.class).primaryKeyBelongClass();
                   map.put("module", module);
                   map.put("opType", opType);
                   map.put("business", description);
                   map.put("primaryKeyName", primaryKeyName);
                   map.put("primaryKeySort", primaryKeySort);
                   map.put("primaryKeyBelongClass", clazz);
                   break;
               }
           }
       }
       return map;
   }

   @AfterReturning("webLog()")
   public void doAfterReturning(JoinPoint joinPoint) {
       // 處理完請求,傳回内容
       // log.info("WebLogAspect.doAfterReturning()");
   }
}      

四、​​Spring下所有的注解,及其用法,持續更補​​