手动调用指定Dubbo接口(一)----Spring注入篇

63 篇文章 11 订阅

初衷

能不能输入任意的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接口的文章:

手动调用指定Dubbo接口(一)----Spring注入篇

手动调用指定Dubbo接口(二)----Reference篇

手动调用指定Dubbo接口(三)----GenericService篇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值