自定义Java注解的方式与应用
注解的作用
Annotation(注解)是JDK 5.0引入的特性,它的基本作用就是修饰编程元素。
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。编译器、开发工具或其他程序可以用反射来了解该类,有什么样的标记,就去干相应的事。
注解的语法比较简单,除了@符号的使用之外,与Java固有语法一致。JDK 5.0内置了三种标准注解:
(1)@Override,表示当前的方法定义将覆盖父类中的方法。
(2)@Deprecated,表示当前元素不推荐使用,是被弃用的代码。
(3)@SuppressWarnings,忽略编译器发出的警告信息。
注解有以下几个作用:
(1)生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see 、@param 、@return 等
(2)跟踪代码依赖性,替代配置文件的功能。比如Spring从 2.5版本 开始支持基于注解的配置。现在的框架基本都使用了注解来减少配置文件的数量。
(3)在编译时进行格式检查。如@override 放在方法前,如果这个方法并不是覆盖了父类方法,则编译时就能检查出来。
元注解
Java 5.0提供了四种元注解——负责注解其他注解。
@Target
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围,即该注解可以用在什么地方。
取值(ElementType枚举类)有:
(1)CONSTRUCTOR:用于描述构造器
(2)FIELD:用于描述变量
(3)LOCAL_VARIABLE:用于描述局部变量
(4)METHOD:用于描述方法
(5)PACKAGE:用于描述包
(6)PARAMETER:用于描述参数
(7)TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention
@Retention定义了该注解被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用@Retention可以限制注解的“生命周期”。
作用:用于描述注解的生命周期,即注解在什么范围内有效。
取值(RetentionPoicy枚举类)有:
(1)SOURCE:在源文件中有效
(2)CLASS:在class文件中有效
(3)RUNTIME:在运行时有效
@Documented
@Documented是一种标记性注解,被其标记的注解会被包含在javadoc文档中。
@Inherited
@Inherited 也是一种标记性注解,被其标记的注解会允许子类继承。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则该注解将被用于该class的子类。
需要注意的是,被@Inheriten标记的注解只会被class的子类所继承,类并不从它所实现的接口继承annotation,方法也不从它所重载的方法继承annotation。
如果我们使用java.lang.reflect去查询一个@Inherited类型的annotation时,反射代码将检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
自定义注解
自定义注解的格式为:
public @interface 注解名 {定义体}
例如:
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value={ElementType.FIELD}) @Retention(value=RetentionPolicy.RUNTIME) @Documented public @interface NotNull { int code() default -1; String message() default ""; }@interface用来声明一个注解,自动继承了java.lang.annotation.Annotation接口,在自定义注解时,不能继承其他的注解或接口。
定义体中的每一个方法实际上是声明了一个配置参数,方法只能用public或默认(default)两种访问权修饰,方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
注解参数支持数据类型:
(1)所有基本数据类型(byte,int,short.long,double,float,boolean,char)
(2)String类型
(3) Class类型
(4) Enum类型
(5) Annotation类型
(6) 以上所有类型的数组
注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法。这个约束使得处理器很难判断一个元素是否缺失,因为每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法。
如果成员名称是value,在赋值过程中可以简写;如果成员类型为数组,但是只赋值一个元素,则也可以简写。上面的例子可以简写为:
import java.lang.annotation.Documented; 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) @Documented public @interface NotNull { int code() default -1; String message() default ""; }上例自定义了名为@NotNull的注解,作用对象为field,其目的是对类变量进行非空检验。可是,到底怎么怎么利用它来进行检验呢?
使用注解最主要的部分在于对注解的处理,那么就会涉及到注解处理器。从原理上讲,注解处理器就是通过反射机制获取被检查字段上的注解信息,然后根据注解元素的值进行特定的处理。
比如,在某网站注册用户,用户输入的信息会被封装成一个User对象:
public class User { @NotNull(message="用户昵称不能为空") private String userName; @NotNull(message="密码不能为空") private String password; private Integer age; public StringgetUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public StringgetPassword() { return password; } public void setPassword(String password) { this.password = password; } public IntegergetAge() { return age; } public void setAge(Integer age) { this.age = age; } }要求用户昵称与密码不能为空,因此userName与password字段被@NotNull注解。
后台程序接收到User对象后,可以通过反射机制探知哪些字段被@NotNull注解了,如果全部满足要求再进行下一步业务逻辑;反之,返回错误页面。
import java.lang.reflect.Field; public class UserTest { public static booleantestNotNull(User user) throws IllegalArgumentException,IllegalAccessException{ Class<?> clazz = user.getClass(); //获取User的Class对象 Field [] userFields = clazz.getDeclaredFields(); //获取User的所有Field对象 for(Field field : userFields){ field.setAccessible(true); //User中的Field都是private的,所以要先setAccessible NotNull notNullAnnotation = field.getAnnotation(NotNull.class); //获取Field的NotNull注解对象 if(notNullAnnotation != null){ //如果该Field被NotNull注解了,那么字段不能为空 if(field.get(user) == null){ System.out.println(notNullAnnotation.message()); return false; } } } return true; } public static void main(String [] args) throws IllegalArgumentException,IllegalAccessException{ User user = new User(); user.setUserName("chenlongfei"); testNotNull(user); } }由于只填写了昵称而没填密码,不能通过验证,控制台打印出:
密码不能为空
该信息正是在@NotNull注解password字段时由message定义的。
自定义Java注解的方式与应用的更多相关文章
- 自定义Java注解(annotation)
https://www.imooc.com/learn/456 笔记 Java从1.5开始引进注解. 首先解决一个问题,为什么要学习Java注解? 1.看懂别人写的代码,尤其是框架的代码 2.可以是 ...
- 分享:自定义JAVA注解
元注解 元注解指用来定义注解的注解,例如:@Retention @Target Inherited @Documented等等.最为重要和经常使用的是@Retention @Target. @Rete ...
- 深入理解Java:自定义java注解
要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5. ...
- java web学习总结(二十一) -------------------模拟Servlet3.0使用注解的方式配置Servlet
一.Servlet的传统配置方式 在JavaWeb开发中, 每次编写一个Servlet都需要在web.xml文件中进行配置,如下所示: 1 <servlet> 2 <servlet- ...
- Java注解教程:自定义注解示例,利用反射进行解析
Java注解能够提供代码的相关信息,同时对于所注解的代码结构又没有直接影响.在这篇教程中,我们将学习Java注解,如何编写自定义注解,注解的使用,以及如何使用反射解析注解. 注解是Java 1.5引入 ...
- java自定义注解知识实例及SSH框架下,拦截器中无法获得java注解属性值的问题
一.java自定义注解相关知识 注解这东西是java语言本身就带有的功能特点,于struts,hibernate,spring这三个框架无关.使用得当特别方便.基于注解的xml文件配置方式也受到人们的 ...
- java注解的自定义和使用
小伙伴们.今天我们来说说注解.标志@ .针对java不同版本来说,注解的出现是在jdk1.5 但是在jdk1.5版本使用注解必须继续类的方法的重写,不能用于实现的接口中的方法实现,在jdk1.6环境下 ...
- java注解自定义使用
Java提供了4种注解,专门负责新注解的创建: @Target: 表示该注解可以用于什么地方,可能的ElementType参数有:CONSTRUCTOR:构造器的声明FIELD:域声明(包括enum实 ...
- [转]Java中实现自定义的注解处理器
Java中实现自定义的注解处理器(Annotation Processor) 置顶2016年07月25日 19:42:49 阅读数:9877 在之前的<简单实现ButterKnife的注解功能& ...
随机推荐
- laravel Passport - 创建 REST API 用户认证以及Dingo/Api v2.0+Passport实现api认证
第一部分: 安装passport 使⽤ Composer 依赖包管理器安装 Passport : composer require laravel/passport 接下来,将 Passport 的服 ...
- mac版chrome升级到Version 65.0.3325.18后无法打开百度bing搜狗
mac版本chrome升级到Version 65.0.3325.18后发现突然无法访问百度,搜狗,bing,神马等一系列的国内搜索引擎网站.连百度的儿子们比如知道,百度百科都无法访问. 1.首先想到的 ...
- js 的作用域 域解析 分析
作用域链 函数每次执行时,浏览器都会在函数中开启一个地方用来存储函数内的局部数据.(声明在函数内的局部变量),这个地方就叫做作用域([scopes]) 作用域链 变量与函数的查找规则:当我们在 ...
- SpringMVC 教程 - Controller
原文地址:https://www.codemore.top/cates/Backend/post/2018-04-10/spring-mvc-controller 声明Controller Contr ...
- [BJOI 2011]元素
Description 相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔法矿石炼制法杖的技术.那时人们就认识到,一个法杖的法力取决于使用的矿石.一般地,矿石越多则法力越强 ...
- [ZJOI2008]瞭望塔
题目描述 致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安. 我们将H村抽象为一维的轮廓.如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), ...
- 【BZOJ1500】【Noi2005】维修数列
题意原题很清楚了. 解题思路:裸的平衡树操作,注意动态开点即可. 细节还是比较多的,具体参见代码吧... #include <stdio.h> #include <algorithm ...
- 【Aho-Corasick automation 大米饼模板】
自动机要打熟.自动机要打好.自动机要打得美妙. [例子:HDU2222] #include<stdio.h> #include<queue> #include<cstri ...
- UESTC 618 无平方因子数 ( 莫比乌斯)
UESTC 618 题意:求1到n中无平方因子数的个数 Sample Input 3 1 10 30 Sample Output 1 7 19 思路:与前面的BZOJ 2440相似 #inc ...
- c++读写MySQL
看过很多C或是C++操作MySQL数据库的文章,大部分太吃力了,甚至有一部分根本没有很好的组织文字,初学者比较难以接受,即使是C++或是C高手也是比较难看懂.写这篇文章的目的不是别的,就一个,告诉您用 ...