zoukankan      html  css  js  c++  java
  • Java中的Annotation (二、自定义Annotation)

    今天学习如何开发一个自定义的Annotation。要想使Annotation有意义,还需要借用前几天学习的反射机制。

    下面就开始今天的学习吧。

    Annotation的定义格式。它类似于新创建一个接口类文件,但为了区分,我们需要将它声明为 @interface

    public @interface Annotation名称{

      数据类型 变量名称();

    }

    下面声明了一个Annotation

    public @interface MyAnnotation {
        
    }

    使用这个Annotation

    @MyAnnotation
    public class AnnotationDemo{
    
        public static void main(String []args){
            
        }
    }

    注意上述Annotation没有参数,这样是没有任何意义的。要让它有意义,需要借用java的反射机制。

    有内容的Annotation

    public @interface MyAnnotation {
        public String value();
    }

    在定义Annotation的时候使用了参数,就必须在使用其的时候清楚的指明变量的内容。

    @MyAnnotation("Annotation")
    public class AnnotationDemo{
        public static void main(String []args){       
        }
    }

    或者明确赋给value

    @MyAnnotation(value="Annotation")
    public class AnnotationDemo{
        public static void main(String []args){        
        }
    }

    可以设置一个参数,也可以设置多个参数。

    public @interface MyAnnotation {
        public String key();
        public String value();
    }

    在使用的时候,传入要接收的参数。

    @MyAnnotation(key="key",value="Annotation")
    public class AnnotationDemo{
        public static void main(String []args){        
        }
    }

    也可为一个参数传递多个值。

    public String[] value(); 接收的时候以数组形式接收。 value={"Annotation","java"}

    相信大家现在都跟我一样,很好奇这个自定义的Annotation里面并没有定义私有域value, public String[] value() 是一个函数,那么它是怎么接收值的呢。带着这个疑问继续学习。

    上述Annotation都必须在使用的时候,给定参数。那么能不能设置默认参数呢,使用的时候使用默认值。答案是肯定的。

    public @interface MyAnnotation {
        public String key() default "java";               //指定好了默认值
        public String[] value() default "Annotation";    //指定好了默认值
    }

    在操作中,有时会对Annotation中的参数固定其取值范围,只能取固定的几个值,这个时候,就需要java中的枚举。

    public @interface MyAnnotation {
        public Color key() default Color.BLACK;               //指定好了默认值
        public Color[] value() default Color.BLUE;            //指定好了默认值
        
        //在外部使用Annotation的时候,也需要固定枚举中的值
        public enum Color{
            RED , BLUE , BLACK
        }
    }

    外部

    //在外部使用Annotation的时候,也需要使用枚举中的值
    @MyAnnotation(key=Color.RED)
    public class AnnotationDemo{
        public static void main(String []args){        
        }
    }


    上述是对自定义注释的简单定义与使用。

    在Annotation中,可以使用Retention定义一个Annotation的保存范围。

    @Documented
    @Retention(value=RetentionPolicy.RUNTIME)
    @Target(value=ElementType.ANNOTATION_TYPE)
    public @interface MyAnnotation {
        public Color key() default Color.BLACK;               //指定好了默认值
        public Color[] value() default Color.BLUE;            //指定好了默认值
        
        //在外部使用Annotation的时候,也需要固定枚举中的值
        public enum Color{
            RED , BLUE , BLACK
        }
    }

    1、@Documented     类和方法的Annotation缺省情况下是不出现在javadoc中的,为了加入这个性质使用@Documented

    2、@Retention

    Annotation的作用范围通过RetentionPolicy来指定,RetentionPolicy有三个范围:

    RetentionPolicy.SOURCE     // 此Annotation类型的信息只会保存在程序原文件之中(*.java),编译之后不会保存在编译好的文件中(*.class)

    RetentionPolicy.CLASS       //  此Annotation类型的信息会保存在程序原文件之中(*.java),编译之后会保存在编译好的文件中(*.class),但在执行的时候这些Annotation信息将不会加载到JVM虚拟机中,如果                一个Annotation没有指定范围,则默认是此范围

    RetentionPolicy.RUNTIME    //顾名思义了,这些Annotation类型信息将会一直保存到执行时,加载到JVM虚拟中。

    在这三个范围中,我们最需要关心的就是 RetentionPolicy.RUNTIME了,因为它会在执行的时候起作用。

    那么前面文章中提到的系统内建的三种Annotation都是什么作用范围呢。

    @Override                   定义的时候采用的是@Retention(value=RetentionPolicy.SOURCE)

    @Deprecated               定义的时候采用的是@Retention(value=RetentionPolicy.RUNTIME)

    @SuppressWarnings      定义的时候采用的是@Retention(value=RetentionPolicy.SOURCE)

    3、@Target

    @Target注解表明某个注解应用在哪些目标上,可选择如下范围,根据因英文名,相信大家都能看懂。

    ElementType.TYPE (class, interface, enum)

    ElementType.FIELD (instance variable)

    ElementType.METHOD

    ElementType.PARAMETER

    ElementType.CONSTRUCTOR

    ElementType.LOCAL_VARIABLE

    ElementType.ANNOTATION_TYPE (应用于另一个注解上)

    ElementType.PACKAGE

    4、@Inherited

    @Inherited 表明是否一个使用某个annotation的父类可以让此annotation应用于子类。

    @Inheriated注解仅在存在继承关系的上产生效果,在接口和实现类上并不工作。这条同样也适用在方法,变量,包等等。只有类才和这个注解连用。

    一条关于@Inheriated注解的很好的解释在Javadoc中

    http://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Inherited.html

    示例代码如下,定义@Inherited

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Documented
    @Retention(value=RetentionPolicy.RUNTIME)            //必须指定为RUNTIME,否则不起作用
    @Target({ElementType.TYPE,ElementType.METHOD,
        ElementType.CONSTRUCTOR,ElementType.ANNOTATION_TYPE,
        ElementType.PACKAGE})
    @Inherited
    public @interface MyAnnotation {
        public String key();               
        public String value();                
    }

    在Stu子类中依然可取得父类中定义的注释信息。

    interface A{    
        public String sayHello();
    }
    
    @MyAnnotation(key="key",value="value")
    class Per implements A{
            
        public String sayHello(){
            return "hello world";
        }
    }
    
    class Stu extends Per{
        public String sayHello(){
            return "hello world";
        }
    }
    
    public class AnnotationDemo{
        
        public static void main(String []args){        
            Class <?> c = null ;        
            c = new Stu().getClass() ;
            
            if(c.isAnnotationPresent(MyAnnotation.class)){  // 判断是否是指定的Annotation
                MyAnnotation mda = null ;
                mda = c.getAnnotation(MyAnnotation.class) ;    // 得到指定的Annotation
                String key = mda.key() ;                        // 取出设置的key
                String value = mda.value() ;                    // 取出设置的value
                System.out.println("key = " + key) ;
                System.out.println("value = " + value) ;
            }            
        }
    }

    下面就是我最关心的了,如何利用反射在运行时候获得注释信息。

    反射与Annotation

    一个Annotation要想变得有意义,就需要结合反射机制。在Class类中,有如下与Annotation相关的方法。

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass)       //如果一个元素存在注释,则取得其全部注释

    public Annotation[] getAnnotations()                                                               //返回此元素上的所有注释

    public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)  //返回直接存放在此元素上的所有注释

    public boolean isAnnotation()                              //判断元素是否表示一个注释

    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)     //判断一个元素上是否存在注释

    等等……

    下面通过代码演示如何通过反射取得Annotation

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    
    
    interface A{
        public String sayHello();
    }
    class Per implements A{
        @SuppressWarnings("unchecked") 
        @Override
        @Deprecated                        //只有它在运行时有效
        public String sayHello(){
            return "hello world";
        }
    }
    
    public class AnnotationDemo{
        public static void main(String []args){        
            Class <?> c = null ;        
            c = new Per().getClass() ;
            Method toM = null;
            try {
                toM = c.getMethod("sayHello") ; // 找到sayHello()方法
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }    
            Annotation an[] = toM.getAnnotations() ;    // 取得全部的Annotation
            for(Annotation a:an){                        // 使用 foreach输出
                System.out.println(a) ;
            }
            
        }
    }

    输出结果为@java.lang.Deprecated(),这显而易见,前面已经提到了,只有@Deprecated 在运行时有效

    所以要想使我们自定义的Annotation在运行时有效,必须在定义的时候指定RetentionPolicy.RUNTIME。

    如何取得定义的注释信息中的值呢。

    在定义的时候

    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;
    
    @Documented
    @Retention(value=RetentionPolicy.RUNTIME)            //必须指定为RUNTIME,否则不起作用
    @Target(value=ElementType.METHOD)
    public @interface MyAnnotation {
        public String key();               
        public String value();                
    }

    取出注释信息中的值

    import java.lang.reflect.Method;
    
    interface A{
        public String sayHello();
    }
    class Per implements A{
        @MyAnnotation(key="key",value="value")
        public String sayHello(){
            return "hello world";
        }
    }
    
    public class AnnotationDemo{
        public static void main(String []args){        
            Class <?> c = null ;        
            c = new Per().getClass() ;
            Method toM = null;
            try {
                toM = c.getMethod("sayHello");
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }    
            if(toM.isAnnotationPresent(MyAnnotation.class)){    // 判断是否是指定的Annotation
                MyAnnotation mda = null ;
                mda = toM.getAnnotation(MyAnnotation.class) ;   // 得到指定的Annotation
                String key = mda.key() ;                        // 取出设置的key
                String value = mda.value() ;                    // 取出设置的value
                System.out.println("key = " + key) ;
                System.out.println("value = " + value) ;
            }
        
            
        }
    }

    通过以上代码就可以取出在注释信息中的值。

    下面简单

    展示一些知名类库是如何利用注解的。一些类库如:JAXB, Spring Framework, Findbugs, Log4j, Hibernate, Junit。它们使用注解来完成代码质量分析,单元测试,XML解析,依赖注入和许多其它的工作。

    1、Hibernate ORM

    Hibernate可能是一个用得最广泛的对象关系映射类库。它提供了对象模型和关系型数据库的映射框架。使用注解作为设计的一部分。

    下面的代码段使用了@Entity和@Table。这两个是用来向消费器(Hibernate处理程序)说明被注解的类是一个实体类,以及它映射的SQL表名。实际上,这个注解仅仅是指明了主表,还可以有说明字表的注解。

    @Entity
    @Table( name = "hibernate_annotated" )
    public class HibernateAnnotated

    接下来的代码段展示了如何向Hibernate处理程序说明被标记的元素是表的主键,并映射名为“id”的列,并且主键是自动生成的。

    @Id
    @GeneratedValue
    @Column( name = "id" )
    private int id;

    为了指定标准的SQL表列名,我们可以写如下注解:

    @Column( name = "description" )
    private String description;

    这说明被标记的元素映射的是这个类所对应的表中名为“description”的一列。

    2、Spring MVC

    Spring是个被广泛使用的Java企业级应用框架。其一项重要的特性就是在Java程序使用依赖注入。

    Spring使用注解作为XML(Spring的早期版本使用的基于XML配置)的一种替代方式。现在两者都是可行的。你可以使用XML文件或者注解配置你的项目。在我看来两者都各有优势。

    我们将在下例中展示两个可用注解:

    @Component
    public class DependencyInjectionAnnotation
    {
     
     private String description;
     
     public String getDescription()
     {
     return description;
     }
     
     @Autowired
     public void setDescription( String description )
     {
     this.description = description;
     }
     
    }

    代码片段中我们可以找到两个使用在了类和方法上的注解。

    @Component      //说明被标记的元素,在本例中是一个类,是一个自动检测的目标。这意味着被注解的类,将会被Spring容器实例化并管理。

    @AutowiredSpring    //容器将会尝试通过类型(这是一种元素匹配机制)使用这个set方法来自动装配。此注解也可以使用在构造器和属性上,Spring也会根据注解的地方不同采取不同的操作。

    更多关于依赖注入和Spring框架的细节请参考:http://projects.spring.io/spring-framework/ 

    总结:

    Annotation在使用中,肯定是结合反射,设定一些内容到方法中去,以完成特定的功能。

    资料:

    官方Java注解地址http://docs.oracle.com/javase/tutorial/java/annotations/

    注解API:http://docs.oracle.com/javase/8/docs/api/java/lang/annotation/package-summary.html

  • 相关阅读:
    ASP.NET 自制时间控件
    ORACLE 函数汇总之单记录函数
    Servers IIS 重启命令
    ASP.NET 两个Dropdownlist控件联动
    ASP.NET datagridview控件表头定义
    python Image 安装
    ssh 不需要密码的链接
    [Redis] redis 相关的博客
    [emacs] python代码折叠
    linux python 链接 oracle
  • 原文地址:https://www.cnblogs.com/maydayit/p/4237277.html
Copyright © 2011-2022 走看看