上一篇文章我们实用JDK原生API构造了简单RMI应用,本篇将实用Spring框架来构造RMI的应用,实用Spring你会体验到简单,不需要那么多的条条框框,因为Spring给你做了很多封装。
项目构架
首先看下我的Demo构架图:
示例代码
-
远程接口IHello.java
package net.oseye.DemoSpringRMI.RemotInterface; /** * 远程接口 * @author oseye */ public interface IHello { String sayHello(String name); }
-
服务器端和客户端需要在pom.xml(我使用了Maven构建项目,你也可以手动添加依赖)添加Spring的context依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.3.RELEASE</version> </dependency>
并引用远程接口项目进来,服务器端HelloImpl.java
package net.oseye.DemoSpringRMI.Server; import net.oseye.DemoSpringRMI.RemotInterface.IHello; public class HelloImpl implements IHello { public String sayHello(String name) { return "Hello,"+name; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="helloService" class="net.oseye.DemoSpringRMI.Server.HelloImpl"></bean> <bean class="org.springframework.remoting.rmi.RmiServiceExporter"> <property name="serviceName" value="HelloService"></property> <property name="service" ref="helloService"/> <property name="serviceInterface" value="net.oseye.DemoSpringRMI.RemotInterface.IHello"/> <property name="registryPort" value="1199"/> </bean> </beans>
主程序App.javapackage net.oseye.DemoSpringRMI.Server; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { @SuppressWarnings("resource") public static void main( String[] args ) { new ClassPathXmlApplicationContext("applicationcontext.xml"); System.out.println("服务器启动成功"); } }
- 客户端同样需要添加spring-context依赖和引用远程接口项目(开发过程中服务器和客户都需要获得远程接口,否则无法进行,远程接口是相互协商的结果),Spring配置文件applicationcontext.xml如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="helloService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> <property name="serviceUrl" value="rmi://10.9.146.113:1199/HelloService"/> <property name="serviceInterface" value="net.oseye.DemoSpringRMI.RemotInterface.IHello"/> </bean> </beans>
package net.oseye.DemoSpringRMI.Client; import java.rmi.RemoteException; import net.oseye.DemoSpringRMI.RemotInterface.IHello; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { @SuppressWarnings("resource") public static void main(String[] args) throws RemoteException { ApplicationContext context = new ClassPathXmlApplicationContext( "applicationcontext.xml"); IHello stub = (IHello) context.getBean("helloService"); System.out.println(stub.sayHello("kevin")); } }
-
运行
至此服务器端和客户端开发完成,可以在Eclipse上多窗口运行服务器端和客户端。
红色报警信息是因为我没有配置log4j。
问题和办法
-
问题
客户端和服务器在Eclipse上成功运行,但当我把它们打成可执行Jar的时候,却报告了类似这样的异常WARNING: Ignored XML validation warning
org.xml.sax.SAXParseException; lineNumber: 6; columnNumber: 120; schema_reference.4: 无法读取方案文档 'http://www.springframework.org/schema/beans/spring-beans.xsd', 原因为 1) 无法找到文档; 2) 无法读取文档; 3) 文档的根元素不是。 - 原因
异常描述得很清楚,是无法读取“http://www.springframework.org/schema/beans/spring-beans.xsd”。
还记得applicationcontext.xml配置文件吗?它有段是这样的<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
.... http\://www.springframework.org/schema/lang/spring-lang-3.0.xsd=org/springframework/scripting/config/spring-lang-3.0.xsd http\://www.springframework.org/schema/lang/spring-lang-3.1.xsd=org/springframework/scripting/config/spring-lang-3.1.xsd http\://www.springframework.org/schema/lang/spring-lang-3.2.xsd=org/springframework/scripting/config/spring-lang-3.2.xsd http\://www.springframework.org/schema/lang/spring-lang-4.0.xsd=org/springframework/scripting/config/spring-lang-4.0.xsd http\://www.springframework.org/schema/lang/spring-lang.xsd=org/springframework/scripting/config/spring-lang-4.0.xsd http\://www.springframework.org/schema/task/spring-task-3.0.xsd=org/springframework/scheduling/config/spring-task-3.0.xsd http\://www.springframework.org/schema/task/spring-task-3.1.xsd=org/springframework/scheduling/config/spring-task-3.1.xsd http\://www.springframework.org/schema/task/spring-task-3.2.xsd=org/springframework/scheduling/config/spring-task-3.2.xsd http\://www.springframework.org/schema/task/spring-task-4.0.xsd=org/springframework/scheduling/config/spring-task-4.0.xsd .....
本例异常的原因就清晰了:spring首先在spring.schemas搜索命名空间“http://www.springframework.org/schema/beans/spring-beans.xsd”对应的本地验证模式路径,没有找到,于是去网上下载,但我的PC联不了外网,造成无法读取"http://www.springframework.org/schema/beans/spring-beans.xsd"。
那么我的eclipse为什么可以运行呢?因为我的Eclipse使用了代理上网。 - 办法
- 在能联公网的情况下运行服务器和客户端;
- 建立提供验证模式文件的内部服务,修改host;
- 使用本地资源:
其实命名空间“http://www.springframework.org/schema/beans”在项目引用的“Spring-beans-4.0.3.Release.jar”里的"spring.schemas"中有,而打包后只把“Spring-context-4.0.3.Release.jar”里的"spring.schemas"打到包里了,估计是因为两个重名吧。查看“Spring-beans-4.0.3.Release.jar”里的"spring.schemas"可以看到:http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans classpath:org/springframework/beans/factory/xml/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
重新打包即可。关于命名空间的深入讨论请点击这里:《Java使用Schema模式对XML验证》。