Java自定义注解


目录:

  1. 概念
  2. 字段脱敏注解Demo

参考:

概念

注解是元数据的一种形式,可以添加到Java源代码中。 类,方法,变量,参数和包都可以被注释。 注解对其注释的代码的操作没有直接影响。

通过自定义注解,可以对类或方法等进行打标,实现自定义的逻辑。

自定义注解使用@interface关键字

public @interface FieldSensitive {
}

还有一些注解,用来对注解使用做一些限制,如下。

@Retention

@Retention作用是什么

  Retention的翻译过来就是”保留”的意思。也就意味着它的作用是,用来定义注解的生命周期的,并且在使用时需要指定RetentionPolicyRetentionPolicy有三种策略,分别是:

  • SOURCE - 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃。
  • CLASS - 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期。
  • RUNTIME - 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。

选择合适的生命周期

  首先要明确生命周期 RUNTIME > CLASS > SOURCE 。一般如果需要在运行时去动态获取注解信息,只能使用RUNTIME。如果要在编译时进行一些预处理操作,比如生成一些辅助代码就用CLASS。如果只是做一些检查性的操作,比如 @Override和@SuppressWarnings,则可选用 SOURCE。

我们实际开发中的自定义注解几乎都是使用的RUNTIME

@Retention(RetentionPolicy.RUNTIME)
public @interface Encryption {

}

@Target

  @Target注解是限定自定义注解可以使用在哪些地方。这就和参数校验一样,约定好规则,防止乱用而导致问题的出现。针对上述的需求可以限定它只能用方法上。根据不同的场景,还可以使用在更多的地方。比如说属性、包、构造器上等等。

  • TYPE - 类,接口(包括注解类型)或枚举
  • FIELD - 字段(包括枚举常量)
  • METHOD - 方法
  • PARAMETER - 参数
  • CONSTRUCTOR - 构造函数
  • LOCAL_VARIABLE - 局部变量
  • ANNOTATION_TYPE -注解类型
  • PACKAGE - 包
  • TYPE_PARAMETER - 类型参数
  • TYPE_USE - 使用类型

  上面两个是比较常用的元注解,Java一共提供了4个元注解。你可能会问元注解是什么?元注解的作用就是负责注解其他注解。

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

}

@Documented

  @Documented的作用是对自定义注解进行标注,如果使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来。没什么实际作用,了解一下就好了。

@Inherited

  被@Inherited修饰的注解,被用在父类上时其子类也拥有该注解。 简单的说就是,当在父类使用了被@Inherited修饰的注解@InheritedTest时,继承它的子类也拥有@InheritedTest注解。

字段脱敏注解Demo

实现一个自定义注解,用来将日志中的敏感字段进行脱敏输出。

依赖

<!-- 集成web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.30</version>
        </dependency>

枚举类

public enum SensitiveEnum {

    /** 自定义(此项需设置脱敏的范围)*/
    CUSTOMER("customer"),

    /** 姓名 */
    NAME("name"),

    /** 身份证号 */
    ID_NO("idNo"),

    /** 手机号 */
    PHONE("phone"),

    /** 邮箱 */
    EMAIL("email");

    private String name;

    SensitiveEnum(String name){
        this.name = name;
    }
}

自定义注解

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldSensitive {
    /**敏感字段脱敏格式,默认为自定义脱敏格式*/
    SensitiveEnum type() default SensitiveEnum.CUSTOMER;

    /**前缀不需要脱敏的长度*/
    int prefixNoMaskLen() default 1;

    /**后缀不需要脱敏的长度*/
    int suffixNoMaskLen() default 1;

    /**使用的脱敏占用符号*/
    String symbol() default "*";
}

拦截AOP

我这里由于直接修改结果返回值,所以使用了**@Around**注解

@Aspect
@Component
public class SensitiveAop {

    /**
     * 定义切入点
     */
//    @Pointcut("execution(* com.qian..*.*(..))")
//    public void pointcut(){
//
//    }

    /**
     * 定义advisor
     */
    @Around("execution(* com.qian.annotationDemo.controller..*.*(..))")
    public Object aroundAop(ProceedingJoinPoint joinPoint) throws Throwable {
//        String methodName = joinPoint.getSignature().getName();
//        if(!methodName.equals("test")){
//            return data;
//        }
        Object data = joinPoint.proceed();
        if (data == null) {
            return "";
        }
        User user;
        if(data instanceof User) {
            user = (User) data;
        }if(data instanceof String){
            user = JSON.parseObject((String) data, User.class);
        }else {
            return "";
        }
        Class<?> objClass = user.getClass();
        Field[] fields = objClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            FieldSensitive annotation = field.getAnnotation(FieldSensitive.class);
            if (annotation != null) {
                Object value = field.get(user);
                if (ObjectUtils.isNotEmpty(value) && StringUtils.isNotBlank((String)value)) {
                    SensitiveEnum sensitiveEnum = annotation.type();
                    int prefixNoMaskLen = annotation.prefixNoMaskLen();
                    int suffixNoMaskLen = annotation.suffixNoMaskLen();
                    String symbol = annotation.symbol();
                    switch (sensitiveEnum) {
                        case CUSTOMER:
                            value = SensitiveUtil.desValue((String)value, prefixNoMaskLen, suffixNoMaskLen, symbol);
                            break;
                        case ID_NO:
                            value = SensitiveUtil.hideIdNo((String)value);
                            break;
                        case NAME:
                            value = SensitiveUtil.hideName((String)value);
                            break;
                        case PHONE:
                            value = SensitiveUtil.hidePhone((String)value);
                            break;
                        default:
                            throw new IllegalArgumentException("unknown privacy type enum " + sensitiveEnum);
                    }
                    field.set(user, value);
                    field.setAccessible(false);
                }
            }
        }
        return JSON.toJSONString(user);
    }
}

实体类

public class User {

    public User() {
    }

    /**
     * 全参构造函数
     * @param userId
     * @param name
     * @param phone
     */
    public User(String userId, String name, String phone) {
        this.userId = userId;
        this.name = name;
        this.phone = phone;
    }

    /**
     * userId
     */
    private String userId;

    /**
     * 姓名
     */
    @FieldSensitive(type = SensitiveEnum.NAME)
    private String name;

    /**
     * 电话
     */
    @FieldSensitive(type = SensitiveEnum.PHONE)
    private String phone;
}

Controller

@RestController
public class SensitiveController {

    /**
     * 测试注解是否生效
     * @return
     */
    @RequestMapping("/annotation")
    public String test(){
        User user = new User("1", "千小虎", "12388889999");
        return JSON.toJSONString(user);
    }
}

运行结果

运行结果


文章作者: 小小千千
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小小千千 !
评论
  目录