当前位置:   article > 正文

java注解完整实例_java自定义注解案例

java自定义注解案例

背景

java注解,网上的实例千篇一律,感觉大多都没有抓到重点。最糟糕的是,示例不全,不能完全让别人把示例做出来。
起因:希望通过自定义一个字段校验的注解,来理解注解。

什么是注解

意如其名,其本来的意思就是用来做标注用:可以在类、字段变量、方法、接口等位置进行一个特殊的标记,为后续做一些诸如:代码生成、数据校验、资源整合等工作做铺垫。
注解一旦对代码标注完成,后续我们就可以结合Java强大的反射机制,在运行时动态地获取到注解的标注信息,从而可以执行很多其他逻辑,完成我们想要的自动化工作。
总的来说就是先标记,然后定义有这个标记的需要做操作,以及什么时候触发这个操作。
但其实注解只是标记,标记的动作和标记的触发是单独的内容。并不需要一定存在,你可以只定义个标记,而不去对这个标记做任何操作。

动手造一个注解

目标:手动创建一个length注解,校验字段的长度。
一般我们使用注解有两种方式,一种是验证器配合着使用,另外一种就是利用 AOP 结合使用。

验证器方式

第一步:首先定义注解:@Length
@Target({ElementType.FIELD})//用来说明该自定义注解可以用在什么位置
@Retention(RetentionPolicy.RUNTIME)//用来说明你自定义注解的生命周期
@Documented
@Constraint(validatedBy = {LengthValidator.class})  // 与约束注解关联的验证器
public @interface Length {
    int min(); //允许字符串长度的最小值
    int max();//允许字符串长度的最大值
    String message() default "长度超过限制";//自定义的错误提示信息
    // 约束注解在验证时所属的组别
    Class<?>[] groups() default {};
    // 约束注解的有效负载
    Class<? extends Payload>[] payload() default {};

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
@Target(xxx)

用来说明该自定义注解可以用在什么位置
ElementType.FIELD:说明自定义的注解可以用于类的变量
ElementType.METHOD:说明自定义的注解可以用于类的方法
ElementType.TYPE:说明自定义的注解可以用于类本身、接口或 enum类型
等等… 还有很多,如果记不住,建议现用现查。

@Retention(xxx)

保留的意思,用来说明你自定义注解的生命周期,比如:
@Retention(RetentionPolicy.RUNTIME):表示注解可以一直保留到运行时,因此可以通过反射获取注解信息
@Retention(RetentionPolicy.CLASS):表示注解被编译器编译进 class文件,但运行时会忽略
@Retention(RetentionPolicy.SOURCE):表示注解仅在源文件中有效,编译时就会被忽略

@Documented

它的作用是能够将注解中的元素包含到 Javadoc 中去。

@Constraint

与约束注解关联的验证器,LengthValidator.class中是验证器的内容。
使用验证器方式时需要配置这个注解,AOP则不需要

注意点

成员变量没有使用 default 定义默认值,在使用注解时,就必须要对它进行赋值,有默认值则可以不必赋值;

String message() default "长度超过限制"
  • 1

使用的地方可以不写message,因为上面有默认值了(这个编译时会检查出来的不用特意去记):

@Length(min= 1, max= 3)
private String username;
  • 1
  • 2

还有就是必须是message,不能改名称,应该是验证器写死了。我改了之后报错:

javax.validation.ConstraintDefinitionException: HV000074: com.xxxxx.annotation.Length contains Constraint annotation, but does not contain a message parameter

固定加上下面的(详细是做什么的,我也不清楚):

// 约束注解在验证时所属的组别
Class<?>[] groups() default {};
// 约束注解的有效负载
Class<? extends Payload>[] payload() default {};
  • 1
  • 2
  • 3
  • 4

如果没有上面的话,会报错:
but does not contain a groups parameter 注解需要定义成员变量 groups
xxx contains Constraint annotation, but does not contain a payload parameter. 注解需要定义成员变量 Payload
还有就是

第二步:定义验证器
/**
 * @author aliyu
 * @create 2020-07-07 18:41
 * 类描述:Length-是哪个注解的校验, Object注解校验的参数是什么类型
 */
public class LengthValidator implements ConstraintValidator<Length, Object> {
    private Integer min;
    private Integer max;

