初识Java注解

  所谓的元数据是指用来描述数据的数据,可能刚听到元数据的时候你会有点陌生,其实任何一个使用过struts或者hibernate的开发人员都在不知不觉中使用元数据,更通俗一点来说元数据是指描述代码间关系或者代码与其它资源(例如数据库表)之间内在联系的数据,对Struts来说就是struts-config.xml文件,对hibernate来说就是.hbm文件。 但是现有的以xml或其它方式存在的元数据文件都有一些不便之处(如:与被描述的文件分离,不利于一致性维护)。

  基于元数据的广泛应用,JDK1.5引入了注解(Annotation)的概念来描述元数据,为我们提供了一种在代码中添加信息的方法,使我们可以在运行时或某个时刻方便地使用这些数据(通过解析注解来使用这些数据)

注解的作用

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。

示例:

  1. package com.test;
  2.  
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. import java.lang.annotation.ElementType;
  6. import java.lang.annotation.Retention;
  7.  
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Target(ElementType.TYPE)
  10. public @interface MyAnnotation {
  11. public String name();
  12. public int value() default 0;
  13. }

解说:上例中的每一个方法实际上是声明了一个配置参数,参数的名称就是方法的名称,参数的类型就是方法的返回值类型(返回值类型只能是基本类型、Class、String、enum),可以通过default来声明参数的默认值。

细说Java注解

初步了解Java注解后,此处细说一下一些参数

(1)@Target表示注解的作用范围,其可选的参数值在枚举类ElemenetType中,包括:

  ElemenetType.CONSTRUCTOR---------------------------构造器声明 
  ElemenetType.FIELD --------------------------------------字段声明(包括 enum 实例) 
  ElemenetType.LOCAL_VARIABLE--------------------------局部变量声明 
  ElemenetType.METHOD ----------------------------------方法声明 
  ElemenetType.PACKAGE --------------------------------- 包声明 
  ElemenetType.PARAMETER ------------------------------参数声明 
  ElemenetType.TYPE--------------------------------------- 类、接口(包括注解类型)或enum声明

(2)@Retention表示在什么级别保存注解信息,其可选的参数值在枚举类型RetentionPolicy中,包括:

  RetentionPolicy.SOURCE ---------------------------------注解将被编译器丢弃 
  RetentionPolicy.CLASS -----------------------------------注解在class文件中可用,但会被VM丢弃 
  RetentionPolicy.RUNTIME -------------------------------VM将在运行期保留注解,因此可以通过反射机制读取注解的信息。

注解处理器

在程序中添加的注解,可以在编译时或是运行时通过注解处理器来进行处理,注解处理器的本质是通过Java反射来处理。

应用:自定义一个注解,使用Java反射来解析注解

网上看到一个比较酷的案例,现展示如下:

注解的定义:

  1. package com.annotation.test;
  2.  
  3. import java.lang.annotation.Documented;
  4. import java.lang.annotation.ElementType;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8.  
  9. /**
  10. * 联系方式校验
  11. */
  12. @Retention(RetentionPolicy.RUNTIME)
  13. @Target(ElementType.FIELD)
  14. @Documented
  15. public @interface ContactValidator {
  16.  
  17. public ContactType type();
  18.  
  19. }

联系方式枚举类:

  1. package com.annotation.test;
  2. /**
  3. * 联系方式类型
  4. */
  5. public enum ContactType {
  6. EMAIL,PHONE,MOBILE,WEBSITE
  7. }

用户User:

  1. package com.annotation.test;
  2.  
  3. public class User {
  4.  
  5. /**
  6. * 姓名
  7. */
  8. private String name;
  9.  
  10. /**
  11. * 邮箱
  12. */
    @ContactValidator(type = ContactType.EMAIL)
    private String email;
  13.  
  14. /**
  15. * 电话
  16. */
    @ContactValidator(type = ContactType.PHONE)
    private String phone;
  17.  
  18. /**
  19. * 手机号
  20. */
    @ContactValidator(type = ContactType.MOBILE)
    private String mobile;
  21.  
  22. /**
  23. * 网址
  24. */
    @ContactValidator(type = ContactType.WEBSITE)
    private String website;
  25.  
  26. public String getName() {
  27. return name;
  28. }
  29.  
  30. public void setName(String name) {
  31. this.name = name;
  32. }
  33.  
  34. public String getEmail() {
  35. return email;
  36. }
  37.  
  38. public void setEmail(String email) {
  39. this.email = email;
  40. }
  41.  
  42. public String getPhone() {
  43. return phone;
  44. }
  45.  
  46. public void setPhone(String phone) {
  47. this.phone = phone;
  48. }
  49.  
  50. public String getMobile() {
  51. return mobile;
  52. }
  53.  
  54. public void setMobile(String mobile) {
  55. this.mobile = mobile;
  56. }
  57.  
  58. public String getWebsite() {
  59. return website;
  60. }
  61.  
  62. public void setWebsite(String website) {
  63. this.website = website;
  64. }
  65.  
  66. }

