注解为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻使用这些数据
基本语法
public class Testable {
public void execute() {
...
}
@Test
void testExecute() {
execute();
}
}
定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}
@Target
定义你的注解用于什么地方,@Retention
定义该注解在哪个级别可用
注解一般会包含一些元素以表示某些值
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "no description";
}
下面的类中有两个方法被注解为用例
public class PasswordUtil {
@UseCase(id = 47, description = "passwords must contain at least on numeric")
public boolean validatePassword(String password) {
...
}
@UseCase(id = 48)
public String encryptPassword(String password) {
...
}
}
元注解
@Target
注解用于什么地方
CONSTRUCTOR
FIELD
LOCAL_VARIABLE
METHOD
PACKAGE
PARAMETER
TYPE
@Retention
在什么级别保存信息
SOURCE
CLASS
RUNTIME
@Documented
此注解中包含在Javadoc中
@Inherited
允许子类继承父类中的注解
注解处理器
public class UseCaseTracker {
public static void trackUseCases(Class<?> clazz) {
for (Method m : clazz.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
......
}
}
}
使用两个反射方法getDeclaredMethods()
和getAnnotation()
注解元素
可以使用下面类型:
- 所有基本类型
String
Class
enum
Annotation
- 以上类型的数组
不允许使用包装类型,注解可以嵌套
默认值限制
元素不能有不确定的值,要么有默认值,要么注解时提供
对非基本类型元素,不能以null
为值,只能自己定义特殊值如空字符串或负数以表明元素不存在
生成外部文件
假设你希望提供一些基本的对象-关系映射功能,能够自动生成数据库表,用以存储JavaBean
对象,可以通过注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
public String name() default "";
}
生成数据库表的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
boolean primaryKey() default false;
boolean allowNull() default true;
boolean unique() default false;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
int value() default 0;
String name() default "";
Constraints constraints() default @Constraints;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
String name() default "";
Constraints constraints() default @Constraints;
}
数据库的约束,以及SQL
类型,其中用到了嵌套注解
@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;
}
一个简单的JavaBean
注解不支持继承
使用apt处理注解
apt
操作源文件而不是编译后的类
通过使用AnnotationProcessorFactory
,apt
可以为每一个它发现的注解生成一个正确的注解处理器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ExtractInterface {
public String value();
}
一个把类中的public
方法提取出来的注解
@ExtractInterface("IMultiplier")
public class Multiplier {
public int multiply(int x, int y) {
...
}
private int add(int x, int y) {
...
}
}
用它来注解这个乘法运算类
public class InterfaceExtractorProcessor implements AnnotationProcessor {
private List<MethodDeclaration> interfaceMethods = new ArrayList<MethodDeclaration>();
public InterfaceExtractorProcessor(AnnotationProcessorEnvironment env) {
this.env = env;
}
public void process() {
for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations()) {
ExtractInterface annot = typeDecl.getAnnotation(ExtractInterface.class);
if (annot == null) {
break;
}
for (MethodDeclaration m : typeDecl.getMethods()) {
if (m.getModifiers().contains(Modifier.PUBLIC)) {
interfaceMethods.add(m);
}
}
if (interfaceMethods.size() > 0) {
PrintWriter writer = env.getFiler().createSourceFile(annot.value());
writer.println("package " + typeDecl.getPackage().getQualifiedName() + ";");
writer.println("public interface " + annot.value()) + " {");
for (MethodDeclaration m : interfaceMethods) {
writer.print(" public ");
writer.print(m.getReturnType() + " ");
writer.print(m.getSimpleName() + " (");
int i = 0;
for (ParameterDeclaration param : m.getParameters()) {
writer.print(param.getType() + " " + param.getSimpleName());
if (++i < m.getParameters().size()) {
writer.print(", ");
}
writer.println(");");
}
writer.println("}");
writer.close();
}
}
}
}
}
所有工作都在process()
中完成,构造器以AnnotationProcessorEnvironment
为参数,可以知道apt
正在处理的所有类型,可以获得Messenger
对象和Filer
对象,前者用来报告消息,后者用来创建新文件
public class InterfaceExtractorProcessorFactory implements AnnotationProcessorFactory {
public Collection<String> supportedOptions() {
}
public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env) {
}
public Collection<String> supportedAnnotationTypes() {
}
}
apt
工具需要一个工厂类来指明正确的处理器
apt -factory annotations.InterfaceExtractorProcessorFactory Multiplier.java -s ../annotations