Java 是傳統意義上的靜態語言,嚴格的類型限制和原始結構的保護,相應的IDE 也有嚴格的語法控制,並不像Ruby, Python 等動態語言的徹底開放,這也就限制了Java 的擴展性,尤爲針對純技術型Framework 的開發的限制很是明顯。目前,大都數技術型框架都是經過動態代理(Dynamic Proxy) 的方式實現技術邏輯的封裝,例如:Cglib、ASM等,均爲運行時動態生成字節碼的形式進行邏輯封裝,這樣的封裝會帶來幾個問題:java
- 沒有意義的接口定義:Java 中的Interface 是一種對(可變)依賴的抽象和封裝,是面向對象設計中的重要概念,但動態代理時,接口轉變爲依賴的描述,喪失的抽象和封裝的意義,反而演變爲一種新的編程模型,將簡單的問題複雜化。
- 增長了堆棧深度:因爲動態代理是在運行期動態生成字節碼,若是業務代碼出現異常,經過堆棧信息很難找出根源,定位故障的成本極高。Sping 的核心技術就是動態代理,新手在使用SpringBoot 時,出現異常後老是無從下手,堆棧信息毫無價值。
- 代碼邏輯複雜:有經驗的程序員在調試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 代碼生成" 查看完整代碼。編程