Tim

一枚野生程序员~

  • 主页
  • 分类
  • 标签
  • 归档
  • 关于
所有文章 工具

Tim

一枚野生程序员~

  • 主页
  • 分类
  • 标签
  • 归档
  • 关于

注解的原理与实现

阅读数:次 2020-04-12
字数统计: 1.5k字   |   阅读时长≈ 6分

注解这个东西自从SpringBoot以来一直是Java开发者们必备的生存技巧呀,我们平时几乎大部分时间都是面向注解编程,通过注解我们可以节约大量的时间。用过了这么多的注解,那么我们否有关注过注解的实现原理呢?所以本篇文章主要是讲述注解的有关操作,自己实现一个注解来体会注解的实现原理,注解也不是特别高深的东西,掌握了自然就明白了。

注解的基本原理

注解本来的意思就是用来做标注用:可以在类、字段变量、方法、接口等位置进行一个特殊的标记,为后续做一些诸如: 代码生成、数据校验、资源整合等工作做铺垫。所以注解就是做标记用的,注解一旦对代码标注完成,后续就可以结合Java强大的反射机制,在运行时动态地获取到注解的标注信息,从而可以执行很多其他逻辑,完成我们想要的自动化工作。所以,反射机制很重要。

注解的使用示例

假设我们现在有个Person类,这个Person类要当做参数传入,我们要对参数进行校验:

1
2
3
4
5
6
7
public class Person {
private Integer id;
private String name;
private Integer age;

//Getter and Setter
}

如果没有注解,那么我们就需要写这样一长串的if else校验:

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
public String addPerson(Person person){
if(person == null){
return "参数为空";
}

if(person.getId() == null || "".equals(person.getId())){
return "Person's id is null";
}

if(person.getName() == null || "".equals(person.getName())){
return "Person's name is null.";
}

if(person.getName().length() < 3){
return "Person's name length must lager 3.";
}

if(person.getAge() == 0){
return "Person's age is null.";
}

if(person.getAge() <= 0 || person.getAge() >= 150){
return "Person's age error.";
}
}

所以,可以参考一下如何使用注解来校验这些参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Person {
@NotNull(message = "传入的Id为空值")
@NotEmpty(message = "传入的Id为空字符串")
private String id;

@NotNull(message = "传入的Name为空值")
@NotEmpty(message = "传入的Name为空字符串")
@Length(min = 3, max = 30, message = "姓名长度必须3-30之间")
private String name;

@NotNull(message = "传入的Age为空值")
@Min(value = 0, message = "年龄应该在0-150之间")
@Max(value = 150, message = "年龄应该在0-150之间")
private Integer age;

//Getter and Setter.
}

@Length注释的实现

本篇文章中,我们就来实现一下@Length这个注解,这个注解学会了,其他注解也都是一样的:

step1.定义注解 @Length

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Length{
// 允许的字符串长度最小值
int min();

// 允许的字符串长度最大值
int max();

// 自定义错误提示
String errorMsg();
}

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,就像本文所用。

step2.获取注解并对其验证

在运行时想获取注解所代包含的信息,该怎么办?我们得用Java的反射相关的知识!下面写了一个验证函数validate(),代码中会逐行用注释去解释想要达到的目的,认真看一下每一行的注释:

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
public class LengthValidator {
public static String validateField(Object object) throws IllegalAccessException {
// 获取字段值
// 对本文来说就是Person的id、name、age三个字段
Field[] fields = object.getClass().getDeclaredFields();

// 逐个字段校验,看看哪个字段标了注解
for (Field field: fields){
// if判断:检查字段上面有没有标注@Length注解
if(field.isAnnotationPresent(Length.class)){
// 通过反射获取到该字段上标注的@Length的注解的详细信息
Length length = field.getAnnotation(Length.class);
// 让我们在反射时看到私有变量
field.setAccessible(true);
// 获取实际字段的值
int value = ((String)field.get(object)).length();
// 将实际字段的值和注解的标记值进行对比
if(value < length.min() || value > length.max()){
return length.errorMsg();
}
}
}

return null;
}
}

step3.使用自定义注解

此时,Person类只需要加上此注解

1
2
3
4
5
6
7
8
9
10
public class Person {
private String id;

@Length(min = 3, max = 30, errorMsg = "姓名长度必须3-30之间")
private String name;

private Integer age;

//Getter and Setter
}

然后使用即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AnnotationTest {
public static void main(String[] args) throws IllegalAccessException {
Person person = new Person();
person.setName("13");
person.setAge(10);
person.setId("001");

String validateField = LengthValidator.validateField(person);
if(validateField == null)
System.out.println(person);
else
System.out.println(validateField);
}
}

mark

赏

谢谢你请我喝咖啡

支付宝
微信
  • 本文作者: Tim
  • 本文链接: https://zouchanglin.cn/607028132.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 许可协议。转载请注明出处!
  • JavaSE
  • JavaWeb
  • JavaEE
  • 编程语言

扫一扫,分享到微信

堆的实现及其应用
关于快排和归并的思考
  1. 1. 注解的基本原理
  2. 2. 注解的使用示例
  3. 3. @Length注释的实现
    1. 3.1. step1.定义注解 @Length
    2. 3.2. step2.获取注解并对其验证
    3. 3.3. step3.使用自定义注解
© 2017-2021 Tim
本站总访问量次 | 本站访客数人
  • 所有文章
  • 工具

tag:

  • 生活
  • Android
  • 索引
  • MySQL
  • 组件通信
  • Nginx
  • JavaSE
  • JUC
  • JavaWeb
  • 模板引擎
  • 前端
  • Linux
  • 计算机网络
  • Docker
  • C/C++
  • JVM
  • 上传下载
  • JavaEE
  • SpringCloud
  • Golang
  • Gradle
  • 网络安全
  • 非对称加密
  • IDEA
  • SpringBoot
  • Jenkins
  • 字符串
  • vim
  • 存储
  • 文件下载
  • Mac
  • Windows
  • NIO
  • RPC
  • 集群
  • 微服务
  • SSH
  • 配置中心
  • XML
  • Chrome
  • 压力测试
  • Git
  • 博客
  • 概率论
  • 排序算法
  • 分布式
  • 异常处理
  • 文件系统
  • 哈希
  • openCV
  • 栈
  • 回溯
  • SpringCore
  • 流媒体
  • rtmp
  • 面向对象
  • Vue
  • ElementUI
  • 软件工程
  • 异步
  • 自定义UI
  • ORM框架
  • 模块化
  • 交互式
  • Jsoup
  • Http Client
  • LRUCache
  • RabbitMQ
  • 消息通信
  • 服务解耦
  • 负载均衡
  • 权限
  • 多线程
  • 单例模式
  • Protobuf
  • 序列化
  • Python
  • m3u8
  • 堆
  • 二叉树
  • 自定义View
  • 观察者模式
  • 设计模式
  • 线程池
  • 动态扩容
  • 高可用
  • GC
  • ffmpeg
  • SpringMVC
  • REST
  • Redis
  • 缓存中间件
  • UML
  • Maven
  • Netty
  • 高性能网络
  • IPC通信
  • IO
  • Stream
  • 发布订阅
  • SQLite
  • Hash
  • 集合框架
  • 链表
  • Lambda
  • 汇编语言
  • 组件化
  • Router
  • 开发工具

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia-plus根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 思维导图
  • PDF工具
  • 无损放大
  • 代码转图
  • HTTPS证书