栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

ActiveJ框架学习——Codegen的初步认识

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ActiveJ框架学习——Codegen的初步认识

2021SC@SDUSC

Codegen初认识

引用ActiveJ网站对于Codegen的介绍:

Codegen是基于ObjectWeb ASM的低开销的java代码生成器,是构成ActiveJ Serializer成为世界上最快的JVM序列化器的技术”。

看到这句话着实被这口气吓了一跳,但是在分析为什么Codegen这么厉害之前,对于我本人来说,先要递归的学习一下什么是ASM。

ASM

这里要明白,ActiveJ是一个网络后端服务器,因此Codegen的串行化技术主要应用领域亦是在不同的计算机上远程传输数据。所以查询和理解的方向就是网络传输领域。

所以,ASM是什么呢?

————简单来讲,java的Class文件是通过javac编译器产生的,通过类加载器加载到虚拟机内,而ASM的功能,就是定义一系列API来直接生成符合java虚拟机规范的Class字节流,因此,ASM做的工作相当于就是javac做的工作,但是ASM生成的Class字节流更小更快。

结合ActiveJ的RPC(远程过程调用,Remote Procedure Call)功能,当一台服务器远程调用另一台服务器提供的方法时,由于底层网络采用TCP协议以二进制传输数据,因此服务器需要使用串行化技术(Serializer)来将参数串行化后发送给被调用的服务器,同时,结果的返回也需要以序列化后的二进制传输。所以,ASM(或者说,这里可以替换为ActiveJ-Codegen)的工作,就是生成更快更小的序列化数据,从而加快服务器相应速度。

因此,Codegen的功能,就是对于Class字节流的生成操作,其对于一个类的生成,经常使用上学期的反射和内省技术,结合官网的例子来认识第一眼Codegen:

public static void main(String[] args) throws ReflectiveOperationException {
		//[START REGION_1]
		Class greeterClass = ClassBuilder.create(Greeter.class)
				.withMethod("sayHello",
						call(staticField(System.class, "out"),
								"println",
								value("Hello world")))
				.defineClass(CLASS_LOADER);
		//[END REGION_1]

		//[START REGION_2]
		Greeter greeter = greeterClass.getDeclaredConstructor().newInstance();
		greeter.sayHello();
		//[END REGION_2]
	}

这里使用面向对象课程里学到的元类(meta class)来定义了一个类,生成了一个类(注意不是定义),并执行了其sayHello()方法来产生输出。

控制台打印了"Hello world"

 并且官网提到了这个类生成语句中特别的一行:

call(staticField(System.class, "out"), "println", value("Hello world"))

Notice the call(staticField(System.class, "out"), "println", value("Hello world")) expression.

This is how expressions DSL looks like

意思是这里使用了ActiveJ的特别的一个expressions DSL,操作了system类的out域,并且调用了out的println()方法,将参数 "Hello world" 打印了出来。在官网给出的另一个例子中,也是使用了类似的expressions DSL语句。

为什么要特别地将expressions这个概念着重指出呢?

Codegen expression
Codegen结构

在Codegen部分的代码中,expression包作为最重要的部分,提供了使用expression来生成一个java Class的大部分功能。

首先是对于expression的定义,很容易想到,其应该是一个接口。

public interface expression {
	
	Type load(Context ctx);
}

expression只有一个返回值类型为Type的load()方法,而关于Type,其被包含于org.objectweb.asm的package里,定义为 "A Java field or method type. This class can be used to make it easier to manipulate type and method descriptors." 这个解释理解起来有些困难,但是在官网给出的另一个例子中,即使用Codegen来构造动态类(dynamic class)里,使用了load()方法,这里暂时将这个疑问保留,之后再回答。

expression结构

概览一遍expression包,可以发现,这些包都是以expression开头的包,然后后缀都是一些基本的功能,例如ArrayGet,即为列表提取成员;Cmp,即为比较二者是否相同。

为了有助于分析,这里首先打开一个expressionToString类来作为例子,因为toString()方法是我们所熟悉的。

expressionToString

首先是这个类的注释:

public final class expressionToString implements expression {

}

解释为定义一些方法来生成一个字符串,和我们理解的toString()方法的效果初步看来是一致的。

之后是expressionToString类的成员变量:

private String begin = "{";
private String end = "}";
private String separator = " ";
private final Map arguments = new linkedHashMap<>();

包含了一个开始符 "{" 和结束符 "}" 以及分隔符 " " ,应该是打印出一种格式为"{ X X X }"的字符串来,此外还有一个final变量Map,结合之后的函数来看是作为存储其他expression的容器,并且这里的Object类其实是String类,因为之后可以发现其Key都是由数字字符串来构成的。

之后来分别查看定义的方法。

    expressionToString() {
	}

	public static expressionToString create() {
		return new expressionToString();
	}

	public expressionToString with(String label, expression expression) {
		this.arguments.put(label, expression);
		return this;
	}

	public expressionToString with(expression expression) {
		this.arguments.put(arguments.size() + 1, expression);
		return this;
	}
  • 构造函数是一个空的构造函数,但是为什么还要写呢,因为这个类是public,其默认构造函数的访问修饰符将为public,而这里写了构造函数,则将访问修饰符设置为了默认的default,这也是为什么提供了create()方法的原因。
  • 一个静态方法create()提供外部调用,来生成一个expressionToString对象。
     
  • 两个with方法,用来将参数的expression添加到定义的成员变量argument里,这里可以看到其Key的类型其实都是String。
    public expressionToString withSeparator(String separator) {
		this.separator = separator;
		return this;
	}

	public expressionToString withQuotes(String begin, String end) {
		this.begin = begin;
		this.end = end;
		return this;
	}
  • 一个是定义分隔符的方法,但是使用空格作为分隔符是常识,所以这个方法并没用被任何类调用...
  • 一个是引用方法,即修改默认的begin和end符,为了某些特殊情况,不过也没有被调用...

最后就是最重要的load()方法的分析了

    GeneratorAdapter g = ctx.getGeneratorAdapter();
    g.newInstance(getType(StringBuilder.class));
    g.dup();
    g.invokeConstructor(getType(StringBuilder.class), getMethod("void  ()"));
    boolean first = true;
  • 首先,expressionToString类也是实现了expression接口的一个类,所以需要重载load()方法根据传入参数Context ctx来生成对应的类生成器(Class builder),然后使用反射内省来生成一个StringBuilder类,并调用其init()初始化方法。
for (Map.Entry entry : arguments.entrySet()) {
			String str = first ? begin : separator;
			first = false;
			if (entry.getKey() instanceof String) {
				str += entry.getKey();
			}
			if (!str.isEmpty()) {
				g.dup();
				g.push(str);
				g.invokeVirtual(getType(StringBuilder.class), getMethod("StringBuilder append(String)"));
				g.pop();
			}
}
  • 之后,进入对Map argument的遍历中,遍历就变得简单一点。首先如果是第一个Map元素的话,就加begin字符串"{"加到首位,然后对元素的Key进行判断,如果是服合规范的字符串类型,则类生成器会分配一个位置,并且将为这个元素执行load(Context ctx)方法,即将传入的描述数据(Context 参数)添加到类生成器里,最后添加end字符串 "}" 。
  • 如果遇到是空的类型,也会自动执行默认的null字符串生成,保证容错率。
return getType(String.class);
  • 当遍历所有的argument元素后,这些元素的字符串打印就都被类生成器记录下来,作为将来的串行化时来代替这些类的toString()方法,生成特定格式的字符串来。
总结

本次代码分析主要是从大体上分析Codegen部分的结构,然后深入到expression包里进行分析,最后以一个expressionToString类来作为例子分析,柿子要挑软的捏嘛,先分析这个比较简单和基础的类,认识到Codegen的expression的一个小功能,从而为以后理解Codegen的ASM功能打下基础。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/299986.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号