ObjectiveSQL 原理之-Java 代碼自動生成

Java 是傳統意義上的靜態語言,嚴格的類型限制和原始結構的保護,相應的IDE 也有嚴格的語法控制,並不像Ruby, Python 等動態語言的徹底開放,這也就限制了Java 的擴展性,尤爲針對純技術型Framework 的開發的限制很是明顯。目前,大都數技術型框架都是經過動態代理(Dynamic Proxy) 的方式實現技術邏輯的封裝,例如:Cglib、ASM等,均爲運行時動態生成字節碼的形式進行邏輯封裝,這樣的封裝會帶來幾個問題:java

  1. 沒有意義的接口定義:Java 中的Interface 是一種對(可變)依賴的抽象和封裝,是面向對象設計中的重要概念,但動態代理時,接口轉變爲依賴的描述,喪失的抽象和封裝的意義,反而演變爲一種新的編程模型,將簡單的問題複雜化。
  2. 增長了堆棧深度:因爲動態代理是在運行期動態生成字節碼,若是業務代碼出現異常,經過堆棧信息很難找出根源,定位故障的成本極高。Sping 的核心技術就是動態代理,新手在使用SpringBoot 時,出現異常後老是無從下手,堆棧信息毫無價值。
  3. 代碼邏輯複雜:有經驗的程序員在調試SpingBoot 的源碼時就會發現,其中的邏輯會讓人極度崩潰,根本就不知道到底運行的實例的構造過程,也徹底不符合Spring 創始人最初的願景。

JDK 1.5 版本發佈了JSR 269 Pluggable Annotation Processing API規範,其實也就是在javac 的過程當中容許根據自定義的Anntation 生成代碼,其原理就是經過API 干預Java 編譯的語法樹,能夠在原有的Class 中的增長字段和方法,也能夠修改原有語法樹中的元素。git

注意:JDK1.8 以前(包括: JDK 1.8),JSR 269 的代碼包含在JAVA_HOME/lib/tools.jar,在Maven 中須要額外增長依賴。程序員

<dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>${java.home}/../lib/tools.jar</systemPath>
    <optional>true</optional>
</dependency>

ObjectiveSQL 中的使用的方法、字段和內部類生成爲例,詳細介紹Java 是如何動態生成代碼。github

1)DomainModel 註解定義

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DomainModel {
...
}

2) AbstractProcessor 擴展,用於響應自定義Annotation 處理邏輯

import org.mangosdk.spi.ProviderFor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.AbstractProcessor;

@ProviderFor(Processor.class)
public class DomainModelCodeGenerator extends AbstractProcessor{
	
     @Override
     public Set<String> getSupportedAnnotationTypes() {
	  Set<String> supportedOptions = new HashSet<>();
	  supportedOptions.add(DomainModel.class.getCanonicalName());
	  return supportedOptions;
     }

     @Override
     public SourceVersion getSupportedSourceVersion() {
	  return SourceVersion.latestSupported();
     }

    @Override
    public synchronized void init(ProcessingEnvironment env) {
        super.init(env);

        this.messager = processingEnv.getMessager();
        this.elementUtils = processingEnv.getElementUtils();
        this.javacTrees = JavacTrees.instance(processingEnv);

        Context context = ((JavacProcessingEnvironment) env).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, 
                           RoundEnvironment env) {
        final Class<? extends Annotation> annClass = getAnnotationClass();
        final Set<? extends Element> elements = env
                .getElementsAnnotatedWith(annClass);
        for(Element element : elements) {
            JCTree ast = javacTrees.getTree(element);
            // The ‘ast’ is the abstract syntax tree of Java compiled.
            // You can adjust the ‘ast’ to change the Java code
        }
     }

}

 上述代碼能夠查看JDK 的源代碼,能夠獲得更詳細的解決,在此不做更多介紹。sql

3)生成Method 處理邏輯

public JCTree.JCMethodDecl build(String name, int modifiers) {
    if (returnType == null)
        returnType = treeMaker.TypeIdent(TypeTag.VOID);

    if (returnStatement != null)
        statements.append(treeMaker.Return(returnStatement));

    return treeMaker.MethodDef(treeMaker.Modifiers(modifiers),
            aptBuilder.toName(name),
            returnType,
            List.<JCTree.JCTypeParameter>nil(),
            parameters.toList(),
            throwsClauses.toList(),
            treeMaker.Block(0, statements.toList()), null);
}

4)生成Field 邏輯

private void handleTableName(APTBuilder aptBuilder) {
    TreeMaker treeMaker = aptBuilder.getTreeMaker();

    JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC | Flags.STATIC | Flags.FINAL);

    JCMethodInvocation methodInvocation = treeMaker.Apply(List.nil(),
            treeMaker.Select(aptBuilder.typeRef(Tables.class), aptBuilder.toName("getTableName")),
            List.of(aptBuilder.classRef(aptBuilder.getClassName())));
    JCVariableDecl tableNameField = treeMaker.VarDef(modifiers,
            aptBuilder.toName("TABLE_NAME"), aptBuilder.typeRef(String.class), methodInvocation);

    aptBuilder.inject(tableNameField);
}

4 生成內部類邏輯

private void handleInnerTableClass(APTBuilder aptBuilder) {
    JCClassDecl classDecl = aptBuilder.classDef(Flags.PUBLIC | Flags.FINAL | Flags.STATIC,
            "Table", AbstractTable.class);
    TreeMaker treeMaker = aptBuilder.getTreeMaker();
    StatementBuilder constructorStatement = aptBuilder.createStatementBuilder();
    MethodBuilder asTableMethod = aptBuilder.createMethodBuilder();

    constructorStatement.append("super", aptBuilder.classRef(aptBuilder.getClassName()));
    JCMethodDecl constructor = aptBuilder.createConstructor(Flags.PRIVATE, List.nil(), constructorStatement.build());
    classDecl.defs = classDecl.defs.append(constructor);

    asTableMethod.setReturnType(aptBuilder.typeRef(aptBuilder.getClassName() + ".Table"));
    asTableMethod.setReturnStatement(treeMaker.NewClass(null, List.nil(), aptBuilder.typeRef("Table"),
            List.nil(), null));

    JCVariableDecl[] fields = aptBuilder.getFields();
    for (JCVariableDecl field : fields) {
        if (!aptBuilder.isStatic(field.mods)) {
            JCExpression init = aptBuilder.staticMethodCall(DefaultColumn.class, "create",
                    aptBuilder.classRef(aptBuilder.getClassName()),
                    aptBuilder.varRef("this"), treeMaker.Literal(field.name.toString()));
            JCVariableDecl var = aptBuilder.newVar(Flags.PUBLIC | Flags.FINAL,
                    Column.class, field.name.toString(), init);

            classDecl.defs = classDecl.defs.append(var);
        }
    }

    aptBuilder.inject(asTableMethod.build("asTable", Flags.PUBLIC | Flags.STATIC | Flags.FINAL));
    aptBuilder.inject(classDecl);
}

JSR 269 - "Pluggable Annotation Processing API" 中涉及太多概念,其中對Java 代碼中涉及的模型進行抽象、封裝,和傳統的應用程序開發的區別比較大,有興趣的同窗能夠訪問:"ObjectiveSQL 代碼生成" 查看完整代碼。編程

相關文章
相關標籤/搜索