RMI服务器:rmiregistry或者LocateRegistry.createRegistry
对于服务器端的RMI,我们是否需要启动rmiregistry
程序,或者只是调用LocateRegistry.createRegistry
?
如果两者都有可能,则优缺点是什么?
解决方案
它们是一样的东西。rmiregistry是一个单独的程序,可以从命令行或者脚本运行,而LocateRegistry.createRegistry可以通过编程方式完成同样的事情。
以我的经验,对于"真实"服务器,我们将要使用rmiregistry
,以便我们知道它始终在运行,而不管是否启动了客户端应用程序。 createRegistry
对于测试非常有用,因为我们可以根据需要从测试中启动和停止注册表。
如果要编写一个独立的Java应用程序,则需要启动自己的rmiregistry,但是如果要编写一个显然在J2EE容器中运行的J2EE应用程序,则要" LocateRegistry",因为应用服务器上已经有一个正在运行的应用程序!
如果使用Spring导出RMI服务,则如果尚未运行注册表,它将自动启动注册表。请参阅RmiServiceExporter
如果我们首先启动rmiregistry,则RmiServiceExporter会将自己注册到正在运行的rmiregistry。在这种情况下,我们必须将系统属性" java.rmi.server.codebase"设置为可找到" org.springframework.remoting.rmi.RmiInvocationWrapper_Stub"类的位置。否则,将不会启动RmiServiceExporter并出现异常"
找不到ClassNotFoundException类:org.springframework.remoting.rmi.RmiInvocationWrapper_Stub;嵌套的例外是:..."
如果rmi服务器,rmi客户端和rmiregistry可以访问相同的文件系统,则我们可能希望将系统属性自动配置为在共享文件系统上可以找到spring.jar的位置。以下实用程序类和spring配置显示了如何实现此目的。
abstract public class CodeBaseResolver { static public String resolveCodeBaseForClass(Class<?> clazz) { Assert.notNull(clazz); final CodeSource codeSource = clazz.getProtectionDomain().getCodeSource(); if (codeSource != null) { return codeSource.getLocation().toString(); } else { return ""; } } } public class SystemPropertyConfigurer { private Map<String, String> systemProperties; public void setSystemProperties(Map<String, String> systemProperties) { this.systemProperties = systemProperties; } @PostConstruct void init() throws BeansException { if (systemProperties == null || systemProperties.isEmpty()) { return; } for (Map.Entry<String, String> entry : systemProperties.entrySet()) { final String key = entry.getKey(); final String value = SystemPropertyUtils.resolvePlaceholders(entry.getValue()); System.setProperty(key, value); } } } <bean id="springCodeBase" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="xx.CodeBaseResolver.resolveCodeBaseForClass" /> <property name="arguments"> <list> <value>org.springframework.remoting.rmi.RmiInvocationWrapper_Stub</value> </list> </property> </bean> <bean id="springCodeBaseConfigurer" class="xx.SystemPropertyConfigurer" depends-on="springCodeBase"> <property name="systemProperties"> <map> <entry key="java.rmi.server.codebase" value-ref="springCodeBase" /> </map> </property> </bean> <bean id="rmiServiceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter" depends-on="springCodeBaseConfigurer"> <property name="serviceName" value="XXX" /> <property name="service" ref="XXX" /> <property name="serviceInterface" value="XXX" /> <property name="registryPort" value="${remote.rmi.port}" /> </bean>
上面的示例显示了仅当rmi服务器,rmi客户端和rmi注册表可以访问同一文件系统时,如何自动设置系统属性。如果不正确,或者通过其他方法(例如HTTP)共享spring代码库,则可以修改CodeBaseResolver以适合需要。