校验工具类:

  1. package com.annotation.test;
  2.  
  3. import java.util.regex.Matcher;
  4. import java.util.regex.Pattern;
  5.  
  6. public class ValidatorUtil {
  7.  
  8. /**
  9. * 校验邮箱
  10. *
  11. * @param email
  12. * @return
  13. */
  14. public static boolean isValidEmail(String email) {
  15.  
  16. Pattern p =Pattern.compile(".+@.+\\.[a-z]+");
  17. Matcher m =p.matcher(email);
  18.  
  19. return m.matches();
  20. }
  21.  
  22. /**
  23. * 校验电话
  24. *
  25. * @param phone
  26. * @return
  27. */
  28. public static boolean isValidPhone(String phone) {
  29.  
  30. Pattern p =Pattern.compile("\\d\\d([,\\s])?\\d\\d\\d\\d([,\\s])?\\d\\d\\d\\d");
  31. Matcher m =p.matcher(phone);
  32.  
  33. return m.matches();
  34. }
  35.  
  36. /**
  37. * 校验手机号
  38. *
  39. * @param mobile
  40. * @return
  41. */
  42. public static boolean isValidMobile(String mobile) {
  43.  
  44. Pattern p =Pattern.compile("^[1]([3][0-9]{1}|59|58|88|89)[0-9]{8}$");
  45. Matcher m =p.matcher(mobile);
  46.  
  47. return m.matches();
  48. }
  49.  
  50. /**
  51. * 校验网址
  52. *
  53. * @param website
  54. * @return
  55. */
  56. public static boolean isValidWebsite(String website){
  57.  
  58. Pattern p =Pattern.compile("^(https?|ftp|file)://.+$");
  59. Matcher m =p.matcher(website);
  60.  
  61. return m.matches();
  62. }
  63.  
  64. }

解析器:

  1. package com.annotation.test;
  2.  
  3. import java.lang.annotation.Annotation;
  4. import java.lang.reflect.Field;
  5. import java.lang.reflect.Modifier;
  6.  
  7. public class FieldAnnotationParser {
  8.  
  9. /**
  10. * 解析器
  11. * @param user
  12. * @throws IllegalArgumentException
  13. * @throws IllegalAccessException
  14. */
  15. public static void parser(User user)throws IllegalArgumentException, IllegalAccessException {
  16.  
  17. //获取所有字段
  18. Field[]fields = user.getClass().getDeclaredFields();
  19.  
  20. for(Field field : fields){
  21.  
  22. Annotation[] annotations = field.getAnnotations();//获取字段上的所有注解
  23.  
  24. for(Annotation annotation : annotations){
  25. //如果是ContactValidator注解
  26. if(annotation instanceof ContactValidator){
  27.  
  28. ContactValidator contactValidator = (ContactValidator) annotation;
  29.  
  30. if(field.getModifiers() == Modifier.PRIVATE){//如果是私有字段,设置反射的对象在使用时取消Java语言访问检查
  31. field.setAccessible(true);
  32. }
  33.  
  34. boolean result =false;//标识变量
  35. //获取字段值
  36. String fieldValue = (String) field.get(user);
  37.  
  38. switch (contactValidator.type()) {
  39. case EMAIL:
  40. result =ValidatorUtil.isValidEmail(fieldValue);
  41. break;
  42. case PHONE:
  43. result =ValidatorUtil.isValidPhone(fieldValue);
  44. break;
  45. case MOBILE:
  46. result =ValidatorUtil.isValidMobile(fieldValue);
  47. break;
  48. case WEBSITE:
  49. result =ValidatorUtil.isValidWebsite(fieldValue);
  50. break;
  51. }
  52.  
  53. if(!result){
  54. System.out.println("Invalid " + field.getName() + ": " +fieldValue);
  55. }
  56. }
  57. }
  58. }
  59. }
  60.  
  61. }

测试类:

  1. package com.annotation.test;
  2.  
  3. public class AnnotationTest {
  4. /**
  5. *主函数
  6. * @param args
  7. * @throws IllegalAccessException
  8. * @throws IllegalArgumentException
  9. */
  10. public static void main(String[] args)throws IllegalArgumentException,IllegalAccessException {
  11.  
  12. User user =new User();
  13. user.setName("TimSturt");
  14. user.setPhone("0931234 3819"); //错误的电话格式
  15. user.setMobile("13575309630"); //正确的手机格式
  16. user.setEmail("test@gmail.com"); //正确邮箱格式
  17. user.setWebsite("fttp://test.com"); //错误的网站url
  18.  
  19. FieldAnnotationParser.parser(user);
  20. }
  21.  
  22. }

输出如下:

  1. Invalid phone: 0931234 3819
  2. Invalid website: fttp://test.com

解说:校验工具类中的正则表达式不适用于所有情况,可自行调整

结语:

在Java中元数据以标签的形式存在于Java代码中,元数据标签的存在并不影响程序代码的编译和执行,它只是被用来生成其它的文件或在运行时知道被运行代码的描述信息。

针对前述内容进行简述:

第一、元数据以标签的形式存在于Java代码中。
第二、元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的。
第三、元数据需要编译器之外的工具进行额外的处理用来生成其它的程序部件。
第四、元数据可以只存在于Java源代码级别,也可以存在于编译之后的Class文件内部。

