初衷
能不能输入任意的dubbo接口名字,输入参数,然后就可以调用这个接口呢?
对于REST风格的接口,我们有Postman,Advanced REST client等一堆工具可以用,但对于dubbo接口却没有直接并且通用的调用方式。当然也可以写针对某个接口的测试调用方法,但是不同dubbo接口的调用方法不统一,新增一个接口就要新增对这个接口的测试调用方法,很麻烦。
思路
在dubbo接口的provider端和consumer端都已经注册好的情况下,在consumer端拿到接口注册的spring上下文,从上下文中拿到接口的代理,然后就可以依照输入的参数调用这个方法,这个操作就相当于consumer端调用了这个dubbo接口,dubbo会完成后续的工作(比如找provider,调用,返回信息等)。
简介
只要给方法传入bean的名字,方法名字,参数,就可以调用对应的dubbo接口了。
只要注册好就可以调用,是一个通用的方法。
代码的主体是testDubboService(String service, String method, String data)方法,这个方法接收了三个参数,分别是:
1,service,bean名。
dubbo接口在consumer端spring上下文中注册的bean的名字,也是他的id,比如:
<dubbo:reference id="testService" interface="com.service.TestService" protocol="dubbo" timeout="300000" check="false"/>
这种注册方式,参数service就是testService。
2,method,方法名。不含参数部分。
3,data,参数。包装类型使用JSON格式,其他类型直接就是参数本身。如果方法有多个参数,data用换行分隔。
使用
因为主体只是一个java的方法,所以可以有很多种使用方式,比如:
1,在单纯的spring的服务中使用,可以在这个方法外面包装一个REST接口,然后用调用REST接口的方式去调用他。
2,在webservice中,可以用一个测试页面给这个方法传递参数。
我采用了第二种方式写了一个测试页面,并把这个方法放在了controller里,这个页面打开是这样的:
就不贴代码了。
不足
1,这个方法并没有写完,只包含了平时常见的参数类型(比如泛型类就只写了List和Map参数的拼装),另外当接口的参数是(? extends xxx)这种格式或者使用了泛型类中的泛型时,拼装方法没有写。不过想扩展这个方法还是比较容易的。
2,对于方法的识别,只用了方法名和参数个数,对于某些Java重载的情况不能准确识别(就是方法名相同,参数个数相同,但参数类型不同的情况)。要解决这个问题也可以,额外增加一个参数,输入参数类型。
使用举例
比如有consumer端有以下注册:
<dubbo:reference id="testService" interface="com.service.TestService" protocol="dubbo" timeout="300000" check="false"/>
在TestService接口中有个方法:
public void getOrder(String id,Order order)
其中Order类的定义:
public class Order {
private int id;
private String code;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
那么我们输入的参数可以是这样:
service参数:testService
method参数:getOrder
data参数:
testid
{"id": 123,"code": "abcde"}
data用两行来代表两个参数
注
1,下面贴的代码的大部分篇幅都在根据data参数拼装dubbo接口的入参,就是各种反射。
2,在研究这段代码的过程中把java的Type接口好好的学习了一下,学习笔记在最后。
最后是代码
package com.action;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
@Controller
@RequestMapping("/TestDubboController")
public class TestDubboController {
@RequestMapping("toTestDubboService")
public ModelAndView toTestDubboService(String service, String method,String data) throws Exception {
return new ModelAndView("jsp/testDubboService");
}
/**
* /toTestDubboService.do
*
* @param service
* 在spring配置文件中注册的id
* @param method
* 方法名
* @param data
* 方法传入参数
* @throws Exception
*/
@RequestMapping("testDubboService")
public void testDubboService(String service, String method, String data)
throws Exception {
System.out.println("service---" + service);
System.out.println("method---" + method);
System.out.println("data---" + data);
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
Object obj = wac.getBean(service);
Field handlerField = obj.getClass().getDeclaredField("handler");
handlerField.setAccessible(true);
com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler invokerInvocationHandler = (com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler) handlerField.get(obj);
Field invokerField = invokerInvocationHandler.getClass().getDeclaredField("invoker");
invokerField.setAccessible(true);
com.alibaba.dubbo.rpc.Invoker invoker = (com.alibaba.dubbo.rpc.Invoker) invokerField.get(invokerInvocationHandler);
Class invokerInterfaceClass = invoker.getInterface();
Field failoverClusterInvokerField = invoker.getClass().getDeclaredField("invoker");
failoverClusterInvokerField.setAccessible(true);
// 当方法只有一个String类型参数时的处理
// Method oneMethod=failoverClusterInvokerInterfaceClass.getMethod(method,String.class);
// Object result=oneMethod.invoke(obj, data);//执行
// 当方法的参数比较复杂时的处理
Object[] resultArr = disposeParameter(invokerInterfaceClass, method,data);
Method oneMethod = (Method) resultArr[0];
Object[] parameterArr = (Object[]) resultArr[1];
Object result = oneMethod.invoke(obj, parameterArr);// 执行
if (result != null) {
Class resultClass = result.getClass();
System.out.println(resultClass.getName());
}
}
/**
* 根据传入的字符串确定要执行的Method,并初始化Method的参数
*
* @param targetClass
* 目标类
* @param method
* 方法名
* @param data
* 传入参数
* @return 长度为2的数组,第一个元素是要执行的Method实例,第二个元素是要的执行的参数数组
*/
public Object[] disposeParameter(Class targetClass, String method,String data) {
Object[] resultArr = new Object[2];
String[] dataArr = data.split("\n");
Method[] methodArr = targetClass.getMethods();
for (int i = 0; i < methodArr.length; i++) {
Method m = methodArr[i];
if (m.getName().equals(method)
&& m.getGenericParameterTypes().length == dataArr.length) {// 命中方法,只校验了名字和参数数量
try {
Type[] typeArr = m.getGenericParameterTypes();// 目标方法的参数列表
resultArr[0] = m;
Object[] parameterArr = new Object[typeArr.length];// 需要赋值的参数列表
for (int j = 0; j < typeArr.length; j++) {// 循环目标方法的参数列表,赋值用
String oneData = dataArr[j]; // 输入的第j行信息
Type parameterClass = typeArr[j];// 目标方法的第j个参数
parameterArr[j] = parseToObject(parameterClass, oneData);
}
resultArr[1] = parameterArr;
} catch (Exception e) {
e.printStackTrace();
}
}
}
return resultArr;
}
/**
* JSON字符串转换为目标对象
*
* @param type
* 目标对象类型
* @param oneData
* 字符串
* @return
*/
public static Object parseToObject(Type type, String oneData) {
Object resultObject = new Object();
if (type instanceof Class) { // 普通类
Class parameterClass = (Class) type;
if (parameterClass.equals(String.class)) { // 参数是String类型
resultObject = oneData;
} else if (parameterClass.equals(int.class)) { // 参数是int类型
resultObject = Integer.parseInt(oneData);
} else if (parameterClass.equals(Integer.class)) { // 参数是Integer类型
resultObject = Integer.valueOf(oneData);
} else if (parameterClass.equals(Date.class)) { // 参数是Date类型
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date oneDate = sdf.parse(oneData);
resultObject = oneDate;
} catch (ParseException e) {
e.printStackTrace();
}
} else if (parameterClass.getName().startsWith("[L")) { // 参数是数组类型,而且数组元素是普通类,字符串参数应该使用JSON的列表格式
resultObject = parseToArray(type, oneData);
} else { // 参数是包装类型,字符串参数应该用JSON格式
Object newInstance = parseToOther(type, oneData);
resultObject = newInstance;
}
} else if (type instanceof ParameterizedType) { // 泛型类
ParameterizedType parameterType = (ParameterizedType) type;
Class parameterClass = (Class) parameterType.getRawType();
if (parameterClass.equals(List.class)) { // 参数是List类型,字符串参数应该使用JSON的列表格式
List newInstance = parseToList(parameterType, oneData);
resultObject = newInstance;
} else if (parameterClass.equals(Map.class)) { // 参数是Map类型,字符串参数应该使用JSON的列表格式
Map newInstance = parseToMap(parameterType, oneData);
resultObject = newInstance;
}
} else if (type instanceof GenericArrayType) { // 泛型类数组
String className = ((Class) ((ParameterizedType) ((GenericArrayType) type).getGenericComponentType()).getRawType()).getName();// 数组组件类名
if (className.equals("java.util.List")) { // List数组
resultObject = parseToListArray(type, oneData);
} else if (className.equals("java.util.Map")) { // Map数组
resultObject = parseToMapArray(type, oneData);
}
} else if (type instanceof WildcardType) {// (? extends xxx)形式
} else if (type instanceof TypeVariable) {// 引用泛型类中的泛型
}
return resultObject;
}
/**
* JSON字符串转换为自定义对象,根据JSON中的key和value来初始化对象属性
*
* @param type
* 对象类型
* @param oneData
* 字符串
* @return
*/
public static Object parseToOther(Type type, String oneData) {
Class parameterClass = (Class) type;
Object newInstance;
try {
newInstance = parameterClass.newInstance();// 新建目标方法的实例
} catch (InstantiationException e2) {
e2.printStackTrace();
return null;
} catch (IllegalAccessException e2) {
e2.printStackTrace();
return null;
}
JSONObject oneJsonObject = (JSONObject) JSON.parse(oneData);
Iterator<Entry<String, Object>> iter = oneJsonObject.entrySet().iterator();
while (iter.hasNext()) {// 循环JSON字符串,初始化对应的参数
Entry<String, Object> entry = iter.next();
String key = entry.getKey();
Object value = entry.getValue();
try {
Field classField = parameterClass.getDeclaredField(key);
classField.setAccessible(true);
classField.set(newInstance, value);
} catch (NoSuchFieldException e) {// 找不到这个属性,再从父类中找这个属性,没有继续往上迭代,只找了直接父类
System.out.println("没有这个属性" + key);
Class superClass = parameterClass.getSuperclass();
try {
Field classField = superClass.getDeclaredField(key);
classField.setAccessible(true);
classField.set(newInstance, value);
} catch (NoSuchFieldException e1) {
System.out.println("父类也没有这个属性" + key);
} catch (Exception e1) {
e1.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return newInstance;
}
/**
* JSON字符串转换为List对象,根据List的泛型类初始化对象
*
* @param type
* List的类型
* @param oneData
* 字符串
* @return
*/
public static List parseToList(Type type, String oneData) {
ParameterizedType parameterType = (ParameterizedType) type;
Type[] types = parameterType.getActualTypeArguments();
Class parameterClass = (Class) types[0];
List list = JSON.parseArray(oneData, parameterClass);
return list;
}
/**
* JSON字符串转换为Map对象,根据Map泛型类型初始化对象
*
* @param type
* Map的类型
* @param oneData
* 字符串
* @return
*/
public static Map parseToMap(Type type, String oneData) {
ParameterizedType parameterType = (ParameterizedType) type;
Type[] types = parameterType.getActualTypeArguments();
Map map = new HashMap();
JSONArray jsonArray = JSON.parseArray(oneData);
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject obj = (JSONObject) jsonArray.get(i);
for (String key : obj.keySet()) {
String value = obj.getString(key);
Object keyObj = parseToObject(types[0], key);
Object valueObj = parseToObject(types[1], value);
map.put(keyObj, valueObj);
}
}
return map;
}
/**
* JSON字符串转换为数组对象,根据数组元素类型初始化对象
*
* @param type
* 数组的class类型
* @param oneData
* 字符串
* @return
*/
public static Object[] parseToArray(Type type, String oneData) {
Class parameterClass = (Class) type;
Class elementClass = parameterClass.getComponentType();
List list = JSON.parseArray(oneData, Object.class);
/*
* Object[] resultObjects=new Object[list.size()];
* 用这种方式定义出来的数组类型就是Object[]
*
* 而用java.lang.reflect.Array定义出来的数组类型就是指定类型
*/
Object[] resultObjects = (Object[]) java.lang.reflect.Array.newInstance(elementClass, list.size());
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
Object resultObj = parseToObject(elementClass,JSON.toJSONString(obj));
resultObjects[i] = resultObj;
}
return resultObjects;
}
/**
* JSON字符串转换为List数组(泛型数组)对象,根据数组元素类型初始化对象
*
* @param type
* 数组的class类型
* @param oneData
* 字符串
* @return
*/
public static Object[] parseToListArray(Type type, String oneData) {
GenericArrayType parameterType = (GenericArrayType) type;
Type elementType = parameterType.getGenericComponentType();
ParameterizedType parameterizedType = (ParameterizedType) elementType;
Type[] elementTypes = parameterizedType.getActualTypeArguments();
List list = JSON.parseArray(oneData);
Object[] resultObjects = (Object[]) java.lang.reflect.Array.newInstance((Class) parameterizedType.getRawType(),list.size());
for (int i = 0; i < list.size(); i++) {
JSONArray objArray = (JSONArray) list.get(i);
List elementList = JSON.parseArray(objArray.toJSONString(),(Class) elementTypes[0]);
resultObjects[i] = elementList;
}
return resultObjects;
}
/**
* JSON字符串转换为Map数组(泛型数组)对象,根据数组元素类型初始化对象
*
* @param type
* 数组的class类型
* @param oneData
* 字符串
* @return
*/
public static Object[] parseToMapArray(Type type, String oneData) {
GenericArrayType parameterType = (GenericArrayType) type;
Type elementType = parameterType.getGenericComponentType();
ParameterizedType parameterizedType = (ParameterizedType) elementType;
Type[] elementTypes = parameterizedType.getActualTypeArguments();
List list = JSON.parseArray(oneData);
Object[] resultObjects = (Object[]) java.lang.reflect.Array.newInstance((Class) parameterizedType.getRawType(),list.size());
for (int i = 0; i < list.size(); i++) {
JSONArray objArray = (JSONArray) list.get(i);
Map map = new HashMap();
for (int j = 0; j < objArray.size(); j++) {
JSONObject obj = (JSONObject) objArray.get(j);
for (String key : obj.keySet()) {
String value = obj.getString(key);
Object keyObj = parseToObject(elementTypes[0], key);
Object valueObj = parseToObject(elementTypes[1], value);
map.put(keyObj, valueObj);
}
}
resultObjects[i] = map;
}
return resultObjects;
}
}
Java的Type接口的学习笔记:
https://blog.csdn.net/lkforce/article/details/82466893
三篇手动调用指定Dubbo接口的文章: