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的配置。

原文地址:https://www.cnblogs.com/pandaly/p/10342623.html