Java注解(入门级)

Kingwan 2020-11-08 23:45:57
java 入门级 入门 注解


Java注解

前言

近日在阅读开源项目,发现项目里好多奇奇怪怪的注解(@DataScope@Log...)看得我一脸懵,不知道大家是否也有过这样的经历,回想了一下,发现自己对于注解的知识,好像只停留在@Override。。。异常尴尬,所以今天就补补注解这个知识,并把自己的收获记录在此,与大家一同交流,如有不对的地方,敬请指正!

希望本文能给读者带来以下收获:

  • 明白注解是什么,大概有什么用
  • 能理解别人代码里面注解的作用
  • 能使用自定义注解

什么是注解

想要了解某个知识点,我首先推荐的都是去官网查看,下面看看Java官方对注解的解释:

Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.

注解是元数据的一种形式,它提供有关程序的数据,但这些数据不是程序本身的一部分。注解对它们注释的代码的操作没有直接影响。

一堆英文读完,一阵云里雾里。没关系,这是正常操作,不过我们从翻译中还是可以了解到注解可以提供数据,并且数据是独立于程序的,那么我们大致可以推断出,注解其实是介于程序和数据之间的一种媒介,程序和数据通过注解达成了某种联系,即注解类似一根红线,把数据和程序关联在一起。

@Override开始

通过对Java官方提供的注解解释的翻译,我们筛选推断出了一个关键信息——关联。那到底如何理解这个词呢?别急,我们从最熟悉的陌生人@Override开始,最熟悉是因为我们知道这是方法重写,子类覆盖父类方法用到的注解,陌生是因为我们从来没有点进去了解过这个注解,那接下来就进去看看吧!

import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

短短的5行,好像除了第一行,其他啥都不知道。。。不急,我们一行一行来解读!

  • 注解导入了一个annotation
  • 注解的“套娃”行为@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)
  • 不同于接口和类的声明public @interface Override { }

除了对新注解不认识,我们大致可以了解到注解的定义格式,修饰符 @interface 注解名{}。(有点接口的感觉)

禁止套娃——元注解

通过对@Override的剖析,我们了解了注解的定义格式,不过我们发现注解里面又有新的注解,本着刨根问底的好奇心,我们继续进入@Target注解一探究竟!

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}

一直点击,发现始终在@Documented@Retention@Target这几个注解之间套娃,通过Java文档我们了解到原来这些修饰注解的注解叫做元注解。元注解(meta-annotation)在java.lang.annotation包下:

@Retention

表示如何存储被标记的注解(指定存储级别),有以下三个级别

  • RetentionPolicy.SOURCE:只保留到源码级别,在编译阶段会被忽略,所以他们不会被写入字节码。
  • RetentionPolicy.CLASS:(默认)编译级别,在编译时由编译器保留,但被Java虚拟机(JVM)忽略。
  • RetentionPolicy.RUNTIME:由JVM保留,可以在运行时环境使用。
@Target

表示被标记的注解可以用于哪种java元素(类、接口、属性、方法......),有以下八种

作用域 解释
ElementType.ANNOTATION_TYPE 可用于注解类型
ElementType.CONSTRUCTOR 可以用于构造函数
ElementType.FIELD 可以用于字段或者属性
ElementType.LOCAL_VARIABLE 可以用于局部变量
ElementType.METHOD 可以用于方法级注解
ElementType.PACKAGE 可以用于包声明
ElementType.PARAMETER 可以用于方法的参数
ElementType.TYPE 可以用于类的任何元素
@Documented

无论何时使用指定的注解,都应使用Javadoc工具记录这些元素。(即会在生成的javadoc中加入注解说明)

@Inherited

可以从超类继承注释类型,仅用于类的声明(接口不会继承)

@Repeatable

在Java SE 8中引入的,表示标记的注释可以多次应用于相同的声明或类型使用。

注解的分类

通过对元注解的了解,我明白了一个注解都是由这些元注解修饰而来,而且我们也收获了一个重要信息——注解可以修饰注解

这样无限的套娃,就会有各种各样的注解,那么到底有哪些注解呢?常见的注解大致分为以下四类:

元注解

即上文提及的5个元注解

jdk注解

常见的如@Override @Deprecated @SuppressWarnings @SafeVarargs @FunctionalInterface

第三方注解

即第三方框架提供的注解,例如自动注入依赖@Autowired@Controller

自定义注解

即开发人员根据项目需求自定义的注解,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。

实战——定义自己的注解

看过了Java提供的注解,相信你已经对注解有个大致的了解了。那你有没有想过,注解是如何化腐朽为神奇,加了一个简单的@Autowired就能实现依赖注入、@Setter就能实现set方法的生成,下面通过简单的实战来体会一下注解的神奇之处吧!

实战目标:

使用自定义注解,通过在实体类及其属性上加注解,实现对实体类查询sql语句的构造

ps:类似select * from t_user where t_name='kingwan'的形式

自定义注解的编写规则

在开始实战之前,我们先了解一下编写自定义注解的规则

  • 注解的定义为@interface,所有的注解会自动继承java.lang.Annotation这个接口,并且不能再去继承别的类或者接口
  • 参数成员只能用publicdefault(默认)访问权限符修饰
  • 参数成员只能用八大基本数据类型、StringEnumClassannotations等数据类型,以及这些类型的数组
  • 要获取类方法和字段的注解信息,必须通过java反射机制来获取
  • 注解也可以没有定义成员(只起到标识作用)