要点总结:

(1)Annotation定义语法为:

  1. modifiers @interface AnnotationName
  2. {
  3. element declaration1
  4. element declaration2
  5. . . .
  6. }

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

Java:注解(元数据)的更多相关文章

  1. Java注解-元数据、注解分类、内置注解和自定义注解|乐字节

    大家好,我是乐字节的小乐,上次说过了Java多态的6大特性|乐字节,接下来我们来看看Java编程里的注解. Java注解有以下几个知识点: 元数据 注解的分类 内置注解 自定义注解 注解处理器 Ser ...

  2. Java注解-注解处理器、servlet3.0|乐字节

    大家好,我是乐字节的小乐,上次给大家带来了Java注解-元数据.注解分类.内置注解和自定义注解|乐字节,这次接着往下讲注解处理器和servlet3.0 一.注解处理器 使用注解的过程中,很重要的一部分 ...

  3. Java注解

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

  4. 框架基础——全面解析Java注解

    为什么学习注解? 学习注解有什么好处? 学完能做什么? 答:1. 能够读懂别人写的代码,特别是框架相关的代码: 2. 让编程更加简洁,代码更加清晰: 3. 让别人高看一眼. spring.mybati ...

  5. Java注解教程:自定义注解示例,利用反射进行解析

    Java注解能够提供代码的相关信息,同时对于所注解的代码结构又没有直接影响.在这篇教程中,我们将学习Java注解,如何编写自定义注解,注解的使用,以及如何使用反射解析注解. 注解是Java 1.5引入 ...

  6. Java注解教程及自定义注解

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  7. Java注解实践

    Java注解实践 标签 : Java基础 注解对代码的语意没有直接影响, 他们只负责提供信息给相关的程序使用. 注解永远不会改变被注解代码的含义, 但可以通过工具对被注解的代码进行特殊处理. JDK ...

  8. Java注解知识点摘抄

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  9. java注解(基础)

    一.认识注解 1.注解的定义: java提供了一种原程序中的元素关联任何信息和元数据的途径和方法. 2.学习注解的目的: (1)能够读懂别人写的代码,特别是框架相关的代码(框架中使用注解是非常方便的) ...

  10. 使用Java注解来简化你的代码

         注解(Annotation)就是一种标签,可以插入到源代码中,我们的编译器可以对他们进行逻辑判断,或者我们可以自己写一个工具方法来读取我们源代码中的注解信息,从而实现某种操作.需要申明一点, ...

随机推荐

  1. Tomcat:基于Apache+Tomcat的集群搭建

    根据Tomcat的官方文档说明可以知道,使用Tomcat配置集群需要与其它Web Server配合使用才可以完成,典型的有Apache和IIS. 这里就使用Apache+Tomcat方式来完成基于To ...

  2. Linux多线程同步方式

    当多个线程共享相同的内存时,需要确保每个线程看到一致的数据视图,当多个线程同时去修改这片内存时,就可能出现偏差,得到与预期不符合的值.为啥需要同步,一件事情逻辑上一定是有序的,即使在并发环境下:而操作 ...

  3. linux下mysql开启远程访问权限及防火墙开放3306端口

    默认mysql的用户是没有远程访问的权限的,因此当程序跟数据库不在同一台服务器上时,我们需要开启mysql的远程访问权限. 主流的有两种方法,改表法和授权法. 相对而言,改表法比较容易一点,个人也是比 ...

  4. Linux IPC POSIX 共享内存

    模型 #include <unistd.h> //for fstat() #include <sys/types.h> //for fstat() #include <s ...

  5. 删除myeclipse下svn用户名和密码

    在不同的操作系统下,操作基本类似. 以win7为例 1.进入c:/Users/[你的用户名]/AppData/Roaming/Subversion/auth目录,删除该目录下的所有文件: 2.重启ec ...

  6. PlaceHolder的两种实现方式

    placeholder属性是HTML5 中为input添加的.在input上提供一个占位符,文字形式展示输入字段预期值的提示信息(hint),该字段会在输入为空时显示. 如 <input typ ...

  7. 使用Tcmalloc进行堆栈分析

    在前一篇译文<使用TCmalloc的堆栈检查>,介绍了Tcmalloc进行堆栈检查,今天翻译<heap-profiling using tcmalloc>,了解如何 TCmal ...

  8. 基于JAVA的全国天气预报接口调用示例

    step1:选择本文所示例的接口"全国天气预报接口" url:https://www.juhe.cn/docs/api/id/39/aid/87step2:每个接口都需要传入一个参 ...

  9. [转]C#面试题

    本文转自http://www.cnblogs.com/lhws/archive/2010/09/21/1827115.html 1.维护数据库的完整性.一致性.你喜欢用触发器还是自写业务逻辑?为什么? ...

  10. 又是一周-AJAX(三)

    hi 我又食言了,但我还是厚颜无耻的回来了... 1.AJAX(三) 三.AJAX的简单的例子 3.1 简介 完成:查询员工信息,通过输入员工编号查询员工的基本信息+新建员工的信息,包含员工姓名,编号 ...