对于java的注解, 自己已经使用了相当长的时间, spring中对注解的使用无处不在,但对它的了解并不深入, 本周对注解进行了较为深入的学习。
Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。
上面的是java注解的官方解释,相信看了以后本来明白的仍然明白,但是不明白的却还是不明白。
我感觉注解可以简单理解为是一个 标签 , 若是在类或属性等上面有什么 标签 ,我们就需要对其进行某些处理,如何处理呢?别急,后面会说。
public @interface TestAnnotion {
}
一个最简单的注解定义方式就像上面一样, 和接口很像对不对,只是多了一个 @ 符号,注解实际上是一种继承自接口 java.lang.annotation.Annotation 的特殊接口,jdk文档有如下说法
An annotation type declaration specifies a new annotation type, a special kind of interface type. To distinguish an annotation type declaration from a normal interface declaration, the keyword interface is preceded by an at-sign (@)
……
The direct superinterface of every annotation type is java.lang.annotation.Annotation.
注释类型声明指定一种新的注释类型,一种特殊的接口类型。要将注释类型声明与普通接口声明区分开来,关键字接口前面有at符号(@)
……
每个注释类型的直接上接口是java.lang.annotation.annotation。
所以上面定义的 TestAnnotion ,可以理解成这样
public interface TestAnnotion extends Annotation {
}
这篇文章 和 这篇文章
通过反编译给我们了更直观的展示这个结论。
想要自定义注解就肯定要了解元注解
一般比较常用的是ElementType.TYPE类型
Inherited的英文意思是继承,但是这个继承和我们平时理解的继承大同小异,一个被@Inherited注解了的注解修饰了一个父类,如果他的子类没有被其他注解修饰,则它的子类也继承了父类的注解。
Repeatable的英文意思是可重复的。顾名思义说明被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义。
### 实现spring data jpa的@Entity(伪)
我们知道 spring data jpa 中 如果给某个类加上 @Entity 的注解,spring就会为我们创建相应的数据表,
接下来我们就实现一个注解:他会生成创建一个数据表的sql,然后打印出来(执行也是的原理,咱就不执行了), 要实现这个功能,需要和反射相结合,若是还未学习过反射可以先通过 这篇文章 学习一下。
Entity.java
@Target(ElementType.TYPE) // 作用于类
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
}
Member.java
@Entity
public class Member {
String name;
Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
TableCreator.java
public class TableCreator {
public static void
main(String[] args) throws Exception {
// 获取Member的实例变量
Class<?> cl = Member.class;
// 查找该类上是否有相应的注解
Entity entity = cl.getAnnotation(Entity.class);
if (entity == null) {
System.out.println(
"该类没有Entity注解");
return;
}
// 获取数据库的名
String tableName = cl.getName().toLowerCase();
// 定义像对象的属性
List<String> columnDefs = new ArrayList<>();
for (Field field : cl.getDeclaredFields()) {
String columnName = field.getName().toLowerCase();
String columnDef = columnName + " varchar(50)";
columnDefs.add(columnDef);
}
// 构造sql语句
StringBuilder createCommand = new StringBuilder(
"create table " + tableName + "(");
for (String columnDef : columnDefs)
createCommand.append("/n ").append(columnDef).append(",");
String tableCreate = createCommand.substring(
0, createCommand.length() - 1) + ");";
System.out.println("生成的sql语句为:/n" + tableCreate);
}
}
像上面这种 处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool) 。
#### 注解成员变量
我们可以在注解中设置成员变量,形如
@Target(ElementType.TYPE) // 作用于类
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
// name 值必须赋值, 若不设置默认值则必须在使用处赋值
String name() default "";
}
赋值方式
同时,还有一点需要注意的是,如果你在注解中定义了名为 value 的元素,并且在使用该注解时, value 为唯一一个需要赋值的元素,你就不需要使用键—值对的语法,你只需要在括号中给出 value 元素的值即可。这可以应用于任何合法类型的元素。这也限制了你必须将元素命名为 value ,不过在上面的例子中,这样的注解语句也更易于理解:
@Target(ElementType.TYPE) // 作用于类
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
//value 为唯一一个需要赋值的元素,你就不需要使用名—值对的语法
String value();
String test() default "";
}
但若不止一个需要赋值的变量,则 value 也需要键值对的形式
直接通过获取即可
// 获取Member的实例变量
Class<?> cl = Member.class;
// 查找该类上是否有相应的注解
Entity entity = cl.getAnnotation(Entity.class);
// 获取注解中的成员变量值
String name = entity.value();
除了上面几种用于定义注解的注解外,java还为我们提供了另外五个注解,Java 5 引入了前三种定义在 java.lang 包中的注解:
对于注解我们可以理解为一个标签,本质上是一个特殊的接口,他的功能主要有如下几点: