注解
文章目录
1. 注解语法
1.1 注解接口
modifiers @interface AnnotationName{
elementDeclaration1;
elementDeclaration2;
...
}
每个元素声明都有如下形式:
type elementName();
或者:
type elementName() default value;
所有注解接口都隐式地扩展自 java.lang.annotation.Annotation接口。这是个常规接口,不是一个注解接口。
注解元素的类型为下列之一:
- 基本类型(int, short, long, byte, char, double, float 或者 double)
- String
- Class (具有一个可选的类型参数,例如 Class<? extends MyClass>)
- enum枚举类型
- 注解类型
- 上述所述类型组成的数组
合法例子:
public @interface Example{
enum Status {A, B, C, D};
boolean isOK() default false;
String assignedTo() default "[none]";
Class<?> test() default Void.class;
Status status() default Status.A;
Reference ref() default @Reference();//这是一个注解类型
String[] report();
}
1.2 常用方法
-
Class<? extends Annotation> annotationType();
返回Class对象,用于描述该注解对象的注解接口。注意:调用注解对象上的
getClass()
方法可以返回真正的类,而不是返回接口。 -
boolean equals(Object other);
判断other是否实现了同样的注解接口,并且该对象与other的所有元素都彼此相等。
-
int hashCode()
-
String toString()
返回一个包含注解接口名以及元素值的字符串表示。例如
@Example(assignedTo=[none],isOk=false)
1.3 注解(打上注解)
每个注解都具有下面这种格式:
@AnnotationName(elementName1 = value1, elementName2 = value2, ...)
元素的顺序无关紧要。如果某个元素没有指定,将会使用声明的默认值。
简化注解:
-
标记注解:没有指定元素,不需要圆括号:
@AnnotationName
-
单值注解:只有唯一元素,且该元素具有特定的名字 value,则可以忽略元素名以及等号:
@AnnotationName("someValue")
所有注解都是由编译器计算而来的,因此所有元素值必须是编译器常量。
一个项可以有多个注解
注解可以重复声明
注解元素不能设置为 null
不允许循环依赖,例如:在注解A中有一个注解类型为B的注解元素,则B中不能再拥有一个类型为A的注解元素。
1.4 注解的声明
注解可以出现的声明处:
- 包
- 类(包括enum)
- 接口(包括注解接口)
- 方法
- 构造器
- 实例域(包括enum常量)
- 局部变量
- 参数变量
- 类型参数
对于类和接口,声明如下:
@Entity
public class User{...}
对于变量,声明如下:
@SuppressWarnings("unchecked")
List<User> users = ...;
public User getUser(@Param("id") String userId)
泛化类或者方法中的类型参数,声明可以如下:
public class Cache<@Immutable V>{...}
1.5 注解类型的用法
类型用法注解可以出现在下面的位置:
@NonNull注解可以通过静态分析工具检查参数不为空的断言是否有效
-
与泛型类型引元一起使用:
List<@NonNull String>, Comparator.<@NonNull String>reverseOrder() //这是静态泛型方法的调用方式
-
在数组中的任何位置:
@NonNull String[][] words,//表示words[i][j]不为null String @NonNull[][] words,//表示words不为null String[] @NonNull[] words //表示words[i]不为null
-
与超类和实现接口一起使用:
class Warning extends @Localized Message
-
与构造器一起使用:
new @Localized String(...)
-
与强制转型和instanceof检查一起使用:
(@Localized String)text, if(text instanceof @Localized String)
这些注解只提供给外部工具使用,对代码实际逻辑没有任何影响
-
与异常规约一起使用:
public String read() throws @Localized IOException;
-
与通配符和类型边界一起使用:
List<@Localized ? extends Message>, List<? extends @Localized Message>
2. 标准注解
2.1 一些常见的注解
-
@Override
应用场合:方法
目的:检查该方法是否覆盖了某一个父类方法
-
@Resource
应用场合:类、接口、方法、域
目的:在类或者接口上时,标记为在其他地方要用到的资源;在方法或域上时,为“注入”而标记。
-
@Target
应用场合:注解
目的:限制该注解可以应用到哪些项。
例子:
@Target({ElementType.TYPE, ElementType.METHOD}) public @interface AnnotationName
-
@Retention
应用场合:注解
目的:指明这个注解可以保留多久,或者说指明这个注解的生命周期
例子:
@Retention(RetentionPolicy.RUNTIME) public @interface ActionListener
-
@Inherited
应用场合:类的注解
目的:指明当这个注解应用于一个类的时候,能够自动被它的子类继承。
3. 自定义注解
使用@interface自定义注解时候,自动继承了 java.lang.annotation.Annotation接口
-
@interface用来声明一个注解,格式:
public @interface AnnotationName{...}
-
其中的每一个方法实际上是声明了一个配置参数,如下的value虽然看着像是方法,其实是一个叫做value的配置参数
@interface Example{ String value() default "[none]" }
-
方法的名称就是参数的名称
-
返回值类型就是参数的类型(只能是基本类型,Class,String,enum)
-
可以通过default声明参数的默认值。如果没有默认值,使用时必须要赋值。
-
如果只有一个参数成员,一般设置参数名为value,成为单值注解
-
注解元素必须要有值,不允许为null,可以使用0、空字符串。
-
注解元素之间没有顺序性。
自定义一个简单注解,例子:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
String name() default "";
int id() default -1;
int age() default 0;
String[] studentIds() default {"U1101","U1102"};
}
4. 使用反射操作注解
核心方法:
//返回类,方法或者域上的具体某个注解
field.getAnnotation(Class<?> annotationType);
method.getAnnotation(Class<?> annotationType);
cl.getAnnotation(Class<?> annotationType);
obj.getAnnotations();//返回类,方法或者域上的所有注解
场景:类与数据库的关联。
首先看一下学生类:
class Student{
privateint id;
private int age;
private String name;
public Student(){
}
public Student(int id,int age, String name){
this.id = id;
this.age= age;
this.name = name;
}
//getter setter
...
//toString
}
创建一个类名的注解:
@Target({ElementType.TYPE})//要作用在类上
@Retention(RetentionPolicy.RUNTIME)//运行时
@interface Table{
String value();
}
将Table注解作用在Student类上:
@Table("db_name")
public Student{...}
创建一个属性的注解:
@Target({ElementType.FIELD})//要作用在域上
@Retention(RetentionPolicy.RUNTIME)//运行时
@interface FieldAnnotation{
String columnName();//数据库中的列名
String type();//数据库中的类型
int length();//长度
}
作用在学生类中的各个要与数据库关联的域上:
public class Student{
@FieldAnnotation(columnName = "db_id", type = "int", length = 10)
private int id;
@FieldAnnotation(columnName = "db_age", type = "int", length = 10)
private int age;
@FieldAnnotation(columnName = "db_name", type = "varchar", length = 3)
private String name;
}
最后,通过反射来进行关联:
public static void main(String[] args) throws Exceptions{
Class cl = Class.forName("com.example.Student");
//通过反射获取注解
Annotation[] annotations = cl.getAnnotations();//获取所有注解
//获取注解的value的值
//首先获取该类中为Table.class的注解
Table tableAnnotation = (Table) cl.getAnnotation(Table.class);
//然后获取该注解中的值
String value = tableAnnotation.value();
//获取域的注解
//首先通过反射获取域
Field f = cl.getDeclaredField("name");
//在通过域,获取加在域上的注解
FieldAnnotation fieldAnnotation = (FieldAnnotation)f.getAnnotation(FieldAnnotation.class);
//获取name域上打上的注解的值
String columnName = fieldAnnotation.columnName();
String type = fieldAnnotation.type();
int length = fieldAnnotation.length();
}
我们获取到所有注解上的参数后,就可以拼接出数据库需要的语句,完成我们的目的。