`
Josh_Persistence
  • 浏览: 1649933 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类

深入浅出Java Annotation(元注解和自定义注解)

阅读更多

一、基本概述

 

 

   Annontation是Java5开始引入的新特征。中文名称一般叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。

 

  更通俗的意思是为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且是供指定的工具或框架使用的。

 

    Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

 

二、原理

 

  Annotation其实是一种接口。通过Java的反射机制相关的API来访问Annotation信息。相关类(框架或工具中的类即使用注解的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。

 

  Annoation和程序代码的隔离性:Annotation是不会影响程序代码的执行,无论Annotation怎么变化,代码都始终如一地执行。

 

  忽略性:Java语言解释器在工作时会忽略这些annotation,因此在JVM 中这些Annotation是“不起作用”的,只能通过配套的工具才能对这些Annontaion类型的信息进行访问和处理。

 

  Annotation与interface的异同:

 

  1)、Annotation类型使用关键字@interface而不是interface。

 

  这个关键字声明隐含了一个信息:它是继承了java.lang.annotation.Annotation接口,并非声明了一个interface。

 

  2)、Annotation类型、方法定义是独特的、受限制的。

 

  Annotation 类型的方法必须声明为无参数无异常抛出的

     这些方法定义了Annotation的成员:方法名成为了成员名,而方法返回值成为了成员的类型。方法返回值类型必须为primitive类型、Class类型、枚举类型、annotation类型或者由前面类型之一作为元素的一维数组。方法的后面可以使用 default和一个默认数值来声明成员的默认值,null不能作为成员默认值,这与我们在非Annotation类型中定义方法有很大不同。

 

  Annotation类型和它的方法不能使用Annotation类型的参数、成员不能是generic。只有返回值类型是Class的方法可以在Annotation类型中使用generic,因为此方法能够用类转换将各种类型转换为Class。

 

  3)、Annotation类型又与接口有着近似之处。

 

  它们可以定义常量、静态成员类型(比如枚举类型定义)。Annotation类型也可以如接口一般被实现或者继承。

 

三、应用场合

 

  Annotation一般作为一种辅助途径,应用在软件框架或工具中,在这些工具类中根据不同的 Annontation注解信息采取不同的处理过程或改变相应程序元素(类、方法及成员变量等)的行为。

 

  例如:Junit、Struts、Spring等流行工具框架中均广泛使用了Annontion。使代码的灵活性大提高。

 

四、元注解

      元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:

  1.@Target,
  2.@Retention,
  3.@Documented,
  4.@Inherited
  这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。


 @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声明

实例:

 

@Target(ElementType.TYPE)
public @interface Table {
    /**
     * 数据表名称注解,默认值为类名称
     * @return
     */
    public String tableName() default "className";
}

@Target(ElementType.FIELD)
public @interface NoDBColumn {

}

注解Table 可以用于注解类、接口(包括注解类型)或enum声明,而注解NoDBColumn仅可用于注解类的成员变量。

 


 @Retention:

  @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

  作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

  取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)

  Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。具体实例如下:

 

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}

 Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理。


 @Documented:

  @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

 

 

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}

 @Inherited:

 

  @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

  注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

  当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层.

实例代码:

/** 
 * @author wangsheng
 **/
@Inherited
public @interface Greeting {
    public enum FontColor{ BULE,RED,GREEN};
    String name();
    FontColor fontColor() default FontColor.GREEN;
}

  

 

五、常见标准的Annotation

java5版本开始,自带了三种标准annontation类型,

 

    (1)、Override

 

  java.lang.Override 是一个marker annotation类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。

 

  这个annotaton常常在我们试图覆盖父类方法而确又写错了方法名时加一个保障性的校验过程。

 

    (2)、Deprecated

 

  Deprecated也是一种marker annotation。当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。

 

  注意:@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的:前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档(包含程序成员为什么已经过时、它应当如何被禁止或者替代的描述)。

 

    (3)、SuppressWarnings

 

  此注解能告诉Java编译器关闭对类、方法及成员变量的警告。

 

  有时编译时会提出一些警告,对于这些警告有的隐藏着Bug,有的是无法避免的,对于某些不想看到的警告信息,可以通过这个注解来屏蔽。

 

  SuppressWarning不是一个marker annotation。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。

 

  annotation语法允许在annotation名后跟括号,括号中是使用逗号分割的name=value对用于为annotation的成员赋值:

代码:

 

@SuppressWarnings(value={"unchecked","fallthrough"})

public void lintTrap() { /* sloppy method body omitted */ }

 

 

   在这个例子中SuppressWarnings annotation类型只定义了一个单一的成员,所以只有一个简单的value={...}作为name=value对。又由于成员值是一个数组,故使用大括号来声明数组值。

 

 

 

   注意:我们可以在下面的情况中缩写annotation:当annotation只有单一成员,并成员命名为"value="。这时可以省去"value="。比如将上面的SuppressWarnings annotation进行缩写:

 

代码:

 

@SuppressWarnings({"unchecked","fallthrough"})

 

 

如果SuppressWarnings所声明的被禁止警告个数为一个时,可以省去大括号:

 

@SuppressWarnings("unchecked")

 

 

六、自定义注解

     使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

       定义注解格式:
  public @interface 注解名 {定义体}

注解参数的可支持数据类型:

    1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2.String类型
    3.Class类型
    4.enum类型
    5.Annotation类型
    6.以上所有类型的数组

  Annotation类型里面的参数该怎么设定: 
  第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
  第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
  第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.例:下面的例子FruitName注解就只有一个参数成员。

简单的自定义注解和使用注解实例:

 

package annotation;

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;

/**
 * 水果名称注解
 * @author wangsheng
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}

 

 

 

package annotation;

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;

/**
 * 水果颜色注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 颜色枚举
     * @author wangsheng
     *
     */
    public enum Color{ BULE,RED,GREEN};
    
    /**
     * 颜色属性
     * @return
     */
    Color fruitColor() default Color.GREEN;

}

 

package annotation;

import annotation.FruitColor.Color;

public class Apple {
    
    @FruitName("Apple")
    private String appleName;
    
    @FruitColor(fruitColor=Color.RED)
    private String appleColor;
    
    
    
    
    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }
    
    
    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }
    
    public void displayName(){
        System.out.println("水果的名字是:苹果");
    }
}

 注解元素的默认值:

 

    注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法。例如:

package annotation;

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;

/**
 * 水果供应者注解
 * @author wangsheng
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供应商编号
     * @return
     */
    public int id() default -1;
    
    /**
     * 供应商名称
     * @return
     */
    public String name() default "";
    
    /**
     * 供应商地址
     * @return
     */
    public String address() default "";
}

 

 

定义了注解,并在需要的时候给相关类,类属性加上注解信息,如果没有响应的注解信息处理流程,注解可以说是没有实用价值。如何让注解真真的发挥作用,主要就在于注解处理方法,下一步我们将学习注解信息的获取和处理!

 

 

看完上面的例子后,来看一个更加具体的例子,该例子共涉及4个类:

清单1:Author.java

/**
 * 
 */
package com.wsheng.aggregator.annotation;

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;

/**
 * 定义作者信息,name和group
 * 
 * @author Josh Wang(Sheng)
 * 
 * @email  josh_wang23@hotmail.com
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Author {
	
	String name(); // 因为没有定义public,所以默认的访问权限为包权限,在定义时没有指定默认值,则使用时必须指定默认值
	String group();

}

 

 

清单2:Description.java

 

/**
 * 
 */
package com.wsheng.aggregator.annotation;

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;

/**
 * 定义描述信息value
 * 
 * @author Josh Wang(Sheng)
 * 
 * @email  josh_wang23@hotmail.com
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Description {

	String value();// 只有一个属性时,最好定义为value,因为可以省略哦:)
}

 

 

 

清单3:Utility.java : 使用自定义Annotation注解的类

 

/**
 * 
 */
package com.wsheng.aggregator.annotation;

/**
 * @author Josh Wang(Sheng)
 * 
 * @email  josh_wang23@hotmail.com
 * 
 */
@Description(value="这是一个有用的工具类") // value可以省略
public class Utility {

	@Author(name="wangsheng", group="developer team")
	public String work() {
		return "work over!";
	}
}

 

 

清单4:AnalysisAnnotation.java

 

/**
 * 
 */
package com.wsheng.aggregator.annotation;

import java.lang.reflect.Method;

/**
 * 
 *在运行时分析处理annotation类型的信息
 * 
 * @author Josh Wang(Sheng)
 * 
 * @email  josh_wang23@hotmail.com
 * 
 */
public class AnalysisAnnotation {
	
	public static void main(String[] args) {
		try {
			
			// 通过运行时反射API获得annotation信息
			Class<?> rtClass = Class.forName("com.wsheng.aggregator.annotation.Utility");
			Method[] methods = rtClass.getMethods();
			
			boolean descriptionExist = rtClass.isAnnotationPresent(Description.class);
			if (descriptionExist) {
				Description description = (Description)rtClass.getAnnotation(Description.class);
				System.out.println("Utility's Description --- > " + description.value());
				
				for (Method method : methods) {
					if (method.isAnnotationPresent(Author.class)) {
						Author author = (Author)method.getAnnotation(Author.class);
						System.out.println("Utility's Author ---> " + author.name() + " from " + author.group());
					}
				}
			}
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

}

 

运行结果:

Utility's Description --- > 这是一个有用的工具类

 

Utility's Author ---> wangsheng from developer team

 

 

 

3
3
分享到:
评论

相关推荐

    深入浅出Java注解(注解概述+注解的本质+反射注解信息+元注解+属性的数据类型及特别的属性:value和数组)

    自定义注解就是我们自己写的注解,而 JDK 内置注解则包括 `@Override`、`@Deprecated` 等。第三方框架定义的注解则包括 SpringMVC 的 `@Controller` 等。 注解的本质 注解的本质是一个接口。我们可以通过反编译...

    java深入浅出哥哥复合管

    【标题】"Java深入浅出哥哥复合管"可能是指一个关于Java编程的教程或课程,其中"哥哥复合管"可能是作者或讲师的别称,或者是某种特定编程概念的比喻。这个标题暗示了内容将深入讲解Java语言,并可能涵盖一些高级或...

    Java+Annotation.pdf

    本文将基于《Java+Annotation.pdf》的内容,深入浅出地介绍注解的基本概念、工作原理以及如何在实际项目中运用。 #### 二、为什么使用注解? 在Java应用开发中,经常需要使用模板代码或维护一些与程序代码同时存在...

    Java基础知识精讲,深入浅出,好资料

    这份"Java基础知识精讲"的资料深入浅出地介绍了Java的核心概念,是初学者理想的入门指南。以下将详细介绍其中可能涵盖的知识点。 1. **Java简介**:讲解Java的发展历史,其跨平台的特性(Write Once, Run Anywhere...

    common(自定义注解校验数据有效性)

    总的来说,“common”这个主题深入浅出地讲解了如何在Java项目中利用自定义注解进行数据有效性校验,这对于提升代码质量、减少运行时错误以及增强系统安全性具有重要意义。通过学习和实践这些知识,开发者可以更有效...

    深入浅出struts2

    本资料"深入浅出Struts2"将带你深入了解这个框架的核心概念、配置、拦截器以及实际应用。 1. **基础概念**: - MVC模式:Struts2基于MVC设计模式,它将业务逻辑、数据和用户界面分离,使得代码更易于维护和测试。 ...

    百度文库 牛牧struts2.x深入浅出

    牛牧的"Struts2.x深入浅出"教程是针对这个框架的深度解析,主要帮助开发者理解并熟练掌握Struts2的核心概念和实践技巧。 在第二部分中,我们首先会接触到`struts2_GetFromData`,这部分主要讲解了如何从HTTP请求中...

    超级经典java例子,涉及到大部分java类库的使用

    "超级经典java例子"集合很可能包含了一系列深入浅出的代码示例,这些示例旨在帮助开发者理解和掌握Java类库的各种功能。这个压缩包可能涵盖了从基本语法到高级特性的大量示例,对于学习和提升Java技能非常有帮助。 ...

    Absolute Java

    这本书深入浅出地讲解了Java语言的基本结构和编程技巧,使得没有C++或其他编程背景的读者也能轻松上手。 1. **Java语言简介**:Java是一种面向对象的、跨平台的编程语言,由Sun Microsystems开发,现在由甲骨文公司...

    Spring Security 3.pdf

    Spring Security 是一个强大的Java安全框架,专为Java和Spring生态系统设计,用于实现全面的身份验证、授权和服务级安全功能。在Spring Security 3版本中,它引入了许多改进和新特性,以适应不断变化的安全需求和...

    Java解惑(中文版)_java_java解惑_solve65p_

    "solve65p"可能代表这本书包含65个问题或主题,每个都深入浅出地进行了讲解,旨在解决初学者在编程实践中可能遇到的常见问题。 Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems(现已被Oracle公司收购...

    struts深入浅出

    Struts2是Java Web开发中的一个强大框架,它基于...通过深入理解和熟练运用,开发者可以构建出高效、易维护的企业级应用。然而,随着Spring Boot等现代框架的崛起,开发者在选择技术栈时也应考虑其适应性和长远发展。

    《Java编程思想》第四版答案

    这本书深入浅出地介绍了Java语言的核心概念和技术,包括面向对象编程、异常处理、多线程、集合框架、网络编程等多个方面。本书的答案集提供了书中练习题的解答,帮助读者更好地理解和应用所学知识。 1. 面向对象...

    Java语言程序设计案例教程__郑莉+刘兆宏__源代码

    该教材通过丰富的案例,深入浅出地介绍了Java编程的基础概念、语法和实战技巧。以下是根据教材标题和描述所提炼的Java语言相关知识点: 1. **Java基础知识**:包括Java的发展历程、特点、应用领域以及为什么选择...

    Thinking In Java第三版本

    这本书以其深入浅出的讲解方式和丰富的实例,深受广大Java开发者喜爱。书中不仅覆盖了Java语言的基础知识,还涵盖了高级特性和设计模式,是Java初学者和进阶者的重要参考书。 第一部分:Java基础知识 1. 类与对象:...

    Java语言程序设计教程 张席

    这门课程深入浅出地介绍了Java编程的基础知识和高级特性,旨在帮助学习者掌握这一强大且广泛使用的编程语言。以下是根据课程标题、描述和文件列表提炼的Java编程相关知识点: 1. **Java简介**:Java是一种跨平台、...

    Java核心技术 卷1 基础知识 原书第9版

    10. **枚举和注解**:介绍Java枚举类型的应用,以及注解(Annotation)的使用,包括元注解和自定义注解,它们在代码管理和框架集成中发挥着重要作用。 11. **泛型**:讨论泛型的引入,类型参数化,通配符,以及泛型...

    Thinking in Java 3rd Edition

    12. **枚举和注解**:介绍了Java 5引入的枚举类型及其优势,以及注解(Annotation)的使用,包括元注解和自定义注解。 13. **设计模式**:探讨了一些常见的设计模式,如工厂模式、单例模式、观察者模式等,帮助读者...

    Thinking in Java 4th Edition及习题答案

    这本书深入浅出地介绍了Java编程语言的核心概念和技术,深受程序员喜爱。第四版更是对原有的内容进行了更新,以适应Java语言的发展变化。下面将详细讨论这本书中的关键知识点及其习题答案。 1. **基础语法**:书中...

    Java开发实战经典—5 (李兴华)(PDF)

    《Java开发实战经典》是李兴华先生撰写的一本针对Java初学者的权威教程,它以其深入浅出的讲解方式和丰富的实战案例深受广大开发者喜爱。这本书覆盖了Java开发的多个核心领域,对于构建扎实的Java基础知识至关重要。...

Global site tag (gtag.js) - Google Analytics