ObjectiveSQL Principle - Automatic Java Code Generation

Java is a static language in the traditional sense, with strict type restrictions and protection of the original structure, and the corresponding IDE also has strict syntax control, which is not completely open like Ruby, Python and other dynamic languages, which also limits the scalability of Java. , especially for the development of purely technical frameworks, the limitations are very obvious. At present, most technical frameworks implement the encapsulation of technical logic through dynamic proxy (Dynamic Proxy), such as Cglib, ASM, etc., all of which are logically encapsulated in the form of dynamically generated bytecodes at runtime. Such encapsulation will Brings up a few questions:

  1. Meaningless interface definition: Interface in Java is an abstraction and encapsulation of (variable) dependencies, an important concept in object-oriented design, but when dynamically proxying, the interface is transformed into a description of dependencies, losing abstraction and encapsulation Instead, it evolved into a new programming model that complicates simple problems.
  2. Increased stack depth: Since the dynamic agent generates bytecode dynamically during runtime, if the business code is abnormal, it is difficult to find the root cause through the stack information, and the cost of locating the fault is extremely high. The core technology of Spring is dynamic proxy. When a novice uses SpringBoot, it is always impossible to start after an exception occurs, and the stack information is worthless.
  3. Complex code logic : When an experienced programmer debugs the source code of SpingBoot, they will find that the logic in it will make people extremely crash, and they don't know the construction process of the running instance at all, and it does not conform to the original vision of the founder of Spring. .

JDK 1.5 The version released the specification, in fact, in the process of javac, it is allowed to generate code according to the customization . The principle is to intervene in the syntax tree compiled by Java through the API. You can add fields and methods in the original Class, and you can also modify the original class. elements in the syntax tree.JSR 269 Pluggable Annotation Processing APIAnntation 

Note: Before JDK1.8 (including: JDK 1.8), the code of JSR 269 is included in JAVA_HOME/lib/tools.jar, which requires additional dependencies in 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>

Taking ObjectiveSQL the used method, field and inner class generation as an example, we will introduce in detail how Java dynamically generates code.

1) DomainModel annotation definition

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

2) AbstractProcessor extension, used to respond to custom Annotation processing logic

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
        }
     }

}

 The above code can be viewed in the source code of the JDK, and a more detailed solution can be obtained, which will not be introduced here.

3) Generate Method processing logic

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) Generate Field logic

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 Generate inner class logic

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" involves too many concepts, among which abstracting and encapsulating the models involved in Java code is quite different from traditional application development. Interested students can visit: " ObjectiveSQL Code Generation " View the full code.

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324129465&siteId=291194637