Java 是传统意义上的静态语言,严格的类型限制和原始结构的保护,相应的IDE 也有严格的语法控制,并不像Ruby, Python 等动态语言的完全开放,这也就限制了Java 的扩展性,尤其针对纯技术型Framework 的开发的限制非常明显。目前,大都数技术型框架都是通过动态代理(Dynamic Proxy) 的方式实现技术逻辑的封装,例如:Cglib、ASM等,均为运行时动态生成字节码的形式进行逻辑封装,这样的封装会带来几个问题:
- 没有意义的接口定义:Java 中的Interface 是一种对(可变)依赖的抽象和封装,是面向对象设计中的重要概念,但动态代理时,接口转变为依赖的描述,丧失的抽象和封装的意义,反而演变为一种新的编程模型,将简单的问题复杂化。
- 增加了堆栈深度:由于动态代理是在运行期动态生成字节码,如果业务代码出现异常,通过堆栈信息很难找出根源,定位故障的成本极高。Sping 的核心技术就是动态代理,新手在使用SpringBoot 时,出现异常后总是无从下手,堆栈信息毫无价值。
- 代码逻辑复杂:有经验的程序员在调试SpingBoot 的源码时就会发现,其中的逻辑会让人极度崩溃,根本就不知道到底运行的实例的构造过程,也完全不符合Spring 创始人最初的愿景。
JDK 1.5
版本发布了JSR 269
Pluggable Annotation Processing API
规范,其实也就是在javac 的过程中允许根据自定义的Anntation
生成代码,其原理就是通过API 干预Java 编译的语法树,可以在原有的Class 中的增加字段和方法,也可以修改原有语法树中的元素。
<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 是如何动态生成代码。
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 的源代码,可以得到更详细的解决,在此不作更多介绍。
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 代码生成" 查看完整代码。