对java调用javascript的封装

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhoujiaping123/article/details/82770362

天马行空:
java开发项目时,调试比较麻烦。每次改一点东西就要重启,每次重启都要等半天。然而并不是每个业务都很明确,每个接口都很清晰。无奈只能一边在测试环境运行一边改代码然后打包发布。这样效率非常低。自己在本地写了一个项目用来mock dubbo的接口。但是每次改代码还是避免不了要重启。
希望用js的动态性解决这个问题。
思路:aop+js
mock所有需要的接口,全部是空实现。然后通过aop,将真正的实现调用js函数,将js的返回值转换后返回给rpc的consummer。

maven依赖

<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>
<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.6</version>
		</dependency>
		<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-core</artifactId>
				<version>4.3.6.RELEASE</version>
			</dependency>
			<!--其他,包括spring、dubbo之类的-->

MyAspect.java
实现aop功能,利用环绕通知,在调用服务之前,读取相应的js代码并执行,然后将结果字符串反序列化为java对象并返回。

import java.util.Collection;

import javax.annotation.PostConstruct;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import utils.Js;

@Aspect
@Component
public class MyAspect {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	@PostConstruct
	public void init() {
		logger.info("void MyAspect.init()");
	}


	/**
	 * 环绕通知
	 * 
	 * @param joinPoint
	 *            可用于执行切点的类
	 * @return
	 * @throws Throwable
	 */
	@Around("execution(* com..impl..*(..))")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		Signature signature =	joinPoint.getSignature();
		MethodSignature ms = (MethodSignature)signature;
		logger.info("方法"+signature.toString());
		Object[] args = joinPoint.getArgs();
		String arg = null;
		//String[] paramNames = ms.getParameterNames();
		if(args!=null && args.length>0){
			arg = JSONArray.toJSONString(args);
			logger.info("参数"+ arg);
		}
		//String typename = signature.getDeclaringTypeName();//类全名
		String funcName = signature.getName();//方法名
		String clazzname = joinPoint.getTarget().getClass().getSimpleName();//简单类名
		//System.out.println(clazzname);
		try{
			String res = Js.runFile("classpath:js/"+clazzname+".js", funcName, arg);
			Class<?> retType = ms.getReturnType();
			if(res==null){
				return res;
			}
			if(retType.isArray()){
				return JSONArray.parseArray(res,retType.getComponentType());
			}else if(Collection.class.isAssignableFrom(retType)){
				Type[] pts = AspectHelper.getActualReturnTypeArguments(joinPoint);
				if(pts==null){
					throw new RuntimeException("返回值类型的泛型类型未指定");
				}
				if(pts[0] instanceof WildcardType){
					throw new RuntimeException("返回值类型的泛型类型为通配符");
				}
				if(pts[0] instanceof Class){
					return JSONArray.parseArray(res,(Class) pts[0]);
				}
				throw new RuntimeException("wtf");
			}else{
				return JSONObject.parseObject(res, retType);
			}
		}catch(Exception e){
			logger.error(e.getMessage());
			Object ret = joinPoint.proceed();
			if(ret!=null){
				if(ret.getClass().isArray() || ret instanceof Collection){
					logger.info("结果"+JSONArray.toJSONString(ret));
				}else{
					logger.info("结果"+JSONObject.toJSONString(ret));
				}
			}
			return ret;
		}
	}
}

AspectHelper.java
用于获取返回值类型。包括部分有泛型类型的场景。这里并不能处理所有情况,比如返回List不指定泛型类型,或者List这样的指定具体类型没什么意义,或者泛型类型是接口等。当然这也是没有办法的事情,只能建议返回值类型不要太个性,要方便json字符串反序列化为java对象。

public class AspectHelper {
	public static Type[] getActualReturnTypeArguments(ProceedingJoinPoint joinPoint){
		Signature signature =	joinPoint.getSignature();
		MethodSignature ms = (MethodSignature)signature;
		Method method = ms.getMethod();
		Type t = method.getGenericReturnType();
		if(t instanceof ParameterizedType){
			ParameterizedType pt = (ParameterizedType) t;
			return pt.getActualTypeArguments();
		}
		return null;
	}
	public static Type[] getActualReturnTypeArguments(Method method){
		Type t = method.getGenericReturnType();
		if(t instanceof ParameterizedType){
			ParameterizedType pt = (ParameterizedType) t;
			return pt.getActualTypeArguments();
		}
		return null;
	}
	public List<AspectHelper> aaa(){
		return null;
	}
	@Test
	public void test() throws NoSuchMethodException, SecurityException, ClassNotFoundException{
		Method method = AspectHelper.class.getMethod("aaa");
		Type[] res = getActualReturnTypeArguments(method);
		System.out.println(res[0] instanceof WildcardType);
		//Class<?> c = Class.forName(res[0].getTypeName());
		System.out.println((Class)res[0]);
	}
}

Js.java
与java对象的方法对应关系:
java方法的入参,会作为数组传递给js函数。
如果java方法有0个入参,那么js函数的入参就是null。
js函数的返回值,会被序列化为json字符串,然后再被反序列化为java对象。

package utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.springframework.util.ResourceUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

public class Js {
	private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
	private static Invocable invocable = (Invocable) engine;

	/**
	 * 测试java传参到js文件,并且js将返回结果返回给java。
	 * 
	 * @throws FileNotFoundException
	 */
	//@Test
	public void testArg() throws FileNotFoundException {
		JSONObject json = new JSONObject();
		json.put("name", "avril lavigne");
		json.put("age", 110);
		json.put("like", 3.14);
		String arg = json.toJSONString();
		/**
		 * 使用spring的ResourceUtils,方便读取文件
		 */
		File file = ResourceUtils.getFile("classpath:js/testArg.js");
		String res = run(file,"main", arg);
		System.out.println(res);
	}

	/**
	 * 将给定文件的内容作为js代码执行,并且将字符串作为入参和出参。
	 * @param file
	 * @param arg
	 * @return
	 */
	public static String run(File file,String funcName, String arg) {
		try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
			StringBuilder sb = new StringBuilder();
			String line = null;
			while ((line = reader.readLine()) != null) {
				sb.append(line).append(System.lineSeparator());
			}
			return run(sb.toString(),funcName, arg);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 将给定名称的文件的内容作为js代码执行,并且将字符串作为入参和出参。
	 * @param file
	 * @param arg
	 * @return
	 */
	public static String runFile(String file,String funcName, String arg) {
		File f;
		try {
			f = ResourceUtils.getFile(file);
			return run(f,funcName, arg);
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 执行给定的js代码,并且将字符串作为入参和出参。
	 * 字符串是最通用的方式,所以设计成入参和出参都是字符串。
	 * @param js
	 * @param arg
	 * @return
	 */
	public static String run(String js,String funcName, String arg) {
		Object res;
		try {
			Object param = null;
			if (arg != null) {
				try{
					param = JSONObject.parseObject(arg);
				}catch(Exception e){
					param = JSONArray.parseArray(arg);
				}
			}
			engine.eval(js);
			res = invocable.invokeFunction(funcName, param);
			if (res == null) {
				return null;
			}
			return res.toString();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

src/main/recources/js/com.zhou.impl.MockMyService.js

/**
 * @param arg java调用时传进来的对象
 * @return JSON字符串 
 * */
function queryUser(arg){
	print('abc');
	print(arg.name);
	var res = {
			a:'A',
			b:'B',
			c:[1,2,3]
	};
	return JSON.stringify(res);
}
function f(){
	
}

猜你喜欢

转载自blog.csdn.net/zhoujiaping123/article/details/82770362
今日推荐