Java 注解

 

一、什么是 java 注解

         注解,顾名思义,注解, 就是对某一事物进行添加注释说明,会存放一些信息,这些信息可能对以后某个时段来说是很有用处的。

         Java 注解又叫 java 标注,java 提供了一套机制,使得我们可以对方法、类、参数、包、域以及变量等添加标准 (即附上某些信息)。且在以后某个时段通过反射将标注的信息提取出来以供使用。

二、自定义 Java 标注

1  为什么要自定义注解

         Java 从 1.5 版本以后默认内置三个标注:

Ø @Override: 只能用在方法之上的,用来告诉别人这一个方法是改写父类的。

Ø @Deprecated: 用来表示某个类的属性或方法已经过时,不想别人再用时, 在属性和方法上用 @Deprecated 修饰; 编译的时候会用产生警告信息, 可以设定在程序里的所有的元素上.

Ø @SuppressWarnings: 这一个类型可以来暂时把一些警告信息消息关闭.

但是,仅仅这三个标注是不能满足我们开发时一些需求的。所以 java 允许我们自定义注解来使用。

2  如何自定义注解

java 用  @interface xxx{ } 定义一个注解 @xxx,一个注解是一个类

 

自定义步骤大致分为两步:

1,              通过 @interface 关键字声明注解名称,以及注解的成员属性或者叫做注解的参数。

2,              使用 java 内置的四个元注解对这个自定义标注的功能和范围进行一些限制

问题来了,什么是元注解?

3  什么是元注解

元注解,就是定义注解的注解,也就是说这些元注解是的作用就是专门用来约束其它注解的注解。请区别上面那三个注解,他们也是通过元注解定义而来的。

元注解有哪些呢,主要有四个 @Target,@Retention,@Documented,@Inherited?

1.  * 元注解有:@Target,@Retention,@Documented,@Inherited

2.  * 

3.  *     @Target 表示该注解用于什么地方,可能的 ElemenetType 参数包括:

4.  *         ElemenetType.CONSTRUCTOR 构造器声明

5.  *         ElemenetType.FIELD 域声明(包括 enum 实例)

6.  *         ElemenetType.LOCAL_VARIABLE 局部变量声明

7.  *         ElemenetType.METHOD 方法声明

8.  *         ElemenetType.PACKAGE 包声明

9.  *         ElemenetType.PARAMETER 参数声明

10. *         ElemenetType.TYPE 类,接口(包括注解类型)或 enum 声明

11. *         

12. *     @Retention 表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括:

13. *         RetentionPolicy.SOURCE 注解仅存在于源码中, 注解将被编译器丢弃. 即不会留在 class(字节码文件) 文件中

14. *         RetentionPolicy.CLASS 注解在 class 文件中可用,但会被 VM 丢弃,即在运行时无法获得

15. *         RetentionPolicy.RUNTIME 注解会在 class 字节码文件中存在,VM 在运行期也保留注释,因此在运行时可以通过反射机制读取注解的信息。

 

16. *         

17. *     @Documented 默认情况下,注解不会在 javadoc 中记录,但是可以通过这个注解来表明这个注解需要包含在 javadoc 中

18. *     

19. *     @Inherited 允许子类继承父类中的注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一个使用了 @Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该 class 的子类。

4 自定义及使用注解示例

自定义注解的语法要求:

1 @Target({ElementType.METHOD,ElementType.TYPE})

2 @Retention(RetentionPolicy.RUNTIME)

3 @Inherited

4 @Documented

5 ublic @interface Description {

     String desc();

     String author();

     int age() default 18;

 }

 

首先我们要明确这不是一个接口,它是使用 @interface 关键字定义的一个注解。

然后我们看下面的几个方法,String desc(); 虽然它很类似于接口里面的方法,其实它在注解里面只是一个成员变量(成员以无参无异常的方式声明),int age() default 18;(成员变量可以用 default 指定一个默认值的)。

最后我们要知道:

①. 成员类型是受限制的,合法的类型包括基本的数据类型以及 String,Class,Annotation,Enumeration 等。

②. 如果注解只有一个成员,则成员名必须取名为 value(),在使用时可以忽略成员名和赋值号(=)。

③. 注解类可以没有成员,没有成员的注解称为标识注解。

 

使用自定义注解:

使用注解的语法:

@< 注解名 >(< 成员名 1>=< 成员值 1>,< 成员名 2>=< 成员值 2>,…)

案例:

 

1 @Description(desc="i am Color",author="boy",age=18)

2 public String Color() {

3    return "red";

4 }

这里的 Description 是我们刚才在自定义注解语法要求里面定义的注解噢,然后我们可以给它的每一个成员变量赋值,注意数据类型。值得注意的是,因为我们前面定义的作用域是在方法和类接口上,所以这个注解在 Color() 方法上使用是没问题的。

 

解析注解

概念:

通过反射获取类 、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。

   Java 使用 Annotation 接口来代表程序元素前面的注解,该接口是所有 Annotation 类型的父接口。相应地,Java 在 java.lang.reflect 包下新增了 AnnotatedElement 接口,该接口代表程序中可以接受注解的程序元素。

   实际上,java.lang.reflect 包所有提供的反射 API 扩充了读取运行时 Annotation 信息的能力。当一个 Annotation 类型被定义为运行时的 Annotation 后,该注解才能是运行时可见,当 class 文件被装载时被保存在 class 文件中的 Annotation 才会被虚拟机读取。

   AnnotatedElement 接口是所有程序元素(Field、Method、Package、Class 和 Constructor)的父接口,所以程序通过反射获取了某个类的 AnnotatedElement 对象之后,程序就可以调用该对象的如下七个方法来访问 Annotation 信息:

<T extends Annotation> T getAnnotation(Class<T> annotationClass) :返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回 null;

Annotation[] getDeclaredAnnotation(Class<T>):返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回 null;与此接口中的其他方法不同,该方法将忽略继承的注解;

Annotation[] getAnnotations():返回该程序元素上存在的所有注解;

Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注解;

Annotation[] getAnnotationsByType(Class<T>):返回直接存在于此元素上指定注解类型的所有注解;

Annotation[] getDeclaredAnnotationsByType(Class<T>):返回直接存在于此元素上指定注解类型的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注解;

boolean isAnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回 true,否则返回 false;

 

 自定义一个类级别的标注 Description

package lighter.javaeye.com;

   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.TYPE)// 这个标注应用于类

   @Retention(RetentionPolicy.RUNTIME)// 标注会一直保留到运行时

   @Documented// 将此注解包含在 javadoc 中

   public @interface Description {

       String value();

   }

  

   首先,一个注解一般需要 2 个元注解修饰:

   @Target(ElementType.FIELD)

   @Retention(RetentionPolicy.RUNTIME)

   具体作用上面已解释。

   所有的注解都会有一个类似于“func”的部分。这个可以理解为注解的参数。

         再定义个方法级别的注解 Name

   package lighter.javaeye.com;

   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 与 @Description 里的不同, 参数成员也不同

   @Target(ElementType.METHOD)

   @Retention(RetentionPolicy.RUNTIME)

   @Documented

   public @interface Name {

       String originate();

       String community();

   }

 

         然后使用以上两个注解

package lighter.javaeye.com;

 

@Description(value="test description")

public class DescTest{

         @Name(originate="panda",community="javatest")

         public String getName()

         {

                   return null;

         }

       

         @Name(originate="tiger",community="spider")

         public String getName2()

         {

                   return "this is a case~”

         }

}

 

说明:其中标注“@Description(value="test description")”,可以写成“@Description("test description") ”,结果也是一样的。因为 Description 标注定义的时候其参数 (或者说属性) 为 value。而 value 比较特殊,它在被指定参数的时候可以不用显示的写出来。当然如果定义的时候参数名不是 value 而是其它的比如 des,那么使用注解的时候,必须显示写出参数名,然后再赋值:@Description(Des=”xxx”)

                                                                                                                                              

 

提取出注解的信息

package lighter.javaeye.com;

 

 import java.lang.reflect.Method;

 import java.util.HashSet;

 import java.util.Set;

 

 public class TestAnnotation {

         /**

          * author lighter

          * 说明: 具体关天 Annotation 的 API 的用法请参见 javaDoc 文档

          */

       public static void main(String[] args) throws Exception {

       String CLASS_NAME = "lighter.javaeye.com.JavaTest";

       Class test = Class.forName(CLASS_NAME);

       Method[] method = test.getMethods();

       boolean flag = test.isAnnotationPresent(Description.class);

        if(flag)

        {

                 Description des = (Description)test.getAnnotation(Description.class);

                 System.out.println("描述:"+des.value());

                 System.out.println("-----------------");

        }

      

        // 把 JavaEyer 这一类有利用到 @Name 的全部方法保存到 Set 中去

        Set<Method> set = new HashSet<Method>();

        for(int i=0;i<method.length;i++)

        {

                 boolean otherFlag = method[i].isAnnotationPresent(Name.class);

                 if(otherFlag) set.add(method[i]);

        }

        for(Method m: set)

        {

                 Name name = m.getAnnotation(Name.class);

                 System.out.println(name.originate());

                 System.out.println("the community:"+name.community());

        }

     }

}

 

注意事项:

所有的 Annotation 会自动继承 java.lang.annotation 这一个接口, 所以不能再去继承别的类或是接口.

   最重要的一点,Annotation 类型里面的参数该怎么设定:

   第一, 只能用 public 或默认 (default) 这两个访问权修饰. 例如,String value(); 这里把方法设为 defaul 默认类型.

   第二, 参数成员只能用基本类型 byte,short,char,int,long,float,double,boolean 八种基本数据类型和 String,Enum,Class,annotations 等数据类型, 以及这一些类型的数组. 例如,String value(); 这里的参数成员就为 String.

注解用例

注解的功能很强大,Spring 和 Hebernate 这些框架在日志和有效性中大量使用了注解功能。注解可以应用在使用标记接口的地方。不同的是标记接口用来定义完整的类,但你可以为单个的方法定义注释,例如是否将一个方法暴露为服务。

在最新的 servlet3.0 中引入了很多新的注解,尤其是和 servlet 安全相关的注解。

HandlesTypes –该注解用来表示一组传递给 ServletContainerInitializer 的应用类。

HttpConstraint – 该注解代表所有 HTTP 方法的应用请求的安全约束,和 ServletSecurity 注释中定义的 HttpMethodConstraint 安全约束不同。

 

HttpMethodConstraint – 指明不同类型请求的安全约束,和 ServletSecurity 注解中描述 HTTP 协议方法类型的注释不同。

MultipartConfig –该注解标注在 Servlet 上面,表示该 Servlet 希望处理的请求的 MIME 类型是 multipart/form-data。

ServletSecurity 该注解标注在 Servlet 继承类上面,强制该 HTTP 协议请求遵循安全约束。

WebFilter – 该注解用来声明一个 Server 过滤器;

WebInitParam – 该注解用来声明 Servlet 或是过滤器的中的初始化参数,通常配合 @WebServlet 或者 @WebFilter 使用。

WebListener –该注解为 Web 应用程序上下文中不同类型的事件声明监听器。

WebServlet –该注解用来声明一个 Servlet 的配置。