Java:注解(元数据)
元数据
所谓的元数据是指用来描述数据的数据,可能刚听到元数据的时候你会有点陌生,其实任何一个使用过 struts 或者 hibernate 的开发人员都在不知不觉中使用元数据。
更通俗一点来说,元数据是指描述代码间关系或者代码与其它资源(例如数据库表)之间内在联系的数据,对 Struts 来说是 struts-config.xml 文件,对 hibernate 来说是.hbm 文件。
以 xml 或其它方式存在的元数据文件都有一些不便之处(如:与被描述的文件分离,不利于维护);基于元数据的广泛应用,JDK1.5 引入了注解(Annotation)的概念来描述元数据,为我们提供了一种在代码中添加信息的方法,使我们可以在运行时或某个时刻方便地使用这些数据(通过解析注解来使用这些数据)
初识注解
1、是一种特殊的接口 —— 使用 @interface 声明
2、默认不会被继承 —— 除了被 @Inherited 修饰的注解【示例】
特别说明:
A、若某个注解被 @Inherited 修饰,则该注解修饰的类可以继承到子类中
B、只有注解目标为 class 时起作用, 其他元素则不生效 (如:interface、field、method、constructors)
示例:
对应解释:
注解的作用
A、编写文档:通过代码里标识的元数据生成文档,常用的有 @param、@return 等;。
B、代码分析:通过代码里标识的元数据对代码进行分析。
(1)替换.properties 和 xml 配置文件:注解可以作为软件部署的一部分配置信息,提供了一种简单和易于理解的方式。然而,当配置信息需要在运行时动态改变时,注解就不适合了。比较常见的是从 spring 2.5 开始的基于注解的配置,其作用就是减少配置文件;现在的框架基本都使用了这种配置来减少配置文件的数量。
(2)支持横切关注点:注解能够很好的处理依赖注入、服务发现管理对象、验证和许多其他类似的事情。如果需要用到面向方面编程,而你不想另外使用一种面向方面的语言(如 AspectJ),这时注解是一个可行的选择。
C、编译检查:通过代码里标识的元数据让编译器实现基本的编译检查。
Java 内置的注解集
注解可以用于类、方法、变量、参数和包等程序元素,Java 定义了一个内置的注解集:
(1)用于 Java 代码的注解:
@Override:校验方法是重写方法,如果方法在父类中未找到会产生一个编译警告。
@Deprecated:标记方法已经废弃不用了,如果还在使用此方法会产生一个编译警告。
@SuppressWarnings:告知编译器抑制由注解参数指定的编译时期警告。
(2)用于其它的注解:以下说明的元注解有一个共同的特点就是它们都只能用在 Annotation 的声明上
@Retention:用来声明注解的保留策略,即生命范围,有 CLASS、RUNTIME 和 SOURCE 这三种,分别表示注解保存在类文件、JVM 运行时和源代码中。只有当声明为 RUNTIME 的时候,才能够在运行时刻通过反射 API来获取到注解的信息;如果注解声明中不存在 Retention 注解,则保留策略默认为RetentionPolicy.CLASS。
@Target:指示注解所适用的程序元素的种类,即注解作用范围(如方法、字段等),如果注解声明中不存在 Target,则声明的注解可以用在任一程序元素上。
@Documented:指示某一类型的注解将通过 javadoc 和类似的默认工具进行文档化。
@Inherited:只能用于 Class 级别的 Annotation,用来说明被标记的 Annotation 会被该类的所有子类自动继承
自定义注解
Java 允许自定义注解,通过在类名前使用 @interface 来定义。包 java.lang.annotation 中包含所有定义自定义注解所需的元注解和接口,如接口 java.lang.annotation.Annotation 是所有注解继承的接口,且是自动继承,不需要定义时指定,类似于所有类都自动继承 Object。
示例:
package com.test;import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
public String name();
public int value() default 0;
}
解说:上例中的每一个方法实际上是声明了一个配置参数,参数的名称就是方法的名称,参数的类型就是方法的返回值类型(返回值类型只能是基本类型、Class、String、enum),可以通过 default 来声明参数的默认值。
细说 Java 注解
初步了解 Java 注解后,此处细说一下一些参数
(1)@Target 表示注解的作用范围,其可选的参数值在枚举类 ElemenetType 中,包括:
(2)@Retention 表示在什么级别保存注解信息,其可选的参数值在枚举类型 RetentionPolicy 中,包括:
注解处理器
在程序中添加的注解,可以在编译时或是运行时通过注解处理器来进行处理,注解处理器的本质是通过 Java 反射来处理。
应用:自定义一个注解,使用 Java 反射来解析注解
网上看到一个比较酷的案例,现展示如下:
注解的定义:
package com.annotation.test;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;/**
联系方式校验
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface ContactValidator {public ContactType type();
}
联系方式枚举类:
package com.annotation.test; /** * 联系方式类型 */ public enum ContactType {EMAIL,PHONE,MOBILE,WEBSITE}
用户 User:
package com.annotation.test;public class User {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 姓名 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String name; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 邮箱 </span><span style="color: rgba(0, 128, 0, 1)">*/<br></span><span style="background-color: rgba(0, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"> @ContactValidator(type </span>=<span style="color: rgba(0, 0, 0, 1)"> ContactType.EMAIL)<br></span></span><span style="color: rgba(0, 0, 255, 1)"> private</span><span style="color: rgba(0, 0, 0, 1)"> String email; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 电话 </span><span style="color: rgba(0, 128, 0, 1)">*/<br></span><span style="background-color: rgba(0, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"> @ContactValidator(type </span>=<span style="color: rgba(0, 0, 0, 1)"> ContactType.PHONE)<br></span></span><span style="color: rgba(0, 0, 255, 1)"> private</span><span style="color: rgba(0, 0, 0, 1)"> String phone; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 手机号 </span><span style="color: rgba(0, 128, 0, 1)">*/<br></span><span style="background-color: rgba(0, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"> @ContactValidator(type </span>=<span style="color: rgba(0, 0, 0, 1)"> ContactType.MOBILE)<br></span></span><span style="color: rgba(0, 0, 255, 1)"> private</span><span style="color: rgba(0, 0, 0, 1)"> String mobile; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 网址 </span><span style="color: rgba(0, 128, 0, 1)">*/<br></span><span style="background-color: rgba(0, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"> @ContactValidator(type </span>=<span style="color: rgba(0, 0, 0, 1)"> ContactType.WEBSITE)<br></span></span><span style="color: rgba(0, 0, 255, 1)"> private</span><span style="color: rgba(0, 0, 0, 1)"> String website; </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getName() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> name; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setName(String name) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.name =<span style="color: rgba(0, 0, 0, 1)"> name; } </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getEmail() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> email; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setEmail(String email) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.email =<span style="color: rgba(0, 0, 0, 1)"> email; } </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getPhone() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> phone; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setPhone(String phone) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.phone =<span style="color: rgba(0, 0, 0, 1)"> phone; } </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getMobile() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mobile; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setMobile(String mobile) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.mobile =<span style="color: rgba(0, 0, 0, 1)"> mobile; } </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getWebsite() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> website; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setWebsite(String website) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.website =<span style="color: rgba(0, 0, 0, 1)"> website; }
}
校验工具类:
package com.annotation.test;import java.util.regex.Matcher;
import java.util.regex.Pattern;public class ValidatorUtil {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 校验邮箱 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> email * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> isValidEmail(String email) { Pattern p </span>=Pattern.compile(".+@.+\\.[a-z]+"<span style="color: rgba(0, 0, 0, 1)">); Matcher m </span>=<span style="color: rgba(0, 0, 0, 1)">p.matcher(email); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> m.matches(); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 校验电话 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> phone * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> isValidPhone(String phone) { Pattern p </span>=Pattern.compile("\\d\\d([,\\s])?\\d\\d\\d\\d([,\\s])?\\d\\d\\d\\d"<span style="color: rgba(0, 0, 0, 1)">); Matcher m </span>=<span style="color: rgba(0, 0, 0, 1)">p.matcher(phone); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> m.matches(); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 校验手机号 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> mobile * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> isValidMobile(String mobile) { Pattern p </span>=Pattern.compile("^[1]([3][0-9]{1}|59|58|88|89)[0-9]{8}$"<span style="color: rgba(0, 0, 0, 1)">); Matcher m </span>=<span style="color: rgba(0, 0, 0, 1)">p.matcher(mobile); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> m.matches(); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 校验网址 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> website * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> isValidWebsite(String website){ Pattern p </span>=Pattern.compile("^(https?|ftp|file)://.+$"<span style="color: rgba(0, 0, 0, 1)">); Matcher m </span>=<span style="color: rgba(0, 0, 0, 1)">p.matcher(website); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> m.matches(); }
}
解析器:
package com.annotation.test;import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;public class FieldAnnotationParser {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 解析器 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> user * </span><span style="color: rgba(128, 128, 128, 1)">@throws</span><span style="color: rgba(0, 128, 0, 1)"> IllegalArgumentException * </span><span style="color: rgba(128, 128, 128, 1)">@throws</span><span style="color: rgba(0, 128, 0, 1)"> IllegalAccessException </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> parser(User user)<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> IllegalArgumentException, IllegalAccessException { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取所有字段 </span> Field[]fields =<span style="color: rgba(0, 0, 0, 1)"> user.getClass().getDeclaredFields(); </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)">(Field field : fields){ Annotation[] annotations </span>= field.getAnnotations();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取字段上的所有注解</span> <span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)">(Annotation annotation : annotations){ </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果是ContactValidator注解 </span> <span style="color: rgba(0, 0, 255, 1)">if</span>(annotation <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> ContactValidator){ ContactValidator contactValidator </span>=<span style="color: rgba(0, 0, 0, 1)"> (ContactValidator) annotation; </span><span style="color: rgba(0, 0, 255, 1)">if</span>(field.getModifiers() == Modifier.PRIVATE){<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果是私有字段,设置反射的对象在使用时取消Java语言访问检查 </span> field.setAccessible(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> result =<span style="color: rgba(0, 0, 255, 1)">false</span>;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">标识变量 </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取字段值 </span> String fieldValue =<span style="color: rgba(0, 0, 0, 1)"> (String) field.get(user); </span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (contactValidator.type()) { </span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> EMAIL: result </span>=<span style="color: rgba(0, 0, 0, 1)">ValidatorUtil.isValidEmail(fieldValue); </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> PHONE: result </span>=<span style="color: rgba(0, 0, 0, 1)">ValidatorUtil.isValidPhone(fieldValue); </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> MOBILE: result </span>=<span style="color: rgba(0, 0, 0, 1)">ValidatorUtil.isValidMobile(fieldValue); </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> WEBSITE: result </span>=<span style="color: rgba(0, 0, 0, 1)">ValidatorUtil.isValidWebsite(fieldValue); </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">result){ System.out.println(</span>"Invalid " + field.getName() + ": " +<span style="color: rgba(0, 0, 0, 1)">fieldValue); } } } } }
}
测试类:
package com.annotation.test;public class AnnotationTest {
/**
主函数
* @param args
* @throws IllegalAccessException
* @throws IllegalArgumentException
/
public static void main(String[] args)throws IllegalArgumentException,IllegalAccessException {FieldAnnotationParser.parser(user); }User user </span>=<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> User(); user.setName(</span>"TimSturt"<span style="color: rgba(0, 0, 0, 1)">); user.setPhone(</span>"0931234 3819"); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">错误的电话格式 </span> user.setMobile("13575309630"); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正确的手机格式</span> user.setEmail("test@gmail.com"); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正确邮箱格式 </span> user.setWebsite("fttp://test.com"); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">错误的网站url</span>
}
输出如下:
Invalid phone: 0931234 3819 Invalid website: fttp://test.com
解说:校验工具类中的正则表达式不适用于所有情况,可自行调整
结语:
在 Java 中元数据以标签的形式存在于 Java 代码中,元数据标签的存在并不影响程序代码的编译和执行,它只是被用来生成其它的文件或在运行时知道被运行代码的描述信息。
针对前述内容进行简述:
第一、元数据以标签的形式存在于 Java 代码中。
第二、元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的。
第三、元数据需要编译器之外的工具进行额外的处理用来生成其它的程序部件。
第四、元数据可以只存在于 Java 源代码级别,也可以存在于编译之后的 Class 文件内部。
要点总结:
(1)Annotation 定义语法为:
modifiers @interface AnnotationName { element declaration1 element declaration2 . . . }
modifiers 指:public,protected,private 或者默认值(什么也没有)。
(2)一个元素的声明(element declaration):
type elementName();
或者
type elementName() default value;
(3)可以通过如下方式来使用 Annotation:
@AnnotationName(elementName1=value1, elementName2=value2, . . .),元素声明的顺序没有关系,有默认值的元素可以不列在初始化表中,此时它们使用默认值。
A、根据上述描述,下述定义的三个 Annotation 是等价的:
@BugReport(assignedTo="Harry", severity=0)
@BugReport(severity=0, assignedTo="Harry")
@BugReport(assignedTo="Harry")
B、如果只有一个元素,最好将参数名称设为 value,赋值时不用指定名称而直接赋值,如下:
AnnotationName(“somevalue”)
C、如果 Annotation 的元素是数组,则可以做如下声明:
@BugReport(. . ., reportedBy={"Harry", "Carl"})
D、如果数组中只有一个元素时可以做如下声明:
@BugReport(. . ., reportedBy="Joe") // OK, same as {"Joe"}
E、如果 Annotation 元素类型为 Annotation 时可以做如下声明:
@BugReport(testCase=@TestCase(name="free"), . . .)
(4)Annotation 中元素的类型必须是下述类型或者这些类型的组合(下述类型构成的数组):
基本类型 (int, short, long, byte, char, double, float, boolean)、字符串(String)、类、枚举类型(enum)、Annotation 类型
参考资料推荐
http://mp.weixin.qq.com/s?__biz=MzA3ODY0MzEyMA==&mid=2657236022&idx=1&sn=e23ccaf9fa05619910e404f9c0f4e8e4&scene=0#wechat_redirect
https://howtodoinjava.com/java/annotations/complete-java-annotations-tutorial/#custom_annotations