想必在项目中大家一定接触过不少注解,特别是在Spring项目中,那注解的作用是什么,有何意义?
什么是注解?
注解可以理解为一个标签,这个标签可以贴在类上,方法上,成员变量上。
注解用来标识某些特征,类似的可理解为Interface接口。
public @interface Table {
String name() default "";
}
元注解
@Retention
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
我们可以这样的方式来加深理解,@Retention 去给一张标签解释的时候,它指定了这张标签张贴的时间。@Retention 相当于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Table {
String name() default "";
}
上面的代码中,我们指定 Table注解可以在程序运行周期被获取到,因此它的生命周期非常的长。
@Documented
这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。
@Target
Target 是目标的意思,@Target 指定了注解运用的地方。
你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。
类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值
- ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
- ElementType.CONSTRUCTOR 可以给构造方法进行注解
- ElementType.FIELD 可以给属性进行注解
- ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
- ElementType.METHOD 可以给方法进行注解
- ElementType.PACKAGE 可以给一个包进行注解
- ElementType.PARAMETER 可以给一个方法内的参数进行注解
- ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
@Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Table {
String name() default "";
}
@Table
class User{
}
class Member extends User{
}
@Repeatable
Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。
什么样的注解会多次应用呢?通常是注解的值可以同时取多个。比如一支笔,可写可读
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Takes {
Pen[] value() default {};
}
@Repeatable(value = Takes.class)
@interface Pen {
String method() default "";
}
@Pen(method="write")
@Pen(method="read")
class XiaoMiPen{
}
如何使用注解
接下来我们自定义三个注解,用来模拟数据库操作
分别是@Table @Column @TableId
@Documented
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Table {
String name() default "tableName";
}
@Target(value = ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Column {
String columnName() default "columnName";
}
@Target(value = ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface TableId {
String Id() default "tableId";
}
创建一个User对象对应User表
package com.test.demo;
@Table(name = "user")
public class User {
@TableId(Id = "user_id")
private String userId;
@Column(columnName = "user_name")
private String userName;
@Column(columnName = "age")
private int age;
@Column(columnName = "memo")
private String memo;
.....getset方法省略
}
利用Java反射我们可以这么做。此仅为demo,没有具体意义
User user = new User("1","张三",18,"这是个逗比");
StringBuilder sb = new StringBuilder();
Class clazz = Class.forName(user.getClass().getName());
//判断是不是对应有数据表
Annotation annotation = clazz.getAnnotation(Table.class);
if(Objects.isNull(annotation)){
System.out.println("数据库没有这张表");
}
Table aClass = (Table) annotation;
//获取表名
String tableName = aClass.name();
sb.append("select * from ").append(tableName);
// TODO: 2020/6/29 excuteSql
System.out.println(sb.toString());
//获取主键
String id = "";
for (Field declaredField : clazz.getDeclaredFields()) {
TableId column = declaredField.getAnnotation(TableId.class);
if(!Objects.isNull(column) ){
id = column.Id();
System.out.println(id);
declaredField.setAccessible(true);
//查询
sb.append(" where "+id +"=" + declaredField.get(user));
System.out.println(sb);
}
}
//获取其他字段
for (Field declaredField : clazz.getDeclaredFields()) {
Column column = declaredField.getAnnotation(Column.class);
if(!Objects.isNull(column) ){
String name = column.columnName();
System.out.println(name);
}
}