    @Override
    public void initialize(Length constraintAnnotation) {
        this.min = constraintAnnotation.min();
        this.max = constraintAnnotation.max();
    }

    /**
     * 验证方法
     * @param object
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Object object, ConstraintValidatorContext context) {
        if (object == null) {
            return true;
        }
        if (((String)object).length() >= min && ((String)object).length() <= max) {
            return true;
        }
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

验证器LengthValidator 实现 ConstraintValidator<Length, Object>接口,并重写initialize和isValid。Length-是哪个注解的校验, Object注解校验的参数是什么类型(说明校验的参数可以是任意类型,当然你也可以指定)。
我们的验证内容写在isValid。

isValid

当触发isValid方法时,object 直接获取到了标注了@Length的字段的值。不知名的内部通过反射找到标注了@Length注解的字段以及它的值
在这里插入图片描述
context的basePath 就是注解的字段 username

第三步 使用注解

dto 为字段加上注解:

public class AdminDTO {

    private Integer id;

    @Length(min= 1, max= 3)
    private String username;

    private String password;


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

调用Controller时,对传入的dto进行校验,需要加 @Validated触发 校验,如果不加的话,不会进行校验。

@RequestMapping("/login")
public R login(@RequestBody @Validated AdminDTO adminDTO)throws Exception{
    System.out.println("A");
    return null;
}
  • 1
  • 2
  • 3
  • 4
  • 5

AOP方式

第一步:首先定义注解:@Length
@Target({ElementType.FIELD})//用来说明该自定义注解可以用在什么位置
@Retention(RetentionPolicy.RUNTIME)//用来说明你自定义注解的生命周期
@Documented
//@Constraint(validatedBy = {LengthValidator.class})  // 与约束注解关联的验证器(AOP方式不需要)
public @interface LengthAop {
    int min(); //允许字符串长度的最小值
    int max();//允许字符串长度的最大值
    String errorMsg();//自定义的错误提示信息(AOP方式随便命名,相对于应验证器模式)
    //String message();//自定义的错误提示信息
    //AOP方式不需要下面的
//    Class<?>[] groups() default {}; // 约束注解在验证时所属的组别
//    Class<? extends Payload>[] payload() default {};// 约束注解的有效负载

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

仔细看一下与验证器方式的不同。

第二步:定义验证方法

AOP的方式,就是标注了@length注解的字段校验,触发由自己定义,一般都是自己定义拦截器或者过滤器。让请求在进入congtroller之前对dto的内容进行校验。这个拦截器,我就不写了,可以自行百度。

/**
     * 验证方法
     * @param object 要验证的数据的类型
     * @return
     */
    public String isValid(Object object) throws IllegalAccessException{
        // 首先通过反射获取object对象的类有哪些字段
        Field[] fields = object.getClass().getDeclaredFields();
        //for循环逐个字段校验,看哪个字段上标了注解
        for (Field field : fields){
            // if判断:检查该字段上有没有标注了@Length注解
            if (field.isAnnotationPresent(Length.class)){
                // 通过反射获取到该字段上标注的@Length注解的详细信息
                Length annotation = field.getAnnotation(Length.class);
                field.setAccessible(true);// 让我们在反射时能访问到私有变量
                int value = ((String)field.get(object)).length();
                if(value<annotation.min() || value>annotation.max()){
                    return annotation.message();
                }
            }
        }
        return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

可以看出查找注解的字段,已及注解字段的值,都需要自己去编写代码。通过反射可以得到注解的信息,并根据这些信息写自己的验证内容。

第三步 使用注解

在这里插入图片描述
aop方式不需要controller 写什么@Validated ,因为它触发的地方是自己写的拦截器。

总结

我感觉把,验证器方式算是比较常用的,但是它应该是写死了触发的时机。而AOP方式较为灵活,但是代码需要自己写。

其他

参考自:
听说你只会用注解,不会自己写注解?那有点危险了
https://mp.weixin.qq.com/s/Z7nL1hNF74CdJGMORyFWzQ
Java 注解的初步学习
https://www.cnblogs.com/michael-xiang/p/12369898.html
下面说的注解原理应该就是实际spring注解实现的原理:
https://www.jianshu.com/p/104984eacf41

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/240210
推荐阅读
相关标签
  

闽ICP备14008679号