第三节 注解的使用
3.1认识注解
Annotation ,JDK1.5新提供的技术
我们在编程中经常会使用到注解,作用有:
1)编译检查:比如@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用
2)替代配置文件:使用反射来读取注解信息
目前大部分框架(如Spring)都使用了注解简化代码并提高编码的效率(使用注解之前使用的xml进行配置)
注解其实就是代码里的特殊标记,它用于替代配置文件:传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。 注解可以标记在包、类、属性、方法,方法参数以及局部变量上,且同一个地方可以同时标记多个注解。注解可以在编译(source),类加载(class),运行时(runtime)被读取,并执行相应的处理,以便于其他工具补充信息或者进行部署
3.2内置注解
主要有三个内置注解
1.@Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
2.@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
3.@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
从 Java 7 开始,额外添加了 3 个注解:
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
Java 8 开始支持,额外添加了两个注解
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
3.3元注解
元注解是指注解的注解,在JDK 1.5中提供了4个标准的用来对注解类型进行注解的注解类。可以使用这4个元注解来对我们自定义的注解类型进行注解
1. @Retention用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime),若没有 @Retention,则默认是 RetentionPolicy.CLASS。其含有如下:
SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)
CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中)。
RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等。
2. @Target -用来约束注解可以应用的地方(如方法、类或字段),其中ElementType是枚举类型。若没有 @Target,则该 Annotation 可以用于任何地方。
3. @Documented - 标记这些注解是否包含在用户文档中。
4. @Inherited - 指示注解类型被自动继承。如果在注解类型声明中存在 Inherited 元注解,并且用户在某一类声明中查询该注解类型,同时该类声明中没有此类型的注解,则将在该类的超类中自动查询该注解类型。
3.4自定义注解
【示例11】自定义注解
package com.bjsxt.demo1;
import java.lang.annotation.*;
import java.util.Date;
import java.util.List;
/** 元注解:用于注解注解的注解* 用于帮助我们定义注解而使用的注解* @Target 控制我们自己定义的注解可以使用的位置* 如果不加该注解 意味着该注解可以使用在任意位置* @Retention 控制我们自己定义的注解的声明周期* 我们的注解在程序运行的那个阶段有效* SOURCE 源代码中 一般用于编写代码的提示* CLASS 编译后代码的处理* RUNTIME运行时可以使用** */
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Anno {
/** 注解的属性* 1注解的属性 不可以使用访问修饰符修饰默认必须是public* 2属性的语法* 单个属性语法 数据类型 属性名();* 数组属性语法 数据类型[] 属性名();* 枚举作为属性 枚举类型 属性名();* 枚举数组作为属性 枚举类型[] 属性名();* 3属性的赋值* @Anno(i=10,arr={10,20},week=Week.星期五,weeks={Week.星期一,Week.星期二})* 4属性的默认值* 数据类型 属性名() default 值;* 5赋值时可以缺省的属性名* 当注解中只有一个属性 推荐使用 value作为属性名* 当属性名是value且仅有一个属性时,属性名可以省略不写* */
String value() default "";
int i() default 1;
int[] arr() default {};
Week week() default Week.星期日;
Week[] weeks() default {Week.星期五,Week.星期四};
}
总结:
定义注解的关键字是@interface
自定义注解中可以定义多个配置参数,不是成员方法,不是成员变量;说明参数的名称,以及参数值的类型
如果只有一个配置参数,一般命名为value
如果配置参数是value,并且只有一个配置参数,value可以省略
注解一定要配套的使用读取注解的程序,否则注解是没有什么意义
注意:
定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。
和我们通常的 implements实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过 @interface 定义注解后,该注解不能继承其他注解或接口。
注解常见的API及其关系如下
3.5 反射结合注解完成ORM映射
数据库中的表格和JAVA中的实体类就是一种映射关系,好比,人和影子,地图和实际地理,用何种方式能够表达出二者的映射呢? 就是sql语句
目前大部分框架(如Spring、MyBatis、SpringMVC)都使用了注解简化代码并提高编码的效率(使用注解之前使用的xml进行配置)。
【示例12】反射结合注解完成ORM映射
准备两个注解
/** 体现类名和表格名称之间的映射关系* */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableAnno {
String value();
}
/** 体现实体类属性和数据库表格字段的映射关系* */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnno {
// 名称的对应关系 String fieldName();
// 数据类型的对应关系 String fieldType();
// 数据大小的对应关系 int length();
// 约束的对应关系 String constraint();
}
在实体类上添加注解
@SuppressWarnings("all")
@TableAnno("person")
public class Person {
@FieldAnno(fieldName ="pid",fieldType ="int",length = 6,constraint = "primary key auto_increment")
private int pid;
@FieldAnno(fieldName ="pname",fieldType ="varchar",length =5,constraint = "not null")
private String pname;
@FieldAnno(fieldName ="page",fieldType ="int",length =2,constraint = "check (page >0 and page <80)")
private int page;
}
使用反射技术编写注解读取和处理程序
public class ORMUtil {
/*** 根据类的全路径名,生成对应数据库表格的sql语句的方法* @param classname* @return*/
public static String getORMSql(String classname) throws Exception{// com.bjsxt.demo2.Person StringBuilder sbd =new StringBuilder("create table ");
//获得类的字节码 Class clazz=Class.forName(classname);// personClass TableAnno tableAnno =(TableAnno) clazz.getAnnotation(TableAnno.class);
String value = tableAnno.value();
sbd.append(value);
sbd.append(" (");
/*获取属性上的注解*/
Field[] fs = clazz.getDeclaredFields();
for (int i =0;i
Field f =fs[i];
FieldAnno fanno = f.getAnnotation(FieldAnno.class);
String fieldName = fanno.fieldName();
sbd.append(fieldName+" ");
String fieldType = fanno.fieldType();
int length = fanno.length();
sbd.append(fieldType+"("+length+") ");
String constraint = fanno.constraint();
sbd.append(constraint);
if(i
sbd.append(",");
}
}
sbd.append(")");
return sbd.toString();
}
}
测试代码
public static void main(String[] args) throws Exception {
String ormSql = ORMUtil.getORMSql("com.bjsxt.demo2.Person");
System.out.println(ormSql);
}
总结:注解一定要有配套的注解读取和处理程序,否则是没有意义的.注解就是信息的一种标志,不是功能直接实现
本节作业:
1.使用java中的内置注解
2.完成一个自定义注解
3.完成emp和数据库之间的ORM映射处理