深入理解Java注解(含案例分析)

来源:互联网 发布:淘宝店铺套现处罚 编辑:程序博客网 时间:2024/05/21 07:50
注解annotation的理解:

    用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。

    什么是元数据:描述数据的数据,更通俗点就是描述代码间关系,或者代码与其他资源之间内在联系的数据。对于struts来说就是struts-config.xml.

    Annotation是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。

    Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。是描述元数据的一种工具。

举个最简单的例子:
    @Override
    public String toString() {
        return "This is String Representation of current object.";
    }
@Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。

为何引入注解?

   使用Annotation之前(甚至在使用之后),XML被广泛的应用于描述元数据。

   XML的不便之处就是与被描述的文件分离,不便于维护,注解克服了这一缺点。另一个很重要的因素是Annotation定义了一种标准的描述元数据的方式。

   假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,开发人员也必须认识到这点。

实现过程:

    Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。
    注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。

    注解是通过@interface关键字定义出来的,和接口有一点点类似。可以自己定义注解。
    预处理,注解的技术是预处理器。
    程序可以利用java的反射机制来了解你的类及各种元素上有无何种标记,针对不同的标记,就去做相应的事件。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。

元注解:
指注解的注解。用来注解其他注解。包括4种:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited。
   比如:
    @Target(value=METHOD)
    @Retention(value=SOURCE)
    public @interface Override{....}

四种元注解的详细解释:

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

  @Retention:

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

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

  取值(RetentionPoicy)有:

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

       @Documented:

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

  @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类型被发现,或者到达类继承结构的顶层。

自定义注解:

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

  定义注解格式:
  public @interface 注解名 {定义体}
自定义注解应用实例:
目录结构:

1.自定义Table注解
packagecustomAnnotation;

importjava.lang.annotation.Documented; 
importjava.lang.annotation.ElementType; 
importjava.lang.annotation.Inherited; 
importjava.lang.annotation.Retention; 
importjava.lang.annotation.RetentionPolicy; 
importjava.lang.annotation.Target; 
 
