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

注解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注解
package customAnnotation;

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

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; 
 
@Inherited  
@Target ({ElementType. FIELD }) 
@Retention (RetentionPolicy. RUNTIME
@Documented  
public @interface Column
    String value() default ""
3.定义使用注解的实体
package customAnnotation;

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

import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
 
public class 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(Object obj ) { 
        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 ) { 
                    Method method = obj .getClass().getMethod( methodName ); 
                    String value = (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 (Exception e ) { 
                e .printStackTrace(); 
            } 
        } 
        return sbSql .toString(); 
    } 
 
    /**
     * 检查给定的值是不是 id 类型 1.检查字段名称 2.检查字段值
     * 
     * @param target
     * @return
     */  
    public static boolean isNum(String target ) { 
        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反射,而我们平时连自定义注解都用不到

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值