基于Java RMI与Reflection实现无接口Broker的异常排查
问题:Java RMI Broker无接口反射调用抛出NoSuchMethodException
问题描述
开发跨硬件部署的Java Broker,已实现服务器向Broker注册服务、客户端通过Broker调用服务器方法的功能。为取消对共享Server接口的依赖,采用Java Reflection技术实现无接口版本后,在Broker的execute_service方法中执行Method method = serverClass.getMethod(service_name);时,抛出以下错误:
java.lang.NoSuchMethodException: com.sun.proxy.$Proxy1.getArticles()
使用共享Server接口时可正常运行,但希望避免依赖该接口。
核心原因
通过RMI的Naming.lookup()获取的服务器对象是动态代理类(com.sun.proxy.$Proxy1),该代理仅实现了Remote接口,并未包含Server自定义的业务方法(如getArticles())。因此直接通过代理类的getMethod()无法找到目标方法。
解决方案
方案1:利用注册时的方法签名直接构造调用(无需共享Server类)
在服务注册时,已传递了方法名、参数类型列表和返回类型,可通过RMI代理的InvocationHandler直接转发调用,绕过代理类方法查找的限制:
public Response execute_service(Vector service_parameters) throws MalformedURLException, RemoteException, NotBoundException, NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException { Object server = Naming.lookup("//"+ this.remote_host_IP_port + "/" + this.server_name); // 从注册信息中获取参数类型数组 Class<?>[] paramTypes = parameter_list.toArray(new Class[0]); // 根据注册时的返回类型字符串转换为Class对象 Class<?> returnType = getReturnTypeFromRegistration(); // 构造Method对象 MethodType methodType = MethodType.methodType(returnType, paramTypes); Method method = MethodHandles.lookup() .findVirtual(Object.class, service_name, methodType) .getMethod(); // 转换参数 Object[] parameters = new Object[service_parameters.size()]; for (int i = 0; i < service_parameters.size(); i++) { try { parameters[i] = convertToType((String) service_parameters.get(i), paramTypes[i]); } catch (IllegalArgumentException e) { System.err.println("Parameter " + i + " could not be transformed: " + e.getMessage()); return null; } } // 通过InvocationHandler直接调用远程方法 InvocationHandler handler = Proxy.getInvocationHandler(server); Object result = handler.invoke(server, method, parameters); return new Response(result); } // 辅助方法:将注册时的返回类型字符串转换为Class private Class<?> getReturnTypeFromRegistration() throws ClassNotFoundException { switch (this.return_type) { case "void": return void.class; case "int": return int.class; case "String": return String.class; default: return Class.forName(this.return_type); } }
方案2:Broker端引入Server类(若允许类共享)
如果Broker的类路径可以访问Server的class文件,可直接加载Server类并获取方法,再调用代理对象:
public Response execute_service(Vector service_parameters) throws MalformedURLException, RemoteException, NotBoundException, NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException { Object server = Naming.lookup("//"+ this.remote_host_IP_port + "/" + this.server_name); // 加载Server的真实类(替换为实际包名+类名) Class<?> serverClass = Class.forName("Server2"); Class<?>[] paramTypes = parameter_list.toArray(new Class[0]); Method method = serverClass.getMethod(service_name, paramTypes); // 转换参数 Object[] parameters = new Object[service_parameters.size()]; for (int i = 0; i < service_parameters.size(); i++) { try { parameters[i] = convertToType((String) service_parameters.get(i), paramTypes[i]); } catch (IllegalArgumentException e) { System.err.println("Parameter " + i + " could not be transformed: " + e.getMessage()); return null; } } // 调用方法,RMI代理会自动转发到远程Server Object result = method.invoke(server, parameters); return new Response(result); }
修复现有代码的参数类型错误
原代码中处理有参数方法时存在逻辑错误,需替换错误的参数类型获取方式:
// 错误代码:传递的是Vector.class,而非实际参数类型数组 // Method method = serverClass.getMethod(service_name, parameter_list.getClass()); // 修正后代码 Class<?>[] paramTypes = parameter_list.toArray(new Class[0]); Method method = serverClass.getMethod(service_name, paramTypes);
内容的提问来源于stack exchange,提问作者Andrei Vlasceanu
相关产品推荐
相关产品推荐

