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文件。
  • 一些注释可以在运行时进行检查
结尾一张图

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

Java注解(入门级)的更多相关文章

  1. java注解基础入门

    前言 这篇博客主要是对java注解相关的知识进行入门级的讲解,包括**,核心内容主要体现在对java注解的理解以及如何使用.希望通过写这篇博客的过程中让自己对java注解有更深入的理解,在工作中可以巧 ...

  2. Java注解

    Java注解其实是代码里的特殊标记,使用其他工具可以对其进行处理.注解是一种元数据,起到了描述.配置的作用,生成文档,所有的注解都隐式地扩展自java.lang.annotation.Annotati ...

  3. 19.Java 注解

    19.Java注解 1.Java内置注解----注解代码 @Deprecated                                    //不推荐使用的过时方法 @Deprecated ...

  4. Java注解入门

    注解的分类   按运行机制分:   源码注解:只在源码中存在,编译后不存在 编译时注解:源码和编译后的class文件都存在(如@Override,@Deprecated,@SuppressWarnin ...

  5. java注解(Annotation)解析

    注解(Annotation)在java中应用非常广泛.它既能帮助我们在编码中减少错误,(比如最常见的Override注解),还可以帮助我们减少各种xml文件的配置,比如定义AOP切面用@AspectJ ...

  6. JAVA 注解的几大作用及使用方法详解

    JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...

  7. attilax.java 注解的本质and 使用最佳实践(3)O7

    attilax.java 注解的本质and 使用最佳实践(3)O7 1. 定义pojo 1 2. 建立注解By eclipse tps 1 3. 注解参数的可支持数据类型: 2 4. 注解处理器 2 ...

  8. paip.java 注解的详细使用代码

    paip.java 注解的详细使用代码 作者Attilax 艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/att ...

  9. JAVA 注解的几大作用及使用方法详解【转】

    java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解 ...

随机推荐

  1. spring-boot-route(七)整合jdbcTemplate操作数据库

    在一部分内容中,我们学习了Restful接口的编写,及接口文档的生成.我们需要将接口数据进行持久化存储,这一部分我们主要学习几种持久化框架将数据进行存储.本部分内容中,我们都将使用mysql为例来做为 ...

  2. HTML中css水平居中的几种方式

    1. 子元素为行内元素时,父元素使用 text-align: center; 实现子元素的水平居中: 2. 子元素为块级元素时, 2.1. 将子元素设置 margin: 0 auto; 实现居中: 2 ...

  3. VS 高级版本新建的项目如何降级使低版本 VS 可以打开

    转载:https://blog.csdn.net/u012814856/article/details/70325267 一.引言 这里因为工作的原因,公司项目使用的是 VS2015 的编译环境,但是 ...

  4. Java 读取文件中的每一行,并为每一行插入特定的字符串

    工具 1:Eclipse Java EE IDE for Web Developers. Version: Photon Release (4.8.0). Build id: 20180619-120 ...

  5. 【题解】一本通例题 S-Nim

    \(\color{purple}{Link}\) \(\text{Solution:}\) 这个题就是给\(Nim\)游戏做了一个限制. 考虑一下\(\text{SG}\)函数:给定的局面下对应的\( ...

  6. 优质分享 | Spring Boot 入门到放弃!!!

    持续原创输出,点击上方蓝字关注我 目录 前言 视频目录 如何获取? 总结 前言 最近不知不觉写Spring Boot专栏已经写了九篇文章了,从最底层的项目搭建到源码解析以及高级整合的部分,作者一直在精 ...

  7. 状压DP——【蜀传之单刀赴会】

    某王   老师今天考了一套三国题,AK了...就挑一道最恶心的题来写一写吧. 题目描述: [题目背景] 公元215年,刘备取益州,孙权令诸葛瑾找刘备索要荆州.刘备不答应,孙权极为恼恨,便派吕蒙率军取长 ...

  8. day12 Pyhton学习

    一.昨日内容回顾 1.函数名 函数名是一个变量名 可以作为集合类的元素 可以作为参数进行传递 def  func(fn): fn() 可以作为返回值返回 def outer(): def inner( ...

  9. centos8上安装phpmyadmin5

    一,下载phpmyadmin5: 1,官网地址: https://www.phpmyadmin.net/ 2,下载 [root@yjweb source]# wget https://files.ph ...

  10. C++ Primer第5版 第一章课后练习

    练习1.9 #include <iostream> int main() { int sum = 0, val = 50; while (val <= 100) { sum += v ...