Java:注解(元数据)

初识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。

示例:

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中,包括:

  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反射来解析注解

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

注解的定义:

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 {  

    /**
     * 姓名
     */
    private String name;  

    /**
     * 邮箱
     */@ContactValidator(type = ContactType.EMAIL)    private String email;  

    /**
     * 电话
     */@ContactValidator(type = ContactType.PHONE)    private String phone;  

     /**
      * 手机号
      */@ContactValidator(type = ContactType.MOBILE)     private String mobile;  

    /**
     * 网址
     */@ContactValidator(type = ContactType.WEBSITE)    private String website;  

    public String getName() {
        return name;
    }  

    public void setName(String name) {
        this.name = name;
    }  

    public String getEmail() {
        return email;
    }  

    public void setEmail(String email) {
        this.email = email;
    }  

    public String getPhone() {
        return phone;
    }  

    public void setPhone(String phone) {
        this.phone = phone;
    }  

    public String getMobile() {
        return mobile;
    }  

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }  

    public String getWebsite() {
        return website;
    }  

    public void setWebsite(String website) {
        this.website = website;
    }  

}

校验工具类:

package com.annotation.test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;  

public class ValidatorUtil {  

    /**
     * 校验邮箱
     *
     * @param email
     * @return
     */
    public static boolean isValidEmail(String email) {  

        Pattern p =Pattern.compile("[email protected]+\\.[a-z]+");
        Matcher m =p.matcher(email);  

        return m.matches();
    }  

    /**
     * 校验电话
     *
     * @param phone
     * @return
     */
    public static boolean isValidPhone(String phone) {  

        Pattern p =Pattern.compile("\\d\\d([,\\s])?\\d\\d\\d\\d([,\\s])?\\d\\d\\d\\d");
        Matcher m =p.matcher(phone);  

        return m.matches();
    }  

    /**
     * 校验手机号
     *
     * @param mobile
     * @return
     */
    public static boolean isValidMobile(String mobile) {  

        Pattern p =Pattern.compile("^[1]([3][0-9]{1}|59|58|88|89)[0-9]{8}$");
        Matcher m =p.matcher(mobile);  

        return m.matches();
    }  

    /**
     * 校验网址
     *
     * @param website
     * @return
     */
    public static boolean isValidWebsite(String website){  

        Pattern p =Pattern.compile("^(https?|ftp|file)://.+$");
        Matcher m =p.matcher(website);  

        return m.matches();
    }  

}

解析器:

package com.annotation.test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;  

public class FieldAnnotationParser {  

