前言:注解是JDK5 之后推出的特性,可修饰包、类、方法、变量等,通过在编译、加载和运行时读取其信息,可执行相应的处理。
基本原理
java注解类型实质上是一个标记,如:
@Autowired
private TestBean testBean;
@Autowired标记了testBean这个域属性,这个标记的意义就是自动注入,告诉spring这个域属性需要注入该类型的对象。
通过反射机制,可以得到相应的注解,并对注解进行定制化处理,如@Deprecated 标记过期,@Override 标记重写函数。
注解本身有无,并不影响程序的编译如@Override,但可能会影响程序的正常运行,如@Autowired,若删掉该注解,则对应域属性没有实例化的bean,产生异常。
注解组成
注解由来
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
注解不是新的东西,也是需要通过java原生语言来定义,Annotation接口声明了注解的源头。在定义注解时,仅需要在定义接口时,在interface 前加 @ 即可定义成功。@interface 实际上继承了 java.lang.annotation.Annotation 表明自己为注解类型。
注:注解本身不能再被继承,且无法通过extends java.lang.annotation.Annotation 方式声明自己的注解类型
以@Override为例,对注解的定义进行讲解。@Override 表明该方法覆盖了其父类或接口的方法。其上又包含了@Target与@Retention两个注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- @Target注解表明该注解可标注的位置,其内部接受一个ElementType类型的数组,ElementType类型如下:
public enum ElementType {
/** 标明该注解可以用于类、接口(包括注解类型)或enum声明 */
TYPE,
/** 标明该注解可以用于字段(域)声明,包括enum实例 */
FIELD,
/** 标明该注解可以用于方法声明 */
METHOD,
/** 标明该注解可以用于参数声明 */
PARAMETER,
/** 标明注解可以用于构造函数声明 */
CONSTRUCTOR,
/** 标明注解可以用于局部变量声明 */
LOCAL_VARIABLE,
/** 标明注解可以用于注解声明(应用于另一个注解上)*/
ANNOTATION_TYPE,
/** 标明注解可以用于包声明 */
PACKAGE,
/**
* 标明注解可以用于类型参数声明(1.8新加入)
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 类型使用声明(1.8新加入)
* @since 1.8
*/
TYPE_USE
}
- @Retention表明注解的生命周期,其接受RetentionPolicy枚举值,RetentionPolicy类型如下:
public enum RetentionPolicy {
/**
* 信息只保留在源码中,编译时将丢弃
*/
SOURCE,
/**
* 信息只保留在源码和 编译后的class文件中,但加载到JVM时将被丢弃。
*/
CLASS,
/**
* 信息将保留在源码、class文件以及运行时。
*/
RUNTIME
}
再看这两个注解的定义:
@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();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
可见这两个注解自身也被自身标记,并声明了自身的声明周期及作用对象。
@Target和@Retention注解中,包含了value()方法,并含有返回值。该方法表明了注解接受何种类型的属性值。如@Target接受ElementType类型的数组,该属性值将由该注解定义者来决定如何使用。
其中,注解内可定义多个方法,且多种返回类型,但由限制。
- 对于方法,若有属性值且无默认值,则必须提供该属性的值,如@Target(ElementType.ANNOTATION_TYPE)或定义默认值。
value() default ElementType.ANNOTATION_TYPE
当注解内有唯一的方法,且方法名为value() 则,在使用时,无须通过key=value形式,直接赋值即可,如@Target(ElementType.ANNOTATION_TYPE),否则需要通过@Target(value=ElementType.ANNOTATION_TYPE)赋值。
- 对于返回值,需要在以下类型范围内,且不能接受null值。
所有基本类型(int,float,boolean,byte,double,char,long,short)
String
Class
enum
Annotation
上述类型的数组
public @interface AnnotationElementDemo {
//枚举类型
enum Status {FIXED,NORMAL};
//声明枚举
Status status() default Status.FIXED;
//布尔类型
boolean showSupport() default false;
//String类型
String name()default "";
//class类型
Class<?> testCase() default Void.class;
//注解嵌套
Override reference();
//数组类型
long[] value();
}
注解信息使用
返回值 | 方法名 | 说明 |
---|---|---|
<A extends Annotation> | getAnnotation(Class<A>annotationClass) | 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。 |
Annotation[] | getAnnotations() | 返回此元素上存在的所有注解,包括从父类继承的 |
boolean | isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。 |
Annotation[] | getDeclaredAnnotations() | 返回直接存在于此元素上的所有注解,注意,不包括父类的注解,调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组 |
简单实例:
package com.zejian.annotationdemo;
import java.lang.annotation.Annotation;
import java.util.Arrays;
/**
* Created by zejian on 2017/5/20.
* Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
*/
@DocumentA
class A{ }
//继承了A类
@DocumentB
public class DocumentDemo extends A{
public static void main(String... args){
Class<?> clazz = DocumentDemo.class;
//根据指定注解类型获取该注解
DocumentA documentA=clazz.getAnnotation(DocumentA.class);
System.out.println("A:"+documentA);
//获取该元素上的所有注解,包含从父类继承
Annotation[] an= clazz.getAnnotations();
System.out.println("an:"+ Arrays.toString(an));
//获取该元素上的所有注解,但不包含继承!
Annotation[] an2=clazz.getDeclaredAnnotations();
System.out.println("an2:"+ Arrays.toString(an2));
//判断注解DocumentA是否在该元素上
boolean b=clazz.isAnnotationPresent(DocumentA.class);
System.out.println("b:"+b);
/**
* 执行结果:
A:@com.zejian.annotationdemo.DocumentA()
an:[@com.zejian.annotationdemo.DocumentA(), @com.zejian.annotationdemo.DocumentB()]
an2:@com.zejian.annotationdemo.DocumentB()
b:true
*/
}
}
Demo
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @ interface PersonalName {
String filedName() default "";
String methodName() default "";
String className() default "";
}
@PersonalName(className = "测试类")
public class TestBean {
@PersonalName(filedName = "我的名字")
public String myName= "";
@PersonalName(filedName = "你的名字")
public String yourName="";
@PersonalName(methodName = "打印A")
public void printA(){
System.out.println("A");
}
}
public class JunitTest {
private Class<?> test = TestBean.class;
@Test
public void testPrintFiled(){
for(Field field:test.getDeclaredFields()){
//getDeclaredAnnotations
Annotation[] annotations = field.getDeclaredAnnotations();
for (Annotation ann:annotations){
if(ann instanceof PersonalName){
System.out.println("类:"+test.getName()+",属性:"+field.getName()+",自定义名:"+((PersonalName) ann).filedName());
}
}
//getDeclaredAnnotation
Annotation annotationFiled = field.getAnnotation(PersonalName.class);
if(annotationFiled!=null){
System.out.println("类:"+test.getName()+",属性:"+field.getName()+",自定义名:"+((PersonalName) annotationFiled).filedName());
}
}
}
@Test
public void testMethod(){
for(Method method:test.getDeclaredMethods()){
Annotation annotationMethod = method.getAnnotation(PersonalName.class);
if(annotationMethod!=null){
System.out.println("类:"+test.getName()+",方法:"+method.getName()+",自定义名:"+((PersonalName) annotationMethod).methodName());
}
}
}
@Test
public void testClass(){
Annotation annotation = test.getAnnotation(PersonalName.class);
TestBean testBean = null;
if(annotation!=null){
System.out.println("类:"+test.getName()+",自定义名:"+((PersonalName) annotation).className());
try{
testBean = (TestBean) test.newInstance();
testBean.printA();
}catch (Exception e){
e.printStackTrace();
}
}
}
}