天天看点

Java注解分析以及自定义注解

注解对于开发人员来讲既熟悉又陌生,熟悉是因为只要你是做开发,都会用到注解(常见的@Override);陌生是因为即使不使用注解也照常能够进行开发;注解不是必须的,但了解注解有助于我们深入理解某些第三方框架(比如Spring,Hibernate,Mybatis等),提高工作效率。

Java注解又称为标注,是Java从1.5开始支持加入源码的特殊语法元数据;Java中的类、方法、变量、参数、包都可以被注解。这里提到的元数据是描述数据的数据

1. 注解作用

格式检查:      告诉编译器信息,比如被@Override标记的方法如果不是父类的某个方法,IDE会报错;

减少配置:      运行时动态处理,得到注解信息,实现代替配置文件的功能;

减少重复工作:比如第三方框架xUtils,通过注解@ViewInject减少对findViewById的调用,

                        类似的还有(JUnit、ActiveAndroid等);

2. 注解分三类

1:JDK自带的注解(Java目前只内置了三种标准注解:@Override、@Deprecated、@SuppressWarnings,以及四种元注解:@Target、@Retention、@Documented、@Inherited)

2:第三方的注解——这一类注解是我们接触最多和作用最大的一类

3:自定义注解——也可以看作是我们编写的注解,其他的都是他人编写注解

3. 自定义注解

JVM5.0定义了4个标准的元注解:

  • @Target,
  • @Retention,
  • @Documented
  • @Inherited

    3.1  @Target

    作用:用于描述注解的使用范围

    取值ElementType有:

  • CONSTRUCTOR:用于描述构造器
  • FIELD:用于描述域
  • LOCAL_VARIABLE:用于描述局部变量
  • METHOD:用于描述方法
  • PACKAGE:用于描述包
  • PARAMETER:用于描述参数
  • TYPE:用于描述类、接口(包括注解类型) 或enum声明

    举例

@Target(ElementType.TYPE)
public @interface Table {
    /**
     * 数据表名称注解,默认值为类名称
     * @return
     */
    public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {

}
           

注解Table可以用于注解类、接口(包括注解类型)或enum声明,而注解NoDBColumn仅用于注解类的成员变量。   

    3.2  @Retention

    作用:用于描述注解的生命周期 ,取值RetentionPolicy有:

    SOURCE:在源文件中有效(即源文件保留)

    CLASS:在class文件中有效(即class保留)

    RUNTIME:在运行时有效(即运行时保留)

    举例:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}
           

    Column注解的的RetentionPolicy的属性值是RUNTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理

    3.3 Documented

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

举例:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}
           

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

实例代码:

@Inherited
public @interface Greeting {
    public enum FontColor{ BULE,RED,GREEN};
    String name();
    FontColor fontColor() default FontColor.GREEN;
}
           

    3.5 自定义注解

    1. 使用interface自定义注解,自动继承java.lang.annotation.Annotation接口。

    2. 不能继承其他的注解或接口。

    3. 每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(只能是基本类型、Class、String、enum)。可以通过default声明参数的默认值。

    定义注解格式:

    public @interface 注解名(定义体)

    注解参数的可支持数据类型:

    所有基本数据类型(int,float,boolean,byte,double,char,long,short)

        1. String类型

        2. Class类型

        3. enum类型

        4.Annotation类型

    以上所有类型的数组

    3.6  代码实践

    注解类代码

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到  
@Target({ElementType.FIELD,ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法  
@Documented//说明该注解将被包含在javadoc中 
public @interface FieldAnnotation {
	String name() default "哈哈";
}
           

    业务类

public class Student {
	@FieldAnnotation(name="牛逼")
	private String name;
	private String id;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
}
           

测试类

public class StudentTest {

	public static void main(String[] args) {
		Field[] declaredFields = Student.class.getDeclaredFields();
	    for (Field f : declaredFields) {
	    	System.out.println(f.isAnnotationPresent(FieldAnnotation.class));
	    	if (f.isAnnotationPresent(FieldAnnotation.class)) {
	    		//获得注解的对象
	    		FieldAnnotation fa = f.getAnnotation(FieldAnnotation.class);
	    		if (fa != null) {
	    			System.out.println(fa.name());
	    		}
	    	}
	    }
	}
}