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这个接口,并且不能再去继承别的类或者接口
  • 参数成员只能用public或default(默认)访问权限符修饰
  • 参数成员只能用八大基本数据类型、String、Enum、Class、annotations等数据类型,以及这些类型的数组
  • 要获取类方法和字段的注解信息,必须通过java反射机制来获取
  • 注解也可以没有定义成员(只起到标识作用)

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

1.自定义注解@KingwanTable、KingwanColumn

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

  • @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

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

以下代码通过获取student的class对象,获取类上的注解@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是不够的,我们还需要考虑其他情况(Integer、Date),限于篇幅原因,这里不做过多介绍,大家完全可以自行补充,如果想了解我的实现思路,移步:案例源码地址

这样,是不是就达到了我们要的效果了,对于任意简单实体类,我们都可以通过加上该注解实现一个简单的查找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. Python3基础——函数

    ython 函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可 ...

  2. centos7卸载mariadb安装mysql

    卸载mariadb 1. 当前安装列表  rpm -qa | grep mariadb 2.卸载 rpm -e --nodeps mariadb-libs-5.5.56-2.el7.x86_64 3 ...

  3. P4821 [中山市选]生成树

    题目链接 我们可以看一下题目中给的这张图. 首先,树是没有环的,所以我们要把所有的环上的边都删去一条. 我们可以现在每个五边形上删去一条边. 但删完之后我们会发现,里面还有一圈. 这时候,我们就要在这 ...

  4. MySQL基础 :基本知识点大纲

  5. 【git】关联本地仓库与远程仓库

    1.在远程建立一个空项目[项目名称]2.git init3.git remote add origin [git 地址]4.git pull origin master5.git push origi ...

  6. dgraph 使用简介

    dgraph 简介 dgraph 使用示例(基于 golang) golang client 安装 创建 schema 数据的 CURD 事务 总结 dgraph 简介 dgraph 是基于 gola ...

  7. 实验五 css进阶应用

    实验五 css进阶应用 实验目的: 掌握CSS在列表中的应用,能利用CSS将列表做成精美的导航栏: 掌握CSS在表单元素中的应用: 掌握SPRY菜单的制作方法和CSS代码修改. 实验内容: 1. 制作 ...

  8. nullptr解决了什么问题

    从0到NULL 在C++的世界中字面值0用来表示空指针,所以0可以当作所有指针类型的字面值.为了让语义更明确引入了NULL宏定义: #undef NULL #ifdef __cplusplus #de ...

  9. 【源码项目】C语言编程之火车票管理系统!(最强代码)

    大学计算机软件技术基础课程设计任务书 一.题目: 火车票信息管理系统: 二.目的与要求 : ● 目的培养学生综合利用C++语言进行程序设计的能力, ● 培养学生的编程能力.用计算机解决实际问题的能力, ...

  10. Go 安装配置golint

    原文链接:http://zhoubotong.site/post/3.html一. Golint介绍 Golint 是一个源码检测工具用于检测代码规范 Golint 不同于gofmt, Gofmt用于 ...