Javapoet源码解析
Javapoet:是生成.java源文件的开源API
以生成一个HelloWrold.java文件为例:
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
///javaFile.writeTo(System.out); //System.out print to the console
//generate a java file in user.dir
String directory = System.getProperty("user.dir");
File file = new File(directory + SEPARATOR + "HelloWrold.java");
PrintStream ps = new PrintStream(new FileOutputStream(file));
javaFile.writeTo(ps);
ps.flush();
ps.close();
在这个例子中用到了三个类的对象 :JavaFile MethodSpec TypeSpec,首先来看第一个JavaFile
public static Builder builder(String packageName, TypeSpec typeSpec) {
checkNotNull(packageName, "packageName == null");
checkNotNull(typeSpec, "typeSpec == null");
return new Builder(packageName, typeSpec);
}
Builder是JavaFile中的一个静态内部类,由此可以看到 Javapoet使用的是建造者设计模式。通过packageName 和TypeSpec生成一个Builder对象,然后调用Builder类的build方法:
public JavaFile build() {
return new JavaFile(this);
}
返回当前的JavaFile对象,然后调用writeto方法将该对象写入流/文件。
接下来看看构造Builder方法时使用的TypeSpec是什么。
staticBlock:A generated class, interface, or enum declaration:对于要生成的类,接口或枚举的声明。就是定义 类 接口或者枚举,定义的时候需要给他们命名,添加权限修饰符 是否静态 是否抽象 是否final,添加方法等操作
所以这个类里面有静态方法classBuilder interfaceBuilder enumBuilder 以及匿名内部类anonymousClassBuilder,这些静态方法都返回对应的一个Builder对象
该Builder对象是TypeSpec中的静态内部类,用于构建当前的TypeSpec对象。在内部类Builder中应该有添加权限修饰符/注释/注解/变量等操作来构建一个类或者接口枚举
MethodSpec:A generated constructor or method declaration. 对方法的声明,包括构造方法。所以这个类里面有methodBuilder constructorBuilder,返回该类的一个Builder对象,这个builder也是该类的一个静态内部类。并且这个Builder内部类中同样包括添加权限修饰符/注释/注解等方法,另外方法有返回值,参数 Comment 语句来构建一个方法
同样的原理,FieldSpec是对成员变量的声明,而且里面也有一个可以返回当前对象的builder静态内部类,在这个builder中同样有添加注释注解访问修饰符等的操作,来构建一个变量。
类变量等已经定义完毕,接下来我们分析一下是如何生成java文件的,回到JavaFile的writeTo方法
这里有很多重载的writeTo方法,最终都会调用
public void writeTo(Appendable out) throws IOException {
// First pass: emit the entire class, just to collect the types we'll need to import.
CodeWriter importsCollector = new CodeWriter(NULL_APPENDABLE, indent, staticImports);
emit(importsCollector);
Map<String, ClassName> suggestedImports = importsCollector.suggestedImports();
// Second pass: write the code, taking advantage of the imports.
CodeWriter codeWriter = new CodeWriter(out, indent, suggestedImports, staticImports);
emit(codeWriter);
}
CodeWriter是什么呢?CodeWriter将JavaFile转换为适用于人类和javac使用的字符串,实现了导入,缩进和延期变量名称。
以FieldSpec为例来说明CodeWriter的用法:
void emit(CodeWriter codeWriter, Set<Modifier> implicitModifiers) throws IOException {
codeWriter.emitJavadoc(javadoc);
codeWriter.emitAnnotations(annotations, false);
codeWriter.emitModifiers(modifiers, implicitModifiers);
codeWriter.emit("$T $L", type, name);
if (!initializer.isEmpty()) {
codeWriter.emit(" = ");
codeWriter.emit(initializer);
}
codeWriter.emit(";\n");
}
这段代码是在生成变量时调用的。第二行是写javadoc注释,第三行是写注解,第四行是写访问修饰符,第五行是写该变量的类型和变量名,第6-9行是判断变量是否有初始化值,如果有就写初始化值
第10行;代表该变量写出完毕。至此,HelloWorld.java的生成过程分析完毕。
用到的一些集合类中的方法:
Collections.singletonList
Collections.emptyList()
EnumSet.copyOf(Set<Modifier> modifiers)
Collections.unmodifiableList(new ArrayList<>(collection)); 返回指定列表的不可修改视图。 此方法允许模块为用户提供对内部列表的“只读”访问权限。
Javapoet源码解析的更多相关文章
- 阿里ARouter使用及源码解析(一)
在app的开发中,页面之间的相互跳转是最基本常用的功能.在Android中的跳转一般通过显式intent和隐式intent两种方式实现的,而Android的原生跳转方式会存在一些缺点: 显式inten ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- jQuery2.x源码解析(缓存篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...
- Spring IoC源码解析——Bean的创建和初始化
Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
- jQuery2.x源码解析(设计篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...
随机推荐
- Day15 jss整体结构梳理
JS DOM--- 两个步骤: 1 查找标签 (1)直接查找 document.getElementById("idname") // dom对象 document.getElem ...
- [ Java面试题 ]基础篇之一
1.一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致. 2.Java有 ...
- C++ 进制转换 十进制十六进制八进制二进制相互转换
思路: 下面我把相互转换的所有类型都写出来了.实际上都是通过十进制中转的,这样比较简单,写出X进制转成十进制和从十进制转成X进制的两份代码直接拷贝就完成了剩余的部分.哦,对,自己封装了一个charTo ...
- 一个简单而实用的JQ插件——lazyload.js图片延迟加载插件
前 言 Cherish 看多了炫酷的插件之后再来看这么一个小清新的东西,是不是突然感觉JQ插件感觉很友好了,简单强大最重要的是实用. 这篇文章将详细讲解一下lazyload.js的用法 lazy ...
- SOFA 源码分析 —— 服务引用过程
前言 在前面的 SOFA 源码分析 -- 服务发布过程 文章中,我们分析了 SOFA 的服务发布过程,一个完整的 RPC 除了发布服务,当然还需要引用服务. So,今天就一起来看看 SOFA 是如何引 ...
- JavaScript之对象继承
原型链继承 function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ ...
- 学习了解CyclicBarrier
CyclicBarrier我的理解就是一个线程等待器,用途就是将注册了这个barrier的线程卡在同一个位置,直到注册这个barrier的所有线程都完成之后,继续执行.下面是一个学习过程中采用的示例, ...
- redis安装、使用
官网:http://redis.io/ github地址:https://github.com/antirez/redis 简介: redis是一个key-value存储系统.和Mem ...
- c# 语法要点速览
C# 变量类型 sbyte byte short ushort int uint long ulong float double decimal char bool string switch 默认不 ...
- goroutine和线程区别
从调度上看,goroutine的调度开销远远小于线程调度开销. OS的线程由OS内核调度,每隔几毫秒,一个硬件时钟中断发到CPU,CPU调用一个调度器内核函数.这个函数暂停当前正在运行的线程,把他的寄 ...