注解的原理

1)什么是注解:

    Java 注解就是代码中的一些特殊标记(元信息),用于在编译、类加载、运行时进行解析和使用,并执行相应的处理。它本质是继承了 Annotation 的特殊接口,其具体实现类是 JDK 动态代理生成的代理类,通过反射获取注解时,返回的也是 Java 运行时生成的动态代理对象 $Proxy1。通过代理对象调用自定义注解的方法,会最终调用 AnnotationInvocationHandler 的 invoke 方法,该方法会从 memberValues 这个Map中查询出对应的值,而 memberValues 的来源是Java常量池。

    注解在实际开发中非常常见,比如 Java 原生的 @Overried、@Deprecated 等,Spring的 @Controller、@Service等,Lombok 工具类也有大量的注解,不过在原生 Java 中,还提供了元 Annotation(元注解),他主要是用来修饰注解的,比如 @Target、@Retention、@Document、@Inherited 等。

@Target:标识注解可以修饰哪些地方,比如方法、成员变量、包等,具体取值有以下几种:ElementType.TYPE/FIELD/METHOD/PARAMETER/CONSTRUCTOR/LOCAL_VARIABLE/ANNOTATION_TYPE/PACKAGE/TYPE_PARAMETER/TYPE_USE
@Retention:什么时候使用注解:SOURCE(编译阶段就丢弃) / CLASS(类加载时丢弃) / RUNTIME(始终不会丢弃),一般来说,我们自定义的注解都是 RUNTIME 级别的,因为大多数情况我们是根据运行时环境去做一些处理,一般需要配合反射来使用,因为反射是 Java 获取运行是的信息的重要手段
@Document:表示该注解可以被生成API文档
@Inherited:表示该注解可以被继承
(2)如何自定义注解?

① 创建一个自定义注解:与创建接口类似,但自定义注解需要使用 @interface

② 添加元注解信息,比如 @Target、@Retention、@Document、@Inherited 等

③ 创建注解方法,但注解方法不能带有参数

④ 注解方法返回值为基本类型、String、Enums、Annotation 或其数组

⑤ 注解可以有默认值;

@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface CarName {
String value() default “”;
}

自定义注解的使用场景
通过AOP和自定义注解实现 >>操作日志 --> DB数据库

定义日志字段属性

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

}

使用注解(处理注解),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()");
   }
}


原文链接:https://blog.csdn.net/a745233700/article/details/80959716,https://blog.csdn.net/qq_42105629/article/details/102474481

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值