    /**
     * 解析器
     * @param user
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void parser(User user)throws IllegalArgumentException, IllegalAccessException {  

        //获取所有字段
        Field[]fields = user.getClass().getDeclaredFields();

        for(Field field : fields){  

           Annotation[] annotations = field.getAnnotations();//获取字段上的所有注解

            for(Annotation annotation : annotations){
                //如果是ContactValidator注解
                if(annotation instanceof ContactValidator){  

                    ContactValidator contactValidator = (ContactValidator) annotation;  

                   if(field.getModifiers() == Modifier.PRIVATE){//如果是私有字段,设置反射的对象在使用时取消Java语言访问检查
                        field.setAccessible(true);
                   }

                   boolean result =false;//标识变量
                   //获取字段值
                   String fieldValue = (String) field.get(user);

                   switch (contactValidator.type()) {
                       case EMAIL:
                            result =ValidatorUtil.isValidEmail(fieldValue);
                            break;
                       case PHONE:
                            result =ValidatorUtil.isValidPhone(fieldValue);
                            break;
                       case MOBILE:
                            result =ValidatorUtil.isValidMobile(fieldValue);
                            break;
                       case WEBSITE:
                            result =ValidatorUtil.isValidWebsite(fieldValue);
                            break;
                   }  

                   if(!result){
                       System.out.println("Invalid " + field.getName() + ": " +fieldValue);
                   }
                }
            }
        }
    }

}

测试类:

package com.annotation.test;

public class AnnotationTest {
    /**
     *主函数
     * @param args
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    public static void main(String[] args)throws IllegalArgumentException,IllegalAccessException {  

       User user =new User();
       user.setName("TimSturt");
       user.setPhone("0931234 3819"); //错误的电话格式
       user.setMobile("13575309630");   //正确的手机格式
       user.setEmail("[email protected]");  //正确邮箱格式
       user.setWebsite("fttp://test.com"); //错误的网站url

       FieldAnnotationParser.parser(user);
    }  

}

输出如下:

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

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

结语:

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

针对前述内容进行小结:

(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, . . .)元素声明的顺序没有关系,有默认值的元素的声明可以不列在初始化表中,此时他们使用默认值。

例如,根据上述定义如下的三个Annotation的声明是等价的:
@BugReport(assignedTo="Harry", severity=0)
@BugReport(severity=0, assignedTo="Harry")
@BugReport(assignedTo="Harry")

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

时间: 2024-10-07 08:28:39

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

转:java 注解 @Retention @interface 元数据

原文: http://www.cnblogs.com/coolgun/p/3802535.html java注解 即是注释了,百度解释:也叫元数据.一种代码级别的说明. 个人理解:就是内容可以被代码理解的注释,一般是一个类. 元数据 也叫元注解,是放在被定义的一个注解类的前面 ,是对注解一种限制.只有两个: @Retention 和 @Target  (我只查到这两个). @Retention :用来说明该注解类的生命周期.它有以下三个参数: RetentionPolicy.SOURCE  :

java 注解@Retention @interface 元数据

原文  http://www.cnblogs.com/coolgun/p/3802535.html java注解 即是注释了,百度解释:也叫元数据.一种代码级别的说明. 个人理解:就是内容可以被代码理解的注释,一般是一个类. 元数据 也叫元注解,是放在被定义的一个注解类的前面 ,是对注解一种限制.只有两个: @Retention 和 @Target  (我只查到这两个). @Retention :用来说明该注解类的生命周期.它有以下三个参数: RetentionPolicy.SOURCE  :

Java注解(1)-注解基础

注解(Annotation)是在JAVA5中开始引入的,它为在代码中添加信息提供了一种新的方式.注解在一定程度上把元数据与源代码文件结合在一起,正如许多成熟的框架(Spring)所做的那样.那么,注解到底可以做什么呢? 1.注解的作用. 提供用来完整地描述程序所需要的信息,如编译期校验程序信息. 生成描述符文件,或生成新类的定义. 减轻编写"样板"代码(配置文件)的负担,可以使用注解自动生成. 更加干净易读的代码. 编译期类型检查. 2.Java提供的注解 Java5内置了一些原生的注

Java注解

注解是一种元数据形式,提供关于不是程序部分的程序的数据.操作代码上的注解不影响注解的代码. 注解有许多用途,其中: 编译器信息 -- 注解被用于编译器检测错误或抑制警告. 编译时和部署时处理 -- 软件工具能处理注解信息生成代码.XML文件等等. 运行时处理 -- 一些注解可用在运行时检查. 1     注解基础 1.1    注解的格式 最简单的注解形式如下所示: @Entity 在符号字符(@)告诉编译器这是一个注解.在下面的例子中,注解的名称是Override: @Override voi

Java注解介绍

原文链接: Java Annotations: An Introduction原文日期: 2005年10月14日翻译日期: 2014年07月20日翻译人员: 铁锚 翻译完后,感觉这篇文章是不是在http://www.developer.com被挖坟了? Java注解介绍 基于注解(Annotation-based)的Java开发无疑是最新的开发趋势.[译者注: 这是05年的文章,在2014年,毫无疑问,多人合作的开发,使用注解变成很好的合作方式,相互之间的影响和耦合可以很低]. 基于注解的开发将

Java注解实践

Java注解实践 注解对代码的语意没有直接影响, 他们只负责提供信息给相关的程序使用. 注解永远不会改变被注解代码的含义, 但可以通过工具对被注解的代码进行特殊处理. JDK 基本Annotation 注解 说明 @Override 重写 @Deprecated 已过时 @SuppressWarnings(value = "unchecked") 压制编辑器警告 @SafeVarargs 修饰"堆污染"警告 @FunctionalInterface Java8特有的

[笔记]Java注解全面解析

JAVA注解 注解(Annotation),也叫元数据.一种代码级别的说明.它是JDK1.5及以后版本引入的一个特性,与类.接口.枚举是在同一个层次.它可以声明在包.类.字段.方法.局部变量.方法参数等的前面,用来对这些元素进行说明,注释. 1.注解分类 1.1按照运行机制分类 源码注解 注解只在源码中存在,编译成.class文件就不存在了 编译时注解 注解在源码和.class文件中都存在 比如: @Override @Deprecated @Suppvisewarnings 运行时注解 在运行

JAVA注解Annotation

JAVA注解Annotation JAVA注解Annotation 什么是注解 为什么要引入注解 JDK内建Annotation JDK元Annotation Annotation示例 自定义注解 提取Annotation信息 Annotation处理器编写 Annotation处理器处理异常 什么是注解? ??用一个词就可以描述注解,那就是元数据,即一种描述数据的数据.所以,可以说注解就是源代码的元数据.比如,下面这段代码: @Override publicString toString()

Java注解编程指南

Java注解编程指南 Java Annotation Tutorial +1概念 注解是JDK1.5开始引入的一个Java语言新特性.注解是与接口很相似,它与类.接口.枚举是在同一个层次,它 们都称作为java 的一个类型(TYPE). +1.1Java语言指南解释 注解(也被称做元数据),为我们提供了程序代码的描述信息,而这些描述信息并不属于程序本身.注解并不直接 影响其注释的代码的工作. +1.2Java编程思想解释 注解(也被称做元数据),为我们在代码中添加信息提供了一种形式化的方法,使我

Java注解教程和自定义注解

Java注解提供关于代码的信息,并且对它们注解的代码没有直接的影响.在这篇教程中,我们将学习Java注解,如何自定义注解,注解用法和如何使用反射解析注解. Java注解在Java1.5被引用并且在一些Java框架如Hibernate,Jersey,Spring中被广泛使用.注解是被嵌入到程序自身中的程序的元数据.它可以被注解解析工具或编译器解析.我们也可以指定注解的生命周期,或者仅在编译期间可用或者直到运行时. 在引入注解之前,我们可以通过程序注释或者Java文档来获取程序的元数据,但是注解提供