注解,一项在框架中与反射相辅相成的技术
注解概述
注解(Annotation),也叫元数据。一种代码级别的说明。它是 JDK1.5 及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。 注解的作用:
- 不是程序本身,可以对程序作出解释
- 可以被其他程序读取
- 作用:可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息
- 代码分析工具通过代码里标识的注解生成帮助文档
- 编译期及运行期处理注解信息
- 简化代码编写和理解
内置注解
@Override :检测被该注解标注的方法是否是继承自父类(接口)的 @Deprecated :该注解标注的内容,表示已过时 @SuppressWarnings :压制警告 一般传递参数 all ,@SuppressWarnings("all")
自定义注解
注解一般在框架中是结合反射来使用的,可通过反射机制来读取注解,实现对元数据的访问
自定义注解的方式
使用 @interface 自定义注解时,自动继承了 java.lang.annotation.Annotation 接口
public @interface MyAnnotation {
// ...
}
@interface 用来声明一个注解:格式: public @interface 注解名
其中的每一个方法实际上是声明了一个配置参数
方法的名称就是参数的名称
返回值类型就是参数的类型(返回值只能是基本类型、Class 、String 、enum )
可以通过 default 来声明参数的默认值
如果只有一个参数成员,一般参数名为 value
注解元素必须要有值,我们定义注解元素时,常使用空字符串,0 作为默认值
@MyAnnotation1
public class AnnotationTest {
@MyAnnotation1
public void test1() {
}
@MyAnnotation2("aaa") // 可以把原先的 @MyAnnotation2(value = "aaa") 省略掉 value。默认规定
public void test2() {
}
}
@Inherited
@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation1 { // 使用 @interface 自定义注解时,自动继承了 java.lang.annotation.Annotation 接口
String name() default ""; // 可以通过 default 来声明参数的默认值
int id() default -1; // 使用注解时,只要有一个属性没有默认赋值,则使用时就要去手动赋值,不然会报错
String[] schools() default {"清华大学", "北京大学"};
}
@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation2 {
/**
* 只有一个注解的情况下,建议把参数名定义成 value,这样子使用时可以省略掉这个参数
* 如果参数不用 value 作为参数名
* 如用 name,则使用起来则需要:@MyAnnotation2(name = "aaa")
*/
String value();
}
元注解
Java 定义了 4 个标准的 meta-annotation 类型,被用来提供对其他 annotation 类型做说明
这些类型和他们所支持的类,在 java.lang.annotation
包中可以找到:@Target
、@Retention
、@Documented
、@Inherited
@Target
:用于描述注解的使用范围@Retention
:表示需要在什么级别保存该注释信息,用于描述注解的生命周期SOURCE < CLASS < RUNTIME(框架都是 runtime,意思是注解要保证到了运行时还是可用的)
@Documented
:说明该注解将被包含在 javadoc 中@Inherited
:说明子类可以继承父类的注解
通过反射来获取注解信息
在框架中,我们可以通过反射来获取注解信息,然后通过注解信息来实现一些功能 比如我们现在有个需求,需要把一个实体类的信息映射到数据库中,我们可以通过注解来实现 首先我们定义两个注解,一个是表的注解,一个是字段的注解
/**
* 对应 ORM 框架的表
* Object Relationship Mapping(对象关系映射)
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableChen {
String value(); // 表名
}
/**
* 对应 ORM 框架的字段
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldChen {
String columnName(); // 字段名
String type(); // 字段类型
int length(); // 字段长度
}
然后在表对应的实体类使用这个注解,是不是就很有感觉,有一种在写 Mybatis 的感觉
@TableChen("db_teacher")
class Teacher {
@FieldChen(columnName = "teacher_id", type = "bigint", length = 255)
private int id;
@FieldChen(columnName = "teacher_name", type = "varchar", length = 255)
private String name;
@FieldChen(columnName = "teacher_name", type = "int", length = 3)
private int age;
}
然后框架底层通过反射,调用到我们实体类的注解上的信息,就取出信息对应到数据库自动连接执行操作
在这里我还没去研究过 Mybatis 源码,估计过阵子会去看看 Mybatis 的源码
Demo
在下面贴一个完整的测试代码,有需要的可以自行 copy 测试
public class Reflect05 {
public static void main(String[] args) throws NoSuchFieldException {
Class<Teacher> clazz = Teacher.class;
Class<? extends Teacher> zhangsan = new Teacher(1, "zhangsan", 30).getClass();
// 获取类上的注解(此时还没能获取 Field 上的注解)
TableChen annotation = clazz.getDeclaredAnnotation(TableChen.class);
System.out.println(annotation.value());
// 获取某个 Field 上的注解
Field field = zhangsan.getDeclaredField("name");
FieldChen fieldAnnotation = field.getDeclaredAnnotation(FieldChen.class);
System.out.println(fieldAnnotation.columnName());
}
}
// 假如有个 Teacher 表,那就要对应一个 Teacher 类
@TableChen("db_teacher")
class Teacher {
@FieldChen(columnName = "teacher_id", type = "bigint", length = 255)
private int id;
@FieldChen(columnName = "teacher_name", type = "varchar", length = 255)
private String name;
@FieldChen(columnName = "teacher_name", type = "int", length = 3)
private int age;
public Teacher() {}
public Teacher(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
}
/**
* 对应 ORM 框架的表
* Object Relationship Mapping(对象关系映射)
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableChen {
String value(); // 表名
}
/**
* 对应 ORM 框架的字段
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldChen {
String columnName(); // 字段名
String type(); // 字段类型
int length(); // 字段长度
}