java 注解 entity_Java 注解

一. 简介

注解是java5的新特性。注解可以看做一种注释或者元数据(MetaData),可以把它插入到我们的java代码中,用来描述我们的java类,从而影响java类的行为。

二. 基本语法

1. 注解的使用形式:

一个java注解由一个@符后面跟一个字符串构成,类似于这样:

@Entity

java注解中一般包含一些元素,这些元素类似于属性或者参数,可以用来设置值,比如我们有一个包含两个元素的@Entity注解:

@Entity(tableName = "vehicles", primaryKey = "id")上面注解中有两个元素,tableName和primaryKey,它们各自都被赋予了自己的元素值。

上面注解中有两个元素,tableName和primaryKey,它们各自都被赋予了自己的元素值。

2. 注解作用域:

注解可以用于描述一个类、接口、方法、方法参数、字段、局部变量等,例如

//注解一个类

@Entity

public class TestClass {

//注解一个字段

@Persistent

protected String name = null;

//注解一个方法

@Getter

public String getName() {

return this.name;

}

//注解一个参数

public void setName(@Optional name) {

this.name = name;

}

public String testLocal(String name) {

//注解一个局部变量

@Optional

List localNames = names;

...

}

}

3. 声明自定义注解:

创建自定义的注解也比较简单,只需要使用 @interface 关键字即可,如

@interface MyAnnotation {}

这样就创建了一个最简单的注解。

注意,注解是不支持继承的,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口。

4. 元注解:

所谓元注解就是标记其他注解的注解,常用的有以下几种:

1)@Target ,用来约束注解可以应用的地方,即上面提到的作用域,类、方法等。这个元注解接收的值是一个枚举类 ElementType 的数组,这个枚举类有以下值:

public enum ElementType {

/** 标明该注解可以用于类、接口(包括注解类型)或enum声明*/

TYPE,

/** 标明该注解可以用于字段(域)声明,包括enum实例 */

FIELD,

/** 标明该注解可以用于方法声明 */

METHOD,

/** 标明该注解可以用于参数声明 */

PARAMETER,

/** 标明注解可以用于构造函数声明 */

CONSTRUCTOR,

/** 标明注解可以用于局部变量声明 */

LOCAL_VARIABLE,

/** 标明注解可以用于注解声明(应用于另一个注解上)*/

ANNOTATION_TYPE,

/** 标明注解可以用于包声明 */

PACKAGE,

/** 标明注解可以用于类型参数(泛型)声明,1.8新加入 */

TYPE_PARAMETER,

/** 类型使用声明,可以用于标注任何类型,但不包括Class,1.8新加入 */

TYPE_USE

}

比如下例,声明的注解只能注解于Java 类型 和 方法

@Target({ElementType.TYPE, ElementType.METHOD})

@interface MyAnnotation {}

请注意,当注解未指定Target值时,则此注解可以用于任何元素之上。

2)@Retention ,用来规定注解的作用时机,该元注解有三种取值,

RetentionPolicy.SOURCE : 注解只存在于源码中,不会存在于.class文件中,在编译时会被忽略掉

RetentionPolicy.CLASS:注解只存在于.class文件中,在编译期有效,但是在运行期会被忽略掉,这也是默认范围

RetentionPolicy.RUNTIME:在运行期有效,JVM在运行期通过反射获得注解信息(源码、class文件和运行时都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等。

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@interface MyAnnotation {}

上例表示MyAnnotation 注解在运行时有效,JVM会在运行时通过反射机制获取注解信息。

3)@Document ,被修饰的注解会生成到javadoc中,即当使用Java 命令生成JavaDoc 时,使用@Documented元注解定义的注解将会生成到javadoc中, 而没有此元注解的注解则不会在doc文档中出现。

4)@Inherited ,可以让注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解,如

@Inherited

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.SOURCE)

@interface MyAnnotation {}

@MyAnnotation

class ParentClass {}

class ChildClass extends ParentClass {}

因为有了@Inherited 声明,所以ChildClass 也从父类ParentClass 那继承了这个注解。

5)@Repeatable ,JDK1.8新加入的,它表示在同一个位置重复相同的注解。在没有该注解前,一般是无法在同一个类型上使用相同的注解的。

//Java8前无法这样使用

@FilterPath("/web/update")

@FilterPath("/web/add")

public class A {}

下例显示了如何使用@Repeatable 元注解

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Repeatable(FilterPaths.class) //参数指明接收的注解class

public @interface FilterPath {

String value();

}

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@interface FilterPaths {

FilterPath[] value(); // 接收的注解必须声明的元素

}

通过使用@Repeatable后,将使用@FilterPaths注解作为接收同一个类型上重复注解的容器,而每个@FilterPath则负责保存指定的路径串。

为了处理上述的新增注解,Java8还在AnnotatedElement接口新增了getDeclaredAnnotationsByType() 和 getAnnotationsByType()两个方法并在接口给出了默认实现,在指定@Repeatable的注解时,可以通过这两个方法获取到注解相关信息。

注意,旧版API中的getDeclaredAnnotation() 和 getAnnotation() 是不对@Repeatable注解的处理的(除非该注解没有在同一个声明上重复出现)。

注意,getDeclaredAnnotationsByType方法获取到的注解不包括父类,其实当 getAnnotationsByType()方法调用时,其内部先执行了getDeclaredAnnotationsByType方法,只有当前类不存在指定注解时,getAnnotationsByType()才会继续从其父类寻找,但请注意如果@FilterPath和@FilterPaths没有使用了@Inherited的话,仍然无法获取。