了解了注解的定义规范,接下来我们开始进入正式的实战环节。

1.自定义注解@KingwanTableKingwanColumn

对于实体类查询的sql语句,我们需要知道两个信息:①查询的表名②字段名。并且我们通常习惯将用户表t_user对应于实体类User,那么我们如何和把t_userUser进行关联呢?一想到关联,回顾我们最开始从官方文档中提取出来的信息,没错,就是使用注解关联。接下来定义两个自定义注解:

  • @KingwanTable:注解实体类对应的表名

    @Target(ElementType.TYPE)//作用在类/接口上
    @Retention(RetentionPolicy.RUNTIME)//保留作用域:保留到运行时
    public @interface KingwanTable {
    String value();//参数:表名
    }
    
  • @KingwanColumn:注解实体类属性对应的表字段名

    @Target(ElementType.FIELD)//表示作用在字段上
    @Retention(RetentionPolicy.RUNTIME)//保留到运行时
    public @interface KingwanColumn {
    String value();//参数:字段名
    }
    
2.实体类添加上自定义注解

有了自定义的两个注解,那么我们现在就可以把它们加在实体类上。

  • 以下代码定义了一个Student实体类,加上了@KingwanTable("t_student")映射表名,@KingwanColumn("stu_birth")映射字段名。

    @Data//简化实体类的set、get方法
    @KingwanTable("t_student")
    public class Student {
    @KingwanColumn("stu_name")
    private String stuName;
    @KingwanColumn("stu_age")
    private Integer stuAge;
    @KingwanColumn("stu_birth")
    private Date stuBirth;
    }
    
  • 以下代码创建了一个student对象,并初始化信息

    public static void main(String[] args) {
    Student student = new Student();
    //初始化信息
    init(student);
    }
    private static void init(Student student) {
    student.setStuName("kingwan");
    student.setStuAge(18);
    student.setStuBirth(new Date());
    }
    
3.反射获取注解信息

有了一个加了自定义注解的Student实体类,那么我们想要构造SQL,就有以下思路:

获取到注解的信息(获取表名、字段名)=>获取属性的值(字段值)=>构造SQL

如何获取呢?规则里说了,使用反射

以下代码通过获取studentclass对象,获取类上的注解@KingwanTable信息。

aClass.isAnnotationPresent:判断指定的注解是否存在

public static void main(String[] args) throws Exception {
StringBuffer sql = new StringBuffer("");//即将拼接的SQL语句
Student student = new Student();
//初始化信息
init(student);
//反射获取class类
Class<? extends Student> aClass = student.getClass();
//1. 判断实体类上是否存在注解@KingwanTable
boolean exist = aClass.isAnnotationPresent(KingwanTable.class);//传入我们自定义的注解类
String tableName = null;
if(exist){
//1.1 存在注解即获取注解值---(表名)
KingwanTable annotation = aClass.getAnnotation(KingwanTable.class);
tableName = annotation.value();
sql.append("select * from ").append(tableName).append(" where 1=1");//拼接SQL
}
System.out.println(sql);
}

此时SQL打印的结果:

获取到了类上的注解信息,接下来我们来看看如何获取属性上的注解信息

//2. 获取属性上的注解
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
//2.1 遍历每个属性上是否有KingwanColumn注解
KingwanColumn column = field.getAnnotation(KingwanColumn.class);
if( column != null){
//2.1.1 获取该属性的值
String fieldName = field.getName();//属性名
String methodName = "get"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);//构造getter方法
Method method = aClass.getMethod(methodName);
//通过反射代理调用get方法,获取属性的值(name='kingwan',age=18....)
Object invoke = method.invoke(student);
if(invoke instanceof String){
String value = (String) invoke;
//sql拼接
sql.append(" and ").append(column.value()).append("=").append("'")
.append(value).append("'");
}else{
//想想还有哪些情况...
}
}
System.out.println(sql);
}

此时SQL的结果:

当然,如果有小伙伴跟着本文敲,可能在这一步就走不下去了,这是因为我们的get方法返回的字段类型多种多样,所以仅仅invoke instanceof String是不够的,我们还需要考虑其他情况(IntegerDate),限于篇幅原因,这里不做过多介绍,大家完全可以自行补充,如果想了解我的实现思路,移步:案例源码地址

这样,是不是就达到了我们要的效果了,对于任意简单实体类,我们都可以通过加上该注解实现一个简单的查找SQL的生成

你学废了吗!

总结

注解有什么用

相信大家看我之后可能会有疑问,注解好复杂,费一大堆功夫,还不如直接点呢!的确,我最开始也觉得注解有点鸡肋,不过用久了之后,发现真香!而且注解的作用不仅仅这些,本文的目的是让大家对注解有一个简单的了解,当你看到别人写的注解是多么巧妙时,你也许就会发现,原来注解这么好用!

  • 在编译时进行格式检查。如@Override
  • 跟踪代码依赖性,实现替代配置文件功能。通过处理注解信息生成代码、XML文件。
  • 一些注释可以在运行时进行检查
结尾一张图

一张思维导图总结一下内容,保存下来,时常复习!

版权声明
本文为[Kingwan]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/zhangqingwang/p/13946505.html

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云