本部分内容参考自:《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包中内置了三种基本的注解:
注解的定义看起来很像接口的定义,而且注解也将会被编译成class文件。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}
除了@符号外,Test的定义就像是一个空的接口。
在定义注解时,需要一些元注解(meta-annotation),如@Target和@Retention。
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, 以及以上类型的数组。
如果使用其它类型,编译器就会报错。
该例用到了三个资源文件: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 prePassword, String newPassword){
return !prePassword.contains(newPassword);
}
}
/**
* UseCaseTracker.java
* @author lenovo
*
*/
public class UseCaseTracker {
/**
* 验证是否缺失用例
* @param usecases
* @param cl
*/
public static void trackUsecase(
List 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 usecases = new ArrayList();
Collections.addAll(usecases, 1, 2, 3, 4);
trackUsecase(usecases, PasswordUtil.class);
}
}
/**
* 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 columnDefs = new ArrayList();
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);