@Inherited 
@Target({ElementType.TYPE}) //用于描述类、接口(包括注解类型) 或 enum声明
@Retention(RetentionPolicy.RUNTIME// 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented //说明该注解将被包含在 javadoc
public@interfaceTable
    String value() default ""
2.自定义Column注解
packagecustomAnnotation;

importjava.lang.annotation.Documented; 
importjava.lang.annotation.ElementType; 
importjava.lang.annotation.Inherited; 
importjava.lang.annotation.Retention; 
importjava.lang.annotation.RetentionPolicy; 
importjava.lang.annotation.Target; 
 
@Inherited 
@Target({ElementType.FIELD}) 
@Retention(RetentionPolicy.RUNTIME
@Documented 
public@interfaceColumn
    String value() default ""
3.定义使用注解的实体
packagecustomAnnotation;

@Table("tb_test"
publicclass TestDto { 
     
    @Deprecated 
    private String tt
     
    @Column("_id"
    private String id
     
    @Column("username"
    private String name
     
    public TestDto(Stringid, Stringname) { 
        super(); 
        this.id= id
        this.name= name
    }  
    public String getId() { 
        return id
    }  
    public void setId(Stringid) { 
        this.id= id
    }  
    public String getName() { 
        return name
    }  
    public void setName(Stringname) { 
        this.name= name
    }  
4.测试注解
packagecustomAnnotation;

importjava.lang.reflect.Field; 
importjava.lang.reflect.Method; 
 
publicclass Test { 
    public static void main(String[]args) { 
        TestDto testDto = new TestDto( "123", "34"); 
        TestDto testDto1 = new TestDto( "123", "test1"); 
        TestDto testDto2 = new TestDto( "", "test1,test2,test3,test4" ); 
        String sql = assembleSqlFromObj(testDto); 
        String sql1 = assembleSqlFromObj(testDto1); 
        String sql2 = assembleSqlFromObj(testDto2); 
        System. out.println(sql); 
        System. out.println(sql1); 
        System. out.println(sql2); 
    } 
 
    /**
     * 通过注解来组装查询条件,生成查询语句
     * 
     *@param obj
     *@return
     */ 
    public static String assembleSqlFromObj(Objectobj) { 
        Table table = obj.getClass().getAnnotation(Table.class); 
        //通过对象,获取该对象的类的相关信息(类的相关信息包括:①用了什么注解 ②类的名称 ③各成员变量的名称等。
        //本例中是通过对象testDto、testDto1、testDto2获取类的名为“Table”的注解)
        //这里就用到了java反射
        StringBuffer sbSql = new StringBuffer(); 
        String tableName = table.value();  //获得该对象的类的Table注解所表示的表名
        sbSql.append("select * from "+ tableName + " where 1=1 "); 
        Field[] fileds = obj.getClass().getDeclaredFields(); //反射的方法,获取类的各个字段(成员变量)
        for (Field f : fileds) { 
            String fieldName = f.getName(); 
            String methodName = "get" + fieldName.substring(0, 1).toUpperCase() 
                    +fieldName.substring(1); 
            try
                Column column = f.getAnnotation(Column.class); 
                if ( column != null) { 
                    Methodmethod = obj.getClass().getMethod(methodName); 
                    Stringvalue = (String) method.invoke(obj); 
                    if ( value != null && ! value.equals( "")) { 
                       if (! isNum(column.value()) && !isNum(value)) { 
                           // 判断参数是不是 in 类型参数 1,2,3 
                           if ( value.contains(",")) { 
                               sbSql.append(" and " + column.value() +" in (" + value + ") "); 
                            }else
                               sbSql.append(" and " + column.value() +" like '%" + value + "%' "); 
                            } 
                        }else
                           sbSql.append(" and " + column.value() +"=" + value + " "); 
                        } 
                    } 
                } 
            } catch (Exceptione) { 
                e.printStackTrace(); 
            } 
        } 
        return sbSql.toString(); 
    } 
 
    /**
     * 检查给定的值是不是 id 类型 1.检查字段名称 2.检查字段值
     * 
     *@param target
     *@return
     */ 
    public static boolean isNum(Stringtarget) { 
        boolean isNum = false
        if (target.toLowerCase().contains("id")) { 
            isNum = true
        } 
        if (target.matches("\\d+")) { 
            isNum = true
        } 
        return isNum
    } 
5.运行结果

该例子解析:
     1.本例子是编写了一个简单的类似于hibernate的框架,框架的作用是,通过对象的操作的方式,替代写sql语句。
     2.本例用到了映射机制,什么是java映射?我理解就是:通过类的对象,获取该对象的类的相关信息。obj.getClass().getAnnotation(Table.class)
     3.注解起到配置文件的作用:
  1. @Table("tb_test")  
  2. public class TestDto { }
意思是,我想将TestDto类和tb_test表绑定起来

       首先new了一个TestDto对象如testDto1,然后进入我的框架(说白了就是文中的assembleSqlFromObj方法)对testDto1进行处理,通过testDto1获取该对象的类即TestDto.Class,然后获取该类的名为“Table”的注解,获取注解类的成员变量(即value)的值,即“tb_test”。


总结:

1.java的反射机制就是,通过类的对象,获取该对象的类的相关信息。类的相关信息包括:①用了什么注解 ②类的名称 ③各成员变量的名称等。

2.什么时候用到注解?我们一般用不到自定义注解,我们平时别人开发好的框架如Hibernate、spring、Struts等时,只需要按照框架的规则在代码里定义注解即可,而不会在代码里调用和操作注解。只有在自己动手写类似于Hibernate框架的时候,会用到自定义注解。这也是为什么我们一般用不到java反射,因为只有在使用自定义注解的时候,才会用到java反射,而我们平时连自定义注解都用不到

参考:
http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html

http://blog.csdn.net/ideality_hunter/article/details/53128238