对于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 包中的注解:
对于注解我们可以理解为一个标签,本质上是一个特殊的接口,他的功能主要有如下几点: