<tr id="ieeco"><small id="ieeco"></small></tr>
<acronym id="ieeco"></acronym>

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

一、注解的作用

  • 注解是一種元數據形式。即注解是屬于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;

(2) - 定義注解

@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();

}

(3) - 使用注解(處理注解),AOP / Aspect + 反射(通過注解獲取對象的字段,方法等信息)

@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下所有的注解,及其用法,持續更補

相關推薦
??2020 CSDN 皮膚主題: 書香水墨 設計師:CSDN官方博客 返回首頁
彩票送彩金