All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rpc.turbo.invoke.JavassistInvoker Maven / Gradle / Ivy

The newest version!
package rpc.turbo.invoke;

import static rpc.turbo.util.SourceCodeUtils.box;
import static rpc.turbo.util.SourceCodeUtils.forceCast;
import static rpc.turbo.util.SourceCodeUtils.unbox;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import rpc.turbo.param.EmptyMethodParam;
import rpc.turbo.param.MethodParam;
import rpc.turbo.param.MethodParamClassFactory;
import rpc.turbo.util.SingleClassLoader;
import rpc.turbo.util.TypeUtils;

/**
 * high performance invoker
 * 
 * @author [email protected]
 *
 * @param 
 *            the method return type
 */
public class JavassistInvoker implements Invoker {
	private static final String NOT_SUPPORT_PARAMETER_NAME_MSG = "must turn on \"Store information about method parameters (usable via reflection)\", see https://www.concretepage.com/java/jdk-8/java-8-reflection-access-to-parameter-names-of-method-and-constructor-with-maven-gradle-and-eclipse-using-parameters-compiler-argument";

	final int serviceId;
	private final Object service;
	final Class clazz;
	final Method method;
	private final Class[] parameterTypes;
	private final String[] parameterNames;
	private final int parameterCount;

	final Class methodParamClass;
	private final Invoker realInvoker;

	private final boolean supportHttpForm;

	/**
	 * 
	 * @param serviceId
	 * 
	 * @param service
	 *            the service implemention
	 * 
	 * @param clazz
	 *            the service interface
	 * 
	 * @param method
	 * @throws InvokeException
	 */
	public JavassistInvoker(int serviceId, Object service, Class clazz, Method method) {
		this.serviceId = serviceId;
		this.service = service;
		this.clazz = clazz;
		this.method = method;
		this.parameterTypes = method.getParameterTypes();
		this.parameterCount = parameterTypes.length;

		if (service == null) {
			throw new InvokeException("service cannot be null");
		}

		if (!clazz.equals(method.getDeclaringClass())) {
			throw new InvokeException(clazz + " have no method: " + method);
		}

		if (!Modifier.isPublic(clazz.getModifiers())) {
			throw new InvokeException("the method must be public");
		}

		try {
			methodParamClass = MethodParamClassFactory.createClass(method);
		} catch (Exception e) {
			throw new InvokeException(e);
		}

		try {
			this.realInvoker = generateRealInvoker();
		} catch (Exception e) {
			throw new InvokeException(e);
		}

		boolean _supportHttpForm = true;
		for (int i = 0; i < parameterCount; i++) {
			Class paramType = parameterTypes[i];

			if (!TypeUtils.supportCast(paramType)) {
				_supportHttpForm = false;
				break;
			}
		}

		supportHttpForm = _supportHttpForm;

		parameterNames = new String[parameterCount];
		Parameter[] parameters = method.getParameters();
		for (int i = 0; i < parameterCount; i++) {
			Parameter parameter = parameters[i];

			if (!parameter.isNamePresent()) {
				throw new RuntimeException(NOT_SUPPORT_PARAMETER_NAME_MSG);
			}

			parameterNames[i] = parameter.getName();
		}
	}

	/**
	 * 
	 * @throws InvokeException
	 */
	@Override
	public T invoke(Object... params) {
		if (params == null) {
			if (parameterCount != 0) {
				throw new InvokeException(method.getName() + " params count error, params is null");
			}
		} else if (parameterCount != params.length) {
			throw new InvokeException(method.getName() + " params count error, " + Arrays.toString(params));
		}

		return realInvoker.invoke(params);
	}

	@Override
	public T invoke() {
		return realInvoker.invoke();
	}

	public T invoke(MethodParam methodParam) {
		if (methodParam == null || methodParam instanceof EmptyMethodParam) {
			return realInvoker.invoke();
		}

		if (!methodParamClass.isInstance(methodParam)) {
			throw new IllegalArgumentException("methodParam not instanceof " + methodParamClass.getName());
		}

		return realInvoker.invoke(methodParam);
	}

	@Override
	public T invoke(Object params0) {
		return realInvoker.invoke(params0);
	}

	@Override
	public T invoke(Object param0, Object param1) {
		return realInvoker.invoke(param0, param1);
	}

	@Override
	public T invoke(Object param0, Object param1, Object param2) {
		return realInvoker.invoke(param0, param1, param2);
	}

	@Override
	public T invoke(Object param0, Object param1, Object param2, Object param3) {
		return realInvoker.invoke(param0, param1, param2, param3);
	}

	@Override
	public T invoke(Object param0, Object param1, Object param2, Object param3, Object param4) {
		return realInvoker.invoke(param0, param1, param2, param3, param4);
	}

	@Override
	public T invoke(Object param0, Object param1, Object param2, Object param3, Object param4, Object param5) {
		return realInvoker.invoke(param0, param1, param2, param3, param4, param5);
	}

	@Override
	public int getServiceId() {
		return serviceId;
	}

	@Override
	public Method getMethod() {
		return method;
	}

	@Override
	public Class[] getParameterTypes() {
		return parameterTypes;
	}

	@Override
	public String[] getParameterNames() {
		return parameterNames;
	}

	@Override
	public Class getMethodParamClass() {
		return methodParamClass;
	}

	@Override
	public boolean supportHttpForm() {
		return supportHttpForm;
	}

	private Invoker generateRealInvoker() throws Exception {
		final String invokerClassName = "rpc.turbo.invoke.generate.Invoker_"//
				+ serviceId + "_" //
				+ UUID.randomUUID().toString().replace("-", "");

		// 创建类
		ClassPool pool = ClassPool.getDefault();
		CtClass invokerCtClass = pool.makeClass(invokerClassName);
		invokerCtClass.setInterfaces(new CtClass[] { pool.getCtClass(Invoker.class.getName()) });

		// 添加私有成员service
		CtField serviceField = new CtField(pool.get(service.getClass().getName()), "service", invokerCtClass);
		serviceField.setModifiers(Modifier.PRIVATE | Modifier.FINAL);
		invokerCtClass.addField(serviceField);

		// 添加有参的构造函数
		CtConstructor constructor = new CtConstructor(new CtClass[] { pool.get(service.getClass().getName()) },
				invokerCtClass);
		constructor.setBody("{$0.service = $1;}");
		invokerCtClass.addConstructor(constructor);

		{// 添加MethodParam方法
			StringBuilder methodBuilder = new StringBuilder();
			StringBuilder resultBuilder = new StringBuilder();

			methodBuilder.append("public Object invoke(rpc.turbo.param.MethodParam methodParam) {\r\n");

			if (parameterTypes.length > 0) {
				// 强制类型转换
				methodBuilder.append(methodParamClass.getName());
				methodBuilder.append("  params = (");
				methodBuilder.append(methodParamClass.getName());
				methodBuilder.append(")methodParam;");
			}

			methodBuilder.append("  return ");

			resultBuilder.append("service.");
			resultBuilder.append(method.getName());
			resultBuilder.append("(");

			for (int i = 0; i < parameterTypes.length; i++) {
				resultBuilder.append("params.$param");
				resultBuilder.append(i);
				resultBuilder.append("()");

				if (i != parameterTypes.length - 1) {
					methodBuilder.append(", ");
				}
			}

			resultBuilder.append(")");

			String resultStr = box(method.getReturnType(), resultBuilder.toString());

			methodBuilder.append(resultStr);
			methodBuilder.append(";\r\n}");

			CtMethod m = CtNewMethod.make(methodBuilder.toString(), invokerCtClass);
			invokerCtClass.addMethod(m);
		}

		{// 添加通用方法
			StringBuilder methodBuilder = new StringBuilder();
			StringBuilder resultBuilder = new StringBuilder();

			methodBuilder.append("public Object invoke(Object[] params) {\r\n");

			methodBuilder.append("  return ");

			resultBuilder.append("service.");
			resultBuilder.append(method.getName());
			resultBuilder.append("(");

			for (int i = 0; i < parameterTypes.length; i++) {
				Class paramType = parameterTypes[i];

				resultBuilder.append("((");
				resultBuilder.append(forceCast(paramType));

				resultBuilder.append(")params[");
				resultBuilder.append(i);
				resultBuilder.append("])");

				resultBuilder.append(unbox(paramType));
			}

			resultBuilder.append(")");

			String resultStr = box(method.getReturnType(), resultBuilder.toString());

			methodBuilder.append(resultStr);
			methodBuilder.append(";\r\n}");

			CtMethod m = CtNewMethod.make(methodBuilder.toString(), invokerCtClass);
			invokerCtClass.addMethod(m);
		}

		if (parameterCount <= 6) {// just for benchmark
			StringBuilder methodBuilder = new StringBuilder();
			StringBuilder resultBuilder = new StringBuilder();

			methodBuilder.append("public Object invoke(");

			String params = IntStream//
					.range(0, parameterCount)//
					.mapToObj(i -> "Object param" + i)//
					.collect(Collectors.joining(","));

			methodBuilder.append(params);

			methodBuilder.append(") {\r\n");

			methodBuilder.append("  return ");

			resultBuilder.append("service.");
			resultBuilder.append(method.getName());
			resultBuilder.append("(");

			for (int i = 0; i < parameterCount; i++) {
				Class paramType = parameterTypes[i];

				resultBuilder.append("((");
				resultBuilder.append(forceCast(paramType));

				resultBuilder.append(")param");
				resultBuilder.append(i);
				resultBuilder.append(")");

				resultBuilder.append(unbox(paramType));

				if (i != parameterCount - 1) {
					resultBuilder.append(",");
				}
			}

			resultBuilder.append(")");

			String resultStr = box(method.getReturnType(), resultBuilder.toString());

			methodBuilder.append(resultStr);
			methodBuilder.append(";\r\n}");

			CtMethod m = CtNewMethod.make(methodBuilder.toString(), invokerCtClass);
			invokerCtClass.addMethod(m);
		}

		byte[] bytes = invokerCtClass.toBytecode();
		Class invokerClass = SingleClassLoader.loadClass(service.getClass().getClassLoader(), bytes);

		// 通过反射创建有参的实例
		@SuppressWarnings("unchecked")
		Invoker invoker = (Invoker) invokerClass.getConstructor(service.getClass()).newInstance(service);

		return invoker;
	}

	@Override
	public int hashCode() {
		return serviceId;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;

		if (obj == null)
			return false;

		if (getClass() != obj.getClass())
			return false;

		JavassistInvoker other = (JavassistInvoker) obj;
		if (serviceId != other.serviceId)
			return false;

		return true;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy