zoukankan      html  css  js  c++  java
  • java注解细节

      现在很多框架都使用注解了,典型的像Spring里面就可以看到大量的注解,比如@Service,@Controller这一类的都是注解。注解Annotation是java一项很重要的功能。下面就来整理一下关于注解的一些细节。

      1.首先,什么是注解呢?

      较为官方的解释是,注解是元数据,就是解释数据的数据。说得通俗一点,它是一种能够修饰类、变量、方法、参数等数据的元数据。以一个简单的例子,我们经常看到的一个注解是@Override。比如如下代码。

    package com.xdx.learn;
    
    public class Father {
        public void func(){
            System.out.println("hello");
        }
    
    }
    class son extends Father{
    
        @Override
        public void func() {
            System.out.println("hi");
        }
    }

    可以看到在son类中,重写了(覆盖)父类的func()方法,我们用了一个@Override的注解去修饰这个方法。它起到的作用是告诉编译器,这个方法时覆盖父类的方法,假如我不小心把func写成了Func,编译器由于已经得知了我是想覆盖父类的func方法,就会提示我,父类并没有一个叫做Func的方法,我就会去检查,哦,原来是我写错了啊。

      所以注解起到了一种修饰元数据的作用,被修饰过的元数据具有某些特性,这些特性将在程序编译、运行等时段被利用。具体是如何利用而起作用的,我们就不用去管了。提供该注解的提供者会搞定。

      比如Spring中用@Resource注解的成员变量,将在该类实例化的时候,被注入到类的实例当中。而具体如何利用@Resource注解实现这一过程的,Spring帮我们做了。

      2.注解的定义

      我们来看看上面讲的@Resource

    的注解的内部是如何定义的。  

    @Target({TYPE, FIELD, METHOD})
    @Retention(RUNTIME)
    public @interface Resource {
        String name() default "";
        String lookup() default "";
        Class<?> type() default java.lang.Object.class;
        enum AuthenticationType {
                CONTAINER,
                APPLICATION
        }
    
        AuthenticationType authenticationType() default        AuthenticationType.CONTAINER;
        boolean shareable() default true;
        String mappedName() default "";
        String description() default "";
    }

      上述便是一个注解的定义,可以看到,这个注解都被两个元注解所修饰,分别是@Target和@Retention

      先来看看@Target。 

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        /**
         * Returns an array of the kinds of elements an annotation type
         * can be applied to.
         * @return an array of the kinds of elements an annotation type
         * can be applied to
         */
        ElementType[] value();
    }

      它也被三个元注解来修饰,除了@Target和@Retention,还多出了一个@Documented

      事实上,总共有4种元注解,@Target、@Retention、@Documented@Inherited

       @Target——指明该类型的注解可以注解的程序元素的范围(作用目标)。该元注解的取值可以为TYPE,METHOD,CONSTRUCTOR,FIELD等。如果Target元注解没有出现,那么定义的注解可以应用于程序的任何元素。由上述的@interface Target这个注解的定义,我们可以看到它的取值是一个枚举类型的数组。事实上,它们具体的值如下。

      

    public enum ElementType {
        /** Class, interface (including annotation type), or enum declaration */
        TYPE,
    
        /** Field declaration (includes enum constants) */
        FIELD,
    
        /** Method declaration */
        METHOD,
    
        /** Formal parameter declaration */
        PARAMETER,
    
        /** Constructor declaration */
        CONSTRUCTOR,
    
        /** Local variable declaration */
        LOCAL_VARIABLE,
    
        /** Annotation type declaration */
        ANNOTATION_TYPE,
    
        /** Package declaration */
        PACKAGE,
    
        /**
         * Type parameter declaration
         *
         * @since 1.8
         */
        TYPE_PARAMETER,
    
        /**
         * Use of a type
         *
         * @since 1.8
         */
        TYPE_USE
    }

      @Documented --类和方法的Annotation在缺省情况下是不出现在javadoc中的。如果使用@Documented修饰该Annotation,则表示它可以出现在javadoc中。
           定义Annotation时,@Documented可有可无;若没有定义,则Annotation不会出现在javadoc中。

      @Retention---指定注解的生命周期,也就是它能存活到什么时候,我们来看看@Retention的具体定义就知道。 

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy
         */
        RetentionPolicy value();
    }

      然后我们看看RetentionPolicy  这个枚举类有哪些对象。  

    public enum RetentionPolicy {
        /**
         * Annotations are to be discarded by the compiler.
         */
        SOURCE,
    
        /**
         * Annotations are to be recorded in the class file by the compiler
         * but need not be retained by the VM at run time.  This is the default
         * behavior.
         */
        CLASS,
    
        /**
         * Annotations are to be recorded in the class file by the compiler and
         * retained by the VM at run time, so they may be read reflectively.
         *
         * @see java.lang.reflect.AnnotatedElement
         */
        RUNTIME
    }

      很清楚了,注解可以存活的生命周期有3种,编译阶段,存储在.class文件里面,还要就是到运行阶段。这里需要注意的是,只有当注解的RetentionRetentionPolicy.RUNTIME的时候,我们才可以通过反射获取方法或者类的注解信息。因为反射机制是在类加载完成以后的运行阶段发挥作用的。

      @Inherited——如果一个类的注解(比如@A)被@Inherited元注解所标注,则这个类的子类可以继承该类的这个注解@A。注意的是,这种继承是针对类的注解,如果是方法的注解,则不需要用@Inherited。子类在继承父类的方法的同时也继承了这个方法的注解,除非你去重写方法。

      回到@Resource注解,我们看到,定义一个注解需要用public @interface来修饰,并且被@Target、@Retention、@Documented(可选)、@Inherited(可选)这四个元注解所修饰。我们可以把注解看成一种特殊的类。看类内部,定义了很多方法,没有访问修饰符,没有方法体,没有参数,只有返回值类型。返回值可以带有默认值,如

       String name() default "";

            String lookup() default "";

      Class<?> type() default java.lang.Object.class;

      enum AuthenticationType { CONTAINER, APPLICATION }

      AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

      boolean shareable() default true;

      String mappedName() default ""; String description() default "";

      需要记住的是这种方法定义的格式,无参,无方法体,事实上,我们可以理解成定义了一些成员变量,只不过这些成员变量名后面都加了一个(),并且可用default关键字来赋初值。

      定义了成员变量以后,我们可以在用这个注解的时候给成员变量赋值。比如@Resource(name="baseDao")。

      ps:如果注解中只有一个成员,且叫做value,我们在传入value的值的时候,就不用指定这个成员的名称的了。比如@Target(ElementType.ANNOTATION_TYPE)就属于这种情况。

      3.就从一个注解类本身提供的信息而言,我们只能知道它的作用目标(从@Target元注解获悉),生命周期(@Retention元注解获悉),然后还能知道的是它的成员变量。除了从其他渠道(比如看源码注释)获悉这个注解的具体作用并加以应用(比如使用@Service标注一个Service类),仅从注解的定义文件你是看不出它有什么作用的。而我们对注解信息的编程也很有限,可以通过反射机制获取类,成员,或者方法的注解信息,注解的成员值等。比如下面代码。

      

    package com.xdx.learn;
    
    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.TYPE,ElementType.METHOD})
    public @interface Anno {
        String value() default "";
    }
    
    package com.xdx.learn;
    
    import java.lang.reflect.Method;
    
    @Anno("anno")
    public class AnnoTest {
        @Anno("anno")
        public void func(){
            System.out.println("hello");
        }
        public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, SecurityException{
            Class clz=Class.forName("com.xdx.learn.AnnoTest");
            //反射获取类的注解信息
            if(clz.getAnnotation(Anno.class)!=null){
                Anno anno=(Anno) clz.getAnnotation(Anno.class);
                System.out.println(anno.value());
                System.out.println(anno.annotationType());
            }
            //反射获取方法的注解信息
            Method method=clz.getDeclaredMethod("func", null);
            if(method.isAnnotationPresent(Anno.class)){
                Anno anno=method.getAnnotation(Anno.class);
                System.out.println(anno.value());
                System.out.println(anno.annotationType());
            }
        }
    
    }

      上述代码运行的结果为:

      anno
      interface com.xdx.learn.Anno
      anno
      interface com.xdx.learn.Anno

      

      

      

  • 相关阅读:
    List of Examples Converting XNA 3.1 to XNA 4.0
    XNA程序开发常用到的一些代码汇总
    在WCF中使用Flag Enumerations
    能飞过海洋的却只有海鸥【转载】
    Variant类型转换成CString代码
    一种漂亮的自绘菜单
    转 在OpenCV中用cvCalibrateCamera2进行相机标定(附程序)
    COM组件设计与应用之VC6中用ATL写组件
    vc的菜单,工具栏
    (餐饮)写一些开店的经验转
  • 原文地址:https://www.cnblogs.com/roy-blog/p/7751987.html
Copyright © 2011-2022 走看看