栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

ActiveJ框架学习(四)——Context功能分析

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

ActiveJ框架学习(四)——Context功能分析

2021SC@SDUSC

前文承接

在第二篇博客中提到了expression接口的方法 load():

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

而通过上一篇博客对于expressionTest和其他expression类的分析,可以看到在每一个expression中,都以不同的方式实现了这个load()方法。而load()方法有一个很重要的参数就是Context类型,因此本篇博客来分析以下Context类的内容和功能。

Context结构概览

context类位于Codegen模块中的util包中,即公共工具包中,因为会被很多类引用。

context类结构:

第一行代码为ActiveJ的注释内容,描述 Context 的作用:
“Context包含了一个动态类有关的信息”。基于之前的官网例子,即是在运行过程中创建动态类的工具。通过Context的内容不同,可以在运行过程中创建新的类(相比于定义好的Class来讲)。

 

接下来是方法和属性,这里显示指明了构造函数,因此不能使用无参构造函数,也就是一个Context必须被创立时就初始化。

private final ClassLoader classLoader;
	private final ClassBuilder classBuilder;
	private final GeneratorAdapter g;
	private final Type selfType;
	private final Method method;

	private Set accessibleMethods;

	public Context(ClassLoader classLoader, ClassBuilder builder,
			GeneratorAdapter g,
			Type selfType,
			Method method) {
		this.classLoader = classLoader;
		this.classBuilder = builder;
		this.g = g;
		this.selfType = selfType;
		this.method = method;
	}

定义一系列的get方法,返回Context的属性,这里使用到了很多classBuilder的功能。

public ClassLoader getClassLoader() {
		return classLoader;
	}

	public ClassBuilder getClassBuilder() {
		return classBuilder;
	}

	public GeneratorAdapter getGeneratorAdapter() {
		return g;
	}

	public Type getSelfType() {
		return selfType;
	}

//	以下都是使用 classBuilder 的功能

	public Class getSuperclass() {
		return classBuilder.superclass;
	}

	public List> getInterfaces() {
		return classBuilder.interfaces;
	}

	public Map> getFields() {
		return classBuilder.fields;
	}

	public Map getMethods() {
		return classBuilder.methods;
	}

在这里有一个标准化的方法 toJavaType(),能将传入类的Type转化为java的Type,为之后的流输出做铺垫。(流是 java8 引入的一个新概念,下文会讲到)

public Class toJavaType(Type type) {
		if (type.equals(getSelfType()))
			throw new IllegalArgumentException();
		int sort = type.getSort();
		if (sort == BOOLEAN)
			return boolean.class;
		if (sort == CHAR)
			return char.class;
		if (sort == BYTE)
			return byte.class;
		...
		if (sort == DOUBLE)
			return double.class;
			return void.class;
		if (sort == OBJECT) {
			try {
				return classLoader.loadClass(type.getClassName());
			} catch (ClassNotFoundException e) {
				throw new IllegalArgumentException(format("No class %s in class loader", type.getClassName()), e);
			}
		}
		if (sort == ARRAY) {
			Class result;
			if (type.equals(getType(Object[].class))) {
				result = Object[].class;
			} else {
				String className = type.getDescriptor().replace('/', '.');
				try {
					result = Class.forName(className);
				} catch (ClassNotFoundException e) {
					throw new IllegalArgumentException(format("No class %s in Class.forName", className), e);
				}
			}
			return result;
		}
		throw new IllegalArgumentException(format("No Java type for %s", type.getClassName()));
	}

这里ToJavaType()方法返回了Class类中的int.class和double.class等类似的java原本数据类型的引用。通过ctrl定位到一个使用这些方法的地方,比如算术运算符类里,可以看到:

