首先先定义SQLString SQLInteger Constraints这三个Annotation,下面以SQLString为代表:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
public int length() default 0;
String name() default "";
Constraints constraints() default @Constraints(allowNull=true);
}
然后定义AnnotationProcessor 和 AnnotationProcessorFactory, 注意内部类和观察者模式在这里的运用:
public class TableCreationProcessorFactory implements AnnotationProcessorFactory{
@Override
public AnnotationProcessor getProcessorFor(
Set<AnnotationTypeDeclaration> atds,
AnnotationProcessorEnvironment env) {
return new TableCreationProcessor(env);
}
@Override
public Collection<String> supportedAnnotationTypes() {
return Arrays.asList("annotations.database2.DBTable", "annotations.database2.Constraints", "annotations.database2.SQLString", "annotations.database2.SQLInteger");
}
@Override
public Collection<String> supportedOptions() {
return Collections.emptyList();
}
private class TableCreationProcessor implements AnnotationProcessor{
public TableCreationProcessor(AnnotationProcessorEnvironment env) {
this.env = env;
}
private final AnnotationProcessorEnvironment env;
private String sql = "";
@Override
public void process() {
for(TypeDeclaration typeDec : this.env.getTypeDeclarations()){
typeDec.accept(DeclarationVisitors.getDeclarationScanner(new TableCreationVisitor(), DeclarationVisitors.NO_OP));
sql = sql.substring(0, sql.length()-1)+");";
System.out.println("creation SQL is : "+sql);
sql = "";
}
}
private class TableCreationVisitor extends SimpleDeclarationVisitor{
@Override
public void visitClassDeclaration(ClassDeclaration d) {
DBTable dbTable = d.getAnnotation(DBTable.class);
if(dbTable != null){
sql +="CREATE TABLE ";
sql += (dbTable.name().length() < 1) ? d.getSimpleName().toUpperCase() : dbTable.name();
sql += " (";
}
}
@Override
public void visitFieldDeclaration(
FieldDeclaration d) {
if(d.getAnnotation(SQLString.class) != null){
System.out.println("process SQLString");
SQLString sqlString = d.getAnnotation(SQLString.class);
sql += (sqlString.name().equals("")? d.getSimpleName().toUpperCase():sqlString.name())+" varchar2("+sqlString.length()+")"
+getConstraints(sqlString.constraints())+",";
}else if(d.getAnnotation(SQLInteger.class) != null){
System.out.println("process SQLInteger");
SQLInteger sqlInteger = d.getAnnotation(SQLInteger.class);
sql += (sqlInteger.name().equals("")?d.getSimpleName().toUpperCase():sqlInteger.name())+" INT" +",";
}
}
private String getConstraints(Constraints c){
String temp = "";
if(c.primaryKey()){
temp += " PRIMARY KEY";
}
if(!c.allowNull()){
temp += " NOT NULL";
}
if(c.unique()){
temp += " UNIQUE";
}
return temp;
}
}
}
}
在书中,getDeclarationScanner这是DeclarationVisitors中的静态方法,作者直接引用,由于我没看import的习惯,害我找了半天,我不推荐直接引用静态方法和变量。
getDeclarationScanner中两个参数分别表示:访问每个声明前使用的Visitor,访问每个声明后的Visitor,这里只需要访问前的Visitor,因此第二个参数用DeclarationVisitors.NO_OP表示什么也不做,这里不能直接new TableCreationVisitor()作为TypeDeclaration.accept的参数,否则只有visitClassDeclaration()这个方法被调用,visitFieldDeclaration()竟然不被调用,很奇怪。SupportedType指明哪些Annotation需要处理,supportedOperation不知道什么作用。
下面是测试代码:
public class Test {
public static void main(String...args){
try{
Process p = Runtime.getRuntime().exec("cmd /c apt -factory annotations.database2.TableCreationProcessorFactory annotations/database2/Member.java");
final BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream(),"gb2312"));//解决中文乱码
final BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream(),"gb2312"));
new Thread(){
String str = null;
@Override
public void run() {
try {
while((str = input.readLine())!=null){
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
new Thread(){
String str = null;
@Override
public void run() {
try {
while((str = error.readLine())!=null){
System.out.println("Err: "+str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
System.out.println("exit: "+p.waitFor());
}catch(IOException e){
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
CREATE TABLE member (FIRSTNAME varchar2(30),LASTNAME varchar2(50),AGE INT,HANDLE varchar2(30) PRIMARY KEY);