5. 注解元素及其数据值:

在自定义注解中,一般都会包含一些元素以表示某些值,以便后续处理注解时使用。注解中的每个元素定义类似于接口中的方法定义,每个元素定义包含一个数据类型和名称。

@interface MyAnnotation {

String name();

int age();

}

// 使用注解,并给注解元素赋值

@MyAnnotation(name = "whx", age = 18)

public class AnnotationTest {

}

注解中的元素可以设置默认值,通过default 来实现

@interface MyAnnotation {

String name() default "whx";

int age() default 18;

}

当一个元素被设置默认值之后,这个元素便成了注解的可选元素,即在使用注解时如果不为这个元素赋值,将使用默认值。

编译器对元素的默认值有一些限制,首先,元素不能有不确定的值,也就是说,元素必须要么具有默认值,要么在使用注解时提供元素的值。其次,对于非基本类型的元素,无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值,因为每个注解的声明中,所有的元素都存在,并且都具有相应的值。

注解元素的数据类型:

所有基本类型(int,float,boolean,byte,double,char,long,short)

String

Class

enum

Annotation

上述类型的数组

倘若使用了其他数据类型,编译器将会丢出一个编译错误,注意,声明注解元素时可以使用基本类型但不允许使用任何包装类型,同时还应该注意到注解也可以作为元素的类型,也就是嵌套注解,如下例

@interface MyAnnotation {

enum Gender {MALE, FEMALE}

// 枚举类型

Gender status() default Gender.MALE;

// 常用类型

String name() default "whx";

int age() default 18;

// Class 类型

Class> testCase() default Void.class;

// 注解嵌套

Reference reference() default @Reference(next = true);

// 数组类型

long[] value() default {1, 2, 3};

}

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@interface Reference {

boolean next() default false;

}

快捷方式:

就是注解中定义了名为value 的元素,并且在使用该注解时,如果该元素是唯一需要赋值的一个元素,那么此时无需使用key=value的语法,而只需在括号内给出value元素所需的值即可。这可以应用于任何合法类型的元素,但是,这限制了元素名必须为value 。

@interface QuickWay {

String value();

}

@QuickWay("hello world")

class QuickTest {

}

6. 常用Java 内置注解:

主要有三个:

@Override:用于标明此方法覆盖了父类的方法, 当我们在子类中覆盖父类的方法时,就要用到@Override注解,这样,如果父类中的方法名称或参数发生改变时,如果子类没有做相应的调整编译器便会报错,这就是@Override注解所起到的作用。

@Deprecated:用于标明已经过时的类、方法、属性、方法参数等,被标记为@Deprecated 的类、方法等在编程中不建议使用,因为后来版本的API 可能将这些过期的去掉。

@SuppressWarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。

三. 注解与反射机制

上面对注解做了一个详细介绍,具体该如何使用我们的自定义注解呢?其实在现实应用中,我们的自定义注解一般都是起到运行时指示的作用,也就是运行时注解。对于运行时注解,我们可以通过反射机制获得注解信息。

Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息,如反射包的Constructor类、Field类、Method类、Package类和Class类都实现了AnnotatedElement接口。

下面是AnnotatedElement中相关的API方法:

返回值

方法名称

说明

getAnnotation(Class annotationClass)

该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。

Annotation[]

getAnnotations()

返回此元素上存在的所有注解,包括从父类继承的

boolean

isAnnotationPresent(Class extends Annotation> annotationClass)

如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。

Annotation[]

getDeclaredAnnotations()

返回直接存在于此元素上的所有注解,注意,不包括父类的注解,调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组

下面来看几个示例:

class ATest {

public static void main(String[] args) {

Class> clazz = ChildClass.class;

// 判断类上是否有指定的注解

boolean b = clazz.isAnnotationPresent(CommonAnno.class);

// 根据指定注解类型获取该注解

ClassAnno classAnno = clazz.getAnnotation(ClassAnno.class);

System.out.println(classAnno.name());

// 获取该类上的所有注解,包括从父类继承

Annotation[] anos = clazz.getAnnotations();

System.out.println(Arrays.toString(anos));

// 获取该类上的所有注解,不包括从父类继承

Annotation[] anos2 = clazz.getDeclaredAnnotations();

System.out.println(Arrays.toString(anos2));

try {

// 获取方法上的注解

Method method = clazz.getMethod("add", int.class, int.class);

Annotation[] ans = method.getAnnotations();

System.out.println(Arrays.toString(ans));

// 获取方法参数的注解

Annotation[][] paramAnnos = method.getParameterAnnotations();

Class[] paramTypes = method.getParameterTypes();

int i = 0;

for (Annotation[] annotations : paramAnnos) {

Class paramType = paramTypes[i++];

for (Annotation anno : annotations) {

if (anno instanceof ParamAnno) {

ParamAnno paramAnno = (ParamAnno) anno;

System.out.println(paramType.getName());

System.out.println(paramAnno.name());

}

}

}

// 获取属性的注解

Field field = clazz.getField("dep");

FiledAnno anno = field.getAnnotation(FiledAnno.class);

} catch (NoSuchMethodException e) {

e.printStackTrace();

}

}

}

上面只是一些简单的示例,可以通过结合注解和反射实现一些复杂的功能,比如Spring 框架的基于注解的配置等。

你可能感兴趣的:(java,注解,entity)