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 代码生成" 查看完整代码。编程