JAVA注解的本质
- 注解本质上是一类模板。
- 注解的读取基于JAVA语言的反射。通过反射可以拿到一个类的包含的所有东西(方法、属性、注解)。
- 注解的功能实现还是以代码进行实现。注解的初始化由JAVA底层读取实现。
- 注解不一定有实际的用处。例如:@SuppressWarnings仅仅用来指示编译器去忽略注解中声明的警告。
以下为两个常见的例子:
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。防止低级错误。
- @Column - 用来映射实体类字段与数据库字段并有添加字段约束的功能。可以防止数据库字段带有下划线导致的问题。
元注解
-
元注解是用来定义注解的基本使用规范。
-
通过元注解可以进行自定义注解。例如:@Column注解的实现方式,通过反射读取实体类属性与对应注解中的参数name,再通过name拼接成SQL进行数据库查询。(实体类字段–@Column注解name值–数据库字段)
-
元注解有 @Retention、@Documented、@Target、@Inherited这四种是最基本注解;jdk1.8新增了@Repeatable注解
@Retention
作用是被修饰的注解可以保存多久,这个注解需要使用参数。这个参数的类型是RetentionPolicy,所以使用这个注解就要对value赋值。
value的值有且仅有三个:
- RetenionPolicy.CLASS 编译器把该注解记录在class文件中。当运行java程序时,JVM不可获取注解信息。这是默认值!
- RetenionPolicy.RUNTIME编译器把该注解记录在class文件中。当运行java程序时,JVM可获取注解信息,程序可以通过反射获取该注解信息。
- RetenionPolicy.SOURCE 该注解只保存在源代码中,编译器直接丢弃该注解。
@Documented
指定被修饰的注解类将被javadoc工具提取成文档,如果定义注解类时使用了这个注解修饰,则所有使用该注解修饰的程序员苏API文档将会包含该注解说明。
例如:@Documentedpublic @interface Testable{}
@Target
@Target也只能修饰一个注解定义,作用是指定被修饰的注解能用于修饰哪些程序单元,@Target也包含了一个value值,他的值只能是下面的:ElementType.
取值 | 使用范围 |
---|---|
METHOD | 可用于方法上 |
TYPE | 可用于类或者接口上 |
ANNOTATION_TYPE | 可用于注解类型上(被@interface修饰的类型) |
CONSTRUCTOR | 可用于构造方法上 |
FIELD | 可用于域上 |
LOCAL_VARIABLE | 可用于局部变量上 |
PACKAGE | 用于记录java文件的package信息 |
PARAMETER | 可用于参数上 |
@Inherited
这个注解指定被他修饰的注解将具有继承性;如果某个类使用了@Xxx,则其子类将自动被@Xxx修饰
@Repeatable
jdk1.8才有的此注解,标识某注解可以在同一个声明上使用多次。例如:对于一个字段已经有@Column注解后,无法在此字段上再次添加@Column注解,如在注解@Column的定义中加上@Repeatable注解则可以同时添加多个。
注解的使用
以@Column为例
- 注解的定义
// 作用于方法上或属性上
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name() default "";
boolean unique() default false;
boolean nullable() default true;
boolean insertable() default true;
boolean updatable() default true;
String columnDefinition() default "";
String table() default "";
int length() default 255;
int precision() default 0;
int scale() default 0;
}
- 注解的使用
// 属性上添加注解,并将name赋值为数据库字段列名
@Column(name = "user_name")
private String userName;
- 注解的实现
以下为代码片段,"…"代表省略。有兴趣可以查看底层源码包(tk.mybatis:mapper:3.4.0)
// tkMybaits底层实现@Column注解
// 1、反射获取实体字段注解,设置查询数据库对应的字段;循环调用此方法遍历实体类所有字段。
private static void processField(EntityTable entityTable, Style style, EntityField field) {
// 排除字段
if (field.isAnnotationPresent(Transient.class)) {
return;
}
......
EntityColumn entityColumn = new EntityColumn(entityTable);
......
String columnName = null;
// 判断字段是否有该注解
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
columnName = column.name();
......
}
......
// 将注解中name赋值EntityColumn对象中Column字段
entityColumn.setColumn(columnName);
......
}
// 2、拼接数据库查询SQL字段
public static String getAllColumns(Class<?> entityClass) {
Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
StringBuilder sql = new StringBuilder();
// 取EntityColumn对象中Column字段拼接要查询数据库的字段
for (EntityColumn entityColumn : columnList) {
sql.append(entityColumn.getColumn()).append(",");
}
return sql.substring(0, sql.length() - 1);
}
- 自定义注解
自定义注解本质上就是JAVA语言的一种扩展。了解了以上注解的本质,就可以写出自己的注解。以上代码还包含了对@Transient注解的处理,不妨找一下,加深理解。
参考文章:
https://baike.baidu.com/item/%E6%B3%A8%E8%A7%A3/22344968#viewPageContent
https://blog.csdn.net/qq1404510094/article/details/80577555
https://www.runoob.com/w3cnote/java-annotation.html.
https://blog.csdn.net/xsp_happyboy/article/details/80987484