正常如果由于一些网络权限限制导致的无法远程调用dubbo进行测试的时候,可以直接在dubbo服务所在的机器上通过telnet来进行一个invoke的调用执行,今天在测试一个接口的时候,参数传入了一个枚举,结果报了一个错误
如果想要重现,可以直接创建一个dubbo工程,然后进行telnet调用,dubbo工程可参考:http://lihuia.com/dubbo%e8%9e%8d%e5%90%88nacos%e6%b3%a8%e5%86%8c%e4%b8%ad%e5%bf%83/
dubbo服务启动之后,invoke报错
lihui@2019 telnet 127.0.0.1 12345 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. dubbo>ls PROVIDER: com.lihuia.DemoService dubbo>invoke com.lihuia.DemoService.sayHello({"class":"com.lihuia.StudentEnum", "student":"LUCY"}) Failed to invoke method sayHello, cause: java.lang.IllegalArgumentException: Cannot reflectively create enum objects java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at org.apache.dubbo.common.utils.PojoUtils.newInstance(PojoUtils.java:547) at org.apache.dubbo.common.utils.PojoUtils.realize0(PojoUtils.java:446) at org.apache.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:205) at org.apache.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:95) at org.apache.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler.telnet(InvokeTelnetHandler.java:125) at org.apache.dubbo.remoting.telnet.support.TelnetHandlerAdapter.telnet(TelnetHandlerAdapter.java:59) at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:205) at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51) at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) dubbo>
本来想放弃的,随手看了下堆栈,似乎找到了些线索
先看看InvokeTelnetHandler类的telnet方法,这里具体还列出来了invoke用法
public class InvokeTelnetHandler implements TelnetHandler {
public static final String INVOKE_MESSAGE_KEY = "telnet.invoke.method.message";
public static final String INVOKE_METHOD_LIST_KEY = "telnet.invoke.method.list";
public static final String INVOKE_METHOD_PROVIDER_KEY = "telnet.invoke.method.provider";
@Override
@SuppressWarnings("unchecked")
public String telnet(Channel channel, String message) {
if (StringUtils.isEmpty(message)) {
return "Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\n" +
"invoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\n" +
"invoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})";
}
可以打一个断点,调试得到的异常是这里抛出来的
紧接着具体看下realize里面的内容
包org.apache.dubbo.common.utils的PojoUtils类里
public static Object[] realize(Object[] objs, Class<?>[] types, Type[] gtypes) {
if (objs.length != types.length || objs.length != gtypes.length) {
throw new IllegalArgumentException("args.length != types.length");
}
Object[] dests = new Object[objs.length];
for (int i = 0; i < objs.length; i++) {
dests[i] = realize(objs[i], types[i], gtypes[i]);
}
return dests;
}
执行到了for循环里面的realize
同样还是在这个类里
public static Object realize(Object pojo, Class<?> type, Type genericType) {
return realize0(pojo, type, genericType, new IdentityHashMap<Object, Object>());
}
具体realize0这个方法比较长,就不贴了,具体有下面一段
invoke执行的sayHello方法,sayHello({“class”:”com.lihuia.StudentEnum”, “student”:”LUCY”})的参数是一个map,第一个传入了参数的class类型,而第二个的键并不是参数变量名,而是写死了一个name,抱着半信半疑的态度试了一下,居然还真正确了
dubbo>invoke com.lihuia.DemoService.sayHello({"class":"com.lihuia.StudentEnum", "name":"LUCY"}) Use default service com.lihuia.DemoService. result: "Hello! My Name is LUCY" elapsed: 8 ms. dubbo> dubbo>
这里的dubbo版本是2.7.3,第一次进dubbo的实现代码,还好这个问题相对比较直接