本部分内容参考自:《Thinking in Java》
基本语法
注解(也被称为元数据),为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。 注解是众多引入到Java SE5中的重要的语言变化之一。它们可以提供用来完整的描述程序所需的信息,而这些信息是无法用Java来表达的。因此,注解使得我们能够以一种可以被编译器测试和验证的格式存储程序的额外信息(Thinking In Java的英文版是这样描述的:Thus, annotations allow you to store extra information about your program in a format that is tested and verified by the compiler)。 通过使用注解,可以将这些元数据保存在Java源代码中,并利用annotation API为自己的注解构造处理工具。 Java在java.lang包中内置了三种基本的注解:
@Override@Deprecated@SuppressWarning
定义注解
注解的定义看起来很像接口的定义,而且注解也将会被编译成class文件。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @
interface Test {}
除了@符号外,Test的定义就像是一个空的接口。 在定义注解时,需要一些元注解(meta-annotation),如@Target和@Retention。
@Target用来定义你的注解将应用于什么地方(例如是一个方法或是一个域);@Rectetion用来定义该注解在哪一个级别可用,在源代码中(SOURCE)、类文件中(CLASS)、或者运行时(RUNTIME)。
标准注解和元注解
Java目前内置了三种标准注解和四种元注解
标准注解描述
@Override表示当前的方法定义将覆盖超类中的方法@Deprecated如果程序员使用了注解为它的元素,那么编译器会发出警告信息@SuppressWarnings关闭不当的编译器警告信息
元注解描述
@Target表示该注解可以用于什么地方,可能的ElementType参数包括:CONSTRUCTOR:构造器的声明;Field:域声明;LOCAL_VARIABLE:局部变量声明;METHOD:方法声明;PACKAGE:包声明;PARAMETER:参数声明;TYPE:类、接口(包括注解类型)和enum声明@Retention表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:SOURCE:注解将被编译器丢弃; CLASS:注解在class文件中可用,但会被VM丢弃;RUNTIME:vm将在运行期也保留注解,因此可以通过反射机制读取注解的信息。@Documented将此注解包含在Javadoc中。@Inherited允许子类继承父类中的注解
编写注解处理器
注解元素可用的类型如下: 所有基本类型(int, float, boolean等), String, Class, enum, Annotation, 以及以上类型的数组。 如果使用其它类型,编译器就会报错。
例1: 利用注解处理器读取PasswordUtils类,并使用反射机制查找@UseCase标记。为注解处理器提供一组id值,并列出PasswordUtils中找到的用例和缺失的用例。
该例用到了三个资源文件:PasswordUtils.java, UseCase.java, UseCaseTracker.java. 标签@UseCase是由UseCase.java定义的。其中包含int元素id和一个String元素description。
/**
* UseCase.java
* @author lovekun
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @
interface UseCase {
public int id();
public String
description()
default "no description";
}
/**
* PasswordUtil.java
* @author lovekun
*
*/
public class PasswordUtil {
/**
* 校验密码格式
* @param password
* @return
*/
@UseCase(id=
1, description=
"validatePassword")
public boolean validatePassword(String password) {
return password.matches(
"\\w*\\d\\w*");
}
/**
* 倒序加密密码
* @param password
* @return
*/
@UseCase(id=
2)
public String
encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
/**
* 判断密码是否已存在
* @param prePassword
* @param newPassword
* @return
*/
@UseCase(id=
3, description=
"checkForNewPassword")
public boolean checkForNewPassword(
List<String> prePassword, String newPassword){
return !prePassword.contains(newPassword);
}
}
/**
* UseCaseTracker.java
* @author lenovo
*
*/
public class UseCaseTracker {
/**
* 验证是否缺失用例
* @param usecases
* @param cl
*/
public static void trackUsecase(
List<Integer> usecases, Class<?> cl){
for (Method m : cl.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
if (uc !=
null) {
System.out.println(uc.id() +
"AND" + uc.description());
usecases.remove(
new Integer(uc.id()));
}
}
for (
int i : usecases) {
System.out.println(
"miss usecase:" + i);
}
}
public static void main(String[] args) {
List<Integer> usecases =
new ArrayList<Integer>();
Collections.addAll(usecases,
1,
2,
3,
4);
trackUsecase(usecases, PasswordUtil.class);
}
}
例2:利用注解生成数据库创建语句
/**
* DBTable.java
* @author lenovo
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @
interface DBTable {
public String
name()
default "";
}
/**
* SQLString.java
* @author lenovo
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @
interface SQLString {
public int value()
default 0;
public String
name()
default "";
public Constraints
constraints()
default @Constraints;
}
/**
* SQLInteger.java
* @author lenovo
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @
interface SQLInteger {
String name()
default "";
public Constraints
constraints()
default @Constraints;
}
/**
* Constraints.java
* @author lenovo
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @
interface Constraints {
public boolean primaryKey()
default false;
public boolean allowNull()
default true;
public boolean unique()
default false;
}
/**
* Member.java
* @author lenovo
*
*/
@DBTable(name=
"member")
public class Member {
@SQLString(
30)
String firstname;
@SQLString(
50)
String lastname;
@SQLInteger
Integer age;
@SQLString(value=
30, constraints=
@Constraints(primaryKey=
true))
String handle;
static int memberCount;
public String
getFirstname() {
return firstname;
}
public String
getLastname() {
return lastname;
}
public Integer
getAge() {
return age;
}
public String
getHandle() {
return handle;
}
@Override
public String
toString() {
return "Member [handle=" + handle +
"]";
}
}
/**
* TableCreator.java
* @author lenovo
*
*/
public class TableCreator {
public static void main(String[] args){
try {
Class<?> cl = Class.forName(
"com.annotation.demo.Member");
DBTable dbtable = cl.getAnnotation(DBTable.class);
if (dbtable ==
null) {
System.out.println(
"no DBTable annotation in class");
return ;
}
String tablename = dbtable.name();
List<String> columnDefs =
new ArrayList<String>();
for (Field field : cl.getDeclaredFields()) {
String columnName =
"";
Annotation[] anns = field.getDeclaredAnnotations();
if (anns.length <
1) {
continue;
}
if (anns[
0]
instanceof SQLInteger) {
SQLInteger sInt = (SQLInteger)anns[
0];
if (sInt.name().length() <
1) {
columnName = field.getName().toUpperCase();
}
else {
columnName = sInt.name();
}
columnDefs.add(columnName +
" INT" + getConstraints(sInt.constraints()));
}
if (anns[
0]
instanceof SQLString) {
SQLString sString = (SQLString) anns[
0];
if (sString.name().length() <
1) {
columnName = field.getName().toUpperCase();
}
else {
columnName = sString.name();
}
columnDefs.add(columnName+
" VARCHAR(" + sString.value() +
")" + getConstraints(sString.constraints()));
}
}
StringBuilder createCommand =
new StringBuilder(
"CREATE TABLE " + tablename +
"(");
for (String columnDef : columnDefs) {
createCommand.append(
"\n " + columnDef +
",");
}
String tableCreate = createCommand.substring(
0, createCommand.length() -
1) +
");";
System.out.println(tableCreate);
}
catch (ClassNotFoundException e) {
System.err.println(
"class not found");
}
}
public static String
getConstraints(Constraints con) {
String constraints =
"";
if (!con.allowNull()) {
constraints +=
" NOT NULL";
}
if (con.primaryKey()) {
constraints +=
" PRIMARY KEY";
}
if (con.unique()) {
constraints +=
" UNIQUE";
}
return constraints;
}
}
最终输出结果为:
CREATE TABLE member(
FIRSTNAME VARCHAR(30),
LASTNAME VARCHAR(50),
AGE INT,
HANDLE VARCHAR(30) PRIMARY KEY);