zoukankan      html  css  js  c++  java
  • 自定义注解

    基础知识:自定义注解

    使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

    定义注解类型元素时需要注意如下几点

    • 访问修饰符必须为public,不写默认为public;
    • 该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组;
    • 该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作);
    • ()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
    • default代表默认值,值必须和第2点定义的类型一致;
    • 如果没有默认值,代表后续使用注解时必须给该类型元素赋值。

     常用的元注解

    java中有四种元注解:@Retention、@Inherited、@Documented、@Target

    @Retention 注解的保留位置(枚举RetentionPolicy),RetentionPolicy可选值:

    • SOURCE:注解仅存在于源码中,在class字节码文件中不包含(如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到)
    • CLASS:默认的保留策略,注解在class字节码文件中存在,但运行时无法获得(如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVM(Java虚拟机)会忽略它,我们在运行期也不能读取到)
    • RUNTIME:注解在class字节码文件中存在,在运行时可以通过反射获取到(如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME,在默认的情况下,自定义注解是使用的RetentionPolicy.CLASS)

    @Inherited 声明子类可以继承此注解,如果一个类A使用此注解,则类A的子类也继承此注解

    @Documented 声明注解能够被javadoc等识别

    @Target 用来声明注解范围(枚举ElementType),ElementType可选值:

    • TYPE:接口、类、枚举、注解
    • FIELD:字段、枚举的常量
    • METHOD:方法
    • PARAMETER:方法参数
    • CONSTRUCTOR:构造函数
    • LOCAL_VARIABLE:局部变量
    • ANNOTATION_TYPE:注解
    • PACKAGE:包

    首先,定义一个注解、和一个供注解修饰的简单Java类

    定义注解格式:
      public @interface 注解名 {定义体} 例:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface OperationLog {
       //表示操作是那个服务哪个模块下的操作
      String module() default "xxxx服务";

      //操作的类型,添加,更新,删除
      String type() default "add";

      //操作者
      String user() default "system";

      //操作描述
      String operation() default "";

    }

     二、Aspect类

    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
     
    import javax.servlet.http.HttpServletRequest;
    import java.text.SimpleDateFormat;
     
    @Aspect
    @Component
    public class OperationLogAspect {
        private ThreadLocal<SimpleDateFormat> format = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        
        //切点表达式,表示加了OperationLog注解的都是切点,路径是自定义注解的全路径
        @Pointcut("@annotation(com.alice.springboot.demo.OperationLog)")
        public void pointcut(){ 
        }
        
        @Around("@annotation(operationLog)")//拦截加了OperationLog注解的方法,也可以直接拦截上面定义的切点(@Around("pointcut()"))
        public Object operationLogRecord(ProceedingJoinPoint joinPoint, OperationLog operationLog){
            //获取请求
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            //响应
            ResponseResult<Object> responseResult = null;
            //判断原方法是否正常执行的标志
            boolean isNormalProcess = false;
            try{
                //返回切点处继续执行原方法,并接收原方法的返回值
                responseResult = (ResponseResult<Object>) joinPoint.proceed();    
                //如果顺利执行,那么说明原方法执行正常,就可以进行日志记录。因为,如果原方法的增删改出问题了,那么日志就不需要记录了,不用记录失败的操作。
                isNormalProcess = true;
            }catch (Throwable e){
                System.out.println("原方法报错,不需要记录日志");
            }try {
                if (isNormalProcess){
                    //如果原方法正常执行完毕,那么需要记录操作日志
                    saveOperationLog(joinPoint, operationLog, request);
                }
            }catch (Exception e){
                System.out.println("保存操作日志出错");
            }
            return  responseResult;
        }
        
        private void saveOperationLog(ProceedingJoinPoint joinPoint, OperationLog operationLog, HttpServletRequest request){
            //用来记录参数的值
            StringBuilder contentBuilder = new StringBuilder();
            //从切点获取切点的所有参数
            Object[] allParams = joinPoint.getArgs();
            
            for (Object param: allParams){
                contentBuilder.append(JSON.toJSONString(param) + ",");
            }
            //删除最后一个多余的逗号
            contentBuilder.delete(contentBuilder.length() - 1, contentBuilder.length());
            
            //执行数据库操作,将信息保存到数据库,笔者这里使用的是mongodb,仅供参考,主要看获取自定义注解里面的值
            Document document = new  Document();
            //获取自定义注解里面的值
            document.append("module", operationLog.module())
                    .append("type", operationLog.type())
                    .append("user", operationLog.user())
                    .append("operation", operationLog.operation())
                    .append("content", contentBuilder.toString());
            
            logDao.saveLogs("mongo collection name", document);
        }
    }

     三、使用方法

    package com.alice.springboot.demo;
     
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
     
    @RestController
    @RequestMapping(value = "/test")
    public class OperationLogController {
        @RequestMapping(value = "/add", method = RequestMethod.POST)
        @OperationLog(module = "xxx服务", type = "add", operation = "添加xxx")
        //这里使用到了自定义注解,并且赋值了自定义注解里面的某些值,最后在aspect里面可以获取到这些值
        public ResponseResult<String> addOperation(String user, String content){
            ResponseResult<String> result = new ResponseResult<>();
            try{
                //执行添加操作
                result.setStatus(ResponseStatusEnum.SUCCESS);
                result.setMessage("添加操作成功");
            }catch (Exception e){
                result.setStatus(ResponseStatusEnum.FAIL);
                result.setMessage("添加操作失败" + e.toString());
            }
            return result;
        }
    }

    原文连接:https://blog.csdn.net/jmdonghao/article/details/78880899

  • 相关阅读:
    java 字符串截取
    字符编码Unicode-正则表达式验证
    APP数据加密解密
    ThreadLocal线程局部变量
    用Eclipse进行远程Debug代码
    JPA对应关系
    JPA名称规则
    dubbo环境搭建
    历史表更新数据
    api加密算法
  • 原文地址:https://www.cnblogs.com/blwy-zmh/p/11775712.html
Copyright © 2011-2022 走看看