【经验分享】Java注解实战进阶

原文地址:https://mp.weixin.qq.com/s/gdYysBB3aD_HmPyvEThFXw

Java猿的命根子!

自Java EE框架步入Spring Boot时代之后,注解简直是Java程序员的命根子啊,面向注解编程成了日常操作!

换句话的意思就是说:如果没有注解,我们啥也干不了哇(滑稽)。

图片

这岂不是很危险!

所以本文来唠一唠关于注解的相关操作,并自己动手来写一个注解感受一下原理。原理性的东西掌握了,心里自然就不慌了。


注解的基本原理

首先必须要说的是,注解它也不是什么高深的玩意儿,没必要畏惧它!

意如其名,其本来的意思就是用来做标注用:可以在字段变量方法接口等位置进行一个特殊的标记,为后续做一些诸如:代码生成数据校验资源整合等工作做铺垫。

对嘛,就做标记用的嘛!

注解一旦对代码标注完成,后续我们就可以结合Java强大的反射机制,在运行时动态地获取到注解的标注信息,从而可以执行很多其他逻辑,完成我们想要的自动化工作。

所以,反射必须要学好!


来!动手造一个注解

Spring自身提供了非常多好用的注解可以用来方便地帮我们做数据校验的工作。

比如,在没有注解加持时,我们想要校验 Student类:

public class Student {
    private Long id;        // 学号
    private String name;    // 姓名    
    private String mobile;  // 手机号码(11位)
}

我们只能通过手写 if判断来进行校验:

image-20240816102430152

这样非常繁琐!

但是借助于 Spring提供的注解,数据校验工作可以变得非常优雅,就像这样:

image-20240816102447880

于是很多人就表示疑问,这些注解到底如何实现功能的呢

今天本文则以上文的 @Length注解为例,自己动手实现一遍,这个学会了,其他注解实现原理也是类似。

图片

总共分三大步实现。

第一步:首先定义注解:@LengthCustom

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LengthCustom {
    /**
     * 允许字符串长度的最大值
     */
    int max();

    /**
     * 允许字符串长度的最小值
     */
    int min();

    /**
     * 错误消息
     */
    String errorMsg() default "string length illegal";


}

下面做几点说明:

1、注解的定义有点像定义接口 interface,但唯一不同的是前面需要加一个 @符号

2、注解的成员变量只能使用基本类型、 String或者 enum枚举,比如 int可以,但 Integer这种包装类型就不行,需注意

3、像上面 @Target@Retention这种加在注解定义上面的注解,我们称为 “元注解”,元注解就是专门用于给注解添加注解的注解,哈哈,很拗口,简单理解,元注解就是天生就有的注解,可直接用于注解的定义上

4、 @Target(xxx) 用来说明该自定义注解可以用在什么位置,比如:

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

5、 @Retention(xxx) 用来说明你自定义注解的生命周期,比如:

  • @Retention(RetentionPolicy.RUNTIME):表示注解可以一直保留到运行时,因此可以通过反射获取注解信息
  • @Retention(RetentionPolicy.CLASS):表示注解被编译器编译进 class文件,但运行时会忽略
  • @Retention(RetentionPolicy.SOURCE):表示注解仅在源文件中有效,编译时就会被忽略

所以声明周期从长到短分别为:RUNTIME > CLASS > SOURCE ,一般来说,如果需要在运行时去动态获取注解的信息,还是得用RUNTIME,就像本文所用。

第二步:获取注解并对其进行验证

在运行时想获取注解所代包含的信息,该怎么办?那当然得用 Java的反射相关知识

下面写了一个验证函数 validate(),代码中会逐行用注释去解释想要达到的目的,认真看一下每一行的注释:

    public static String validate(Object object) throws IllegalAccessException {

        // 首先通过反射获取object对象的类有哪些字段
        // 对本文来说就可以获取到Student类的id、name、mobile三个字段
        Field[] fields = object.getClass().getDeclaredFields();

        // for循环逐个字段校验,看哪个字段上标了注解
        for (Field field : fields) {

            // if判断:检查该字段上有没有标注了@Length注解
            if (field.isAnnotationPresent(LengthCustom.class)) {

                // 通过反射获取到该字段上标注的@LengthCustom注解的详细信息
                LengthCustom lengthCustom = field.getAnnotation(LengthCustom.class);
                field.setAccessible(true);
                // 让我们在反射时能访问到私有变量
                // 用过反射获取字段的实际值:这里直接强转成string并获取长度
                int value = ((String) field.get(object)).length();
                // 将字段的实际值和注解上做标示的值进行比对
                if (value < lengthCustom.min() || value > lengthCustom.max()) {
                    return lengthCustom.errorMsg();
                }
            }
        }
        return null;
    }
}

可见,学好Java的反射知识是多么的重要!

第三步:使用注解

这一步比较轻松,使用注解的过程往往都是很愉悦的

public class Student implements Serializable {

    private Long id;

    @LengthCustom(min = 2 , max= 4 , errorMsg = "名称长度不符合")
    private String name;
    private Integer age;

    private Integer classId;
}

写一个测试案例:

public class Validator {
    public static void main(String[] args) throws IllegalAccessException {
        Student 小王 = Student.builder().id(1L).name("小王").build();
        System.out.println(validate(小王));
        Student 小王2 = Student.builder().id(2L).name("小王小王小王小王小王小王").build();
        System.out.println(validate(小王2));
    }
}

结果:

null
名称长度不符合

怎么样,其实一点也不复杂吧,主要就是反射相关的知识!

好了,关于如何动手自定义注解的相关内容就抛砖引玉到这里吧,每天进步一点点,Peace!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xcong_Zhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值