if (op != SHL && op != SHR && op != USHR) {
	Type resultType = getType(unifyArithmeticTypes(ctx.toJavaType(leftType), ctx.toJavaType(rightType)));
			

这里使用了ToJavaType()方法来进行操作,来将运算符两边的数据来进行转化,从而进行数据的运算。

接下来还有其他的方法,但考虑到之前分析expression过程中遇到的主要矛盾,这里就不再写其他方法的分析与调用方式,让我们直接跳转到最重要的 invoke() 方法这里。

Invoke()

首先是最简单的invoke调用步骤,需要的参数分别为expression,methodName,expression-
List,这里的invoke()没有实现代码,而是调用了紧邻的invoke()方法(很像javascript风格,比较难看清楚)

public Type invoke(expression owner, String methodName, expression... arguments) {
		return invoke(owner, methodName, asList(arguments));
}

在下一个invoke()里,将owner转为了Type类型,arguments通过load()方法转为了Type数组。同 时在invoke()方法里又使用了一个invoke(),return语句中的invoke()将跳转到下一个紧邻的重载invoke() 里,在这一步里仍没有具体的调用语句(即真正的invoke操作)

public Type invoke(expression owner, String methodName, List arguments) {
		Type ownerType = owner.load(this);
		Type[] argumentTypes = new Type[arguments.size()];
		for (int i = 0; i < arguments.size(); i++) {
			expression argument = arguments.get(i);
			argumentTypes[i] = argument.load(this);
		}
		return invoke(ownerType, methodName, argumentTypes);
	}

在第三个invoke()里,第一行语句使用到了 java8 新的Stream数据结构:

public Type invoke(Type ownerType, String methodName, Type... argumentTypes) {
		Class[] arguments = Stream.of(argumentTypes)
                               .map(this::toJavaType)
                               .toArray(Class[]::new);
}

第一行里将参数列表转换为Java Type,并且生成类型为Stream流。这里关于Stream流的操作,是为了将来的元素读取不再使用迭代的方式,而是使用foreach方式。关于Java8中新引入的Stream菜鸟教程描述如下:

使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象

官方网站则提到Stream是一种更快的元素操作,其与迭代的区别可以如下表示,并且任务越多优势越明显:

简单来讲,类似于CPU中的流水线

之后判断调用方法的所有者是否为本类。
如果为本类,则在自身里查找对应Method参数的方法并且传入arguments参数进行调用;
如果不是本类,则先做一个标准化JavaType转换,然后调用其非静态方法里的对应Method的方法(可能是因为被调用参数arguments并不一定是非静态变量)。无论是否为本类,如果在类的methods里搜索对应的方法名没有找到时都会抛出异常。

if (ownerType.equals(getSelfType())) {
    foundMethod = findMethod(
		    getAccessibleMethods().stream(),
			methodName,
			arguments);
	if (foundMethod == null) {
		throw new IllegalArgumentException("Method not found: " + ownerType.getClassName() 
        + '#' + methodName +Arrays.stream(arguments)
        .map(Class::getName).collect(joining(",", "(", 
              ")")));
		}
	g.invokeVirtual(ownerType, foundMethod);
}else {
    Class javaOwnerType = toJavaType(ownerType);
    foundMethod = findMethod(
		   Arrays.stream(javaOwnerType.getMethods())
				   .filter(m -> !isStatic(m.getModifiers()))
				   .map(Method::getMethod),
		   methodName,
		   arguments);
    if (foundMethod == null) {
	   throw new IllegalArgumentException("Method not found: " + ownerType.getClassName() 
        + '#' + methodName +Arrays.stream(arguments).map(Class::getName)
         .collect(joining(",", "(", ")")));
	}
}

对于静态方法的调用,需要使用invokeStatic()方法来主动声明是调用的静态方法,但其调用过程与上文无异,不再额外分析。

在invoke()方法结束后,由于invoke()方法的返回值类型为Type,因此还需要调用一次foundMethod.getReturnType()来返回。

foundMethod.getReturnType()

第一行代码将方法所属的类的所有可行方法,都保存在集合里(collection),这里也使用到了Stream流数据结构的操作,看来ActiveJ不愧是最新的技术框架,将各种新的java技术都包括了进来,以提供更快的用户体验。

methodSet.addAll(Arrays.stream(Object.class.getMethods())
				.filter(m -> !isStatic(m.getModifiers()))
				.map(Method::getMethod)
				.collect(toSet()));

添加所有的元素后,进行一次集合的遍历。如果方法名称与methodname不同的,则跳过;如果方法名称就是传入的name,则将method对应方法需要的参数都转化为Stream流这种数据结构保存起来。

if (!name.equals(method.getName())) 
    continue;
	Class[] methodArguments = Stream.of(method.getArgumentTypes())
                                 .map(this::toJavaType).toArray(Class[]::new);
	if (!isAssignable(methodArguments, arguments)) {
	      continue;
}

只根据函数名进行匹配可能是不准确的,因为可能有重载的存在,需要额外判断:

if (!isAssignable(methodArguments, arguments)) 

这条语句判断参数类型是否匹配,此方法引用到了如下循环来逐一判断所有的参数类型是否匹配:

private static boolean isAssignable(Class[] to, Class[] from) {
		if (to.length != from.length) return false;
		return IntStream.range(0, from.length)
				.allMatch(i -> to[i].isAssignableFrom(from[i]));
	}

to[i].isAssignableFrom(from[i]) 方法来自于java的Class.java类,其通过类型转化来检查二者是否为相同的类或者是超类。从而检查合法性。

如果重载的参数依旧相同或者为父子类关系,则淘汰类范围大的参数对应的方法,即取子类参数对应的方法。遍历结束后,返回唯一的一个方法对应的类。

if (foundMethod == null) {
	foundMethod = method;
	foundMethodArguments = methodArguments;
} else {
	if (isAssignable(foundMethodArguments, methodArguments)) {
		foundMethod = method;
		foundMethodArguments = methodArguments;
	} else if (isAssignable(methodArguments, foundMethodArguments)) {
		// do nothing
	} else {
		throw new IllegalArgumentException("Ambiguous method: " + method + " " + 
        Arrays.toString(arguments));
	}
}
结语 

Context的内容是完成一个动态类的创建所用的工具集合,包括了如何定义一个运行时的动态类的属性和方法,从而帮助用户快速的生成一个动态类。使用load()方法加载对应的Context,即可动态的生成类或是使用此类的功能。本次分析主要是集中在Context的invoke()方法里,因为当生成一个类时,主要就是使用其提供的方法来进行操作(面向对象思想),Context提供了多种Invoke()方法来帮助用户调用静态和非静态方法,从而完成一个类的功能。

到第四篇博客了,大致可以得到一个判断:ActiveJ在提高JVM速度层面,使用了大量的面向对象里的meta class,以及反射和内省操作,可惜,这些概念在上学期的面向对象第一次接触,没有深入的理解,在学习ActiveJ的过程中还需要学习这些内容。

最后,在Context类的集合数据进行迭代和生成操作中,使用了Stream流操作来完成。此方法可以提高JVM序列的速度,这也是接触到的第一个ActiveJ为什么快捷方便的原因。在以后的分析和学习中,还可以接触到更多的新技术,继续学习吧。

参考

Java 8 Stream | 菜鸟教程 (runoob.com)

Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合_云深不知处-CSDN博客

往期回顾

ActiveJ框架学习(三)——expressionTest类千行源码分析_m0_56367233的博客-CSDN博客

ActiveJ框架学习(二)——Codegen的初步认识_m0_56367233的博客-CSDN博客

ActiveJ框架学习(一)---起步_m0_56367233的博客-CSDN博客

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

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

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