Для RMI на стороне сервера нам нужно запустить программу rmiregistry
или просто вызвать LocateRegistry.createRegistry
?
Если оба варианта возможны, каковы преимущества и недостатки?
Сервер RMI: rmiregistry или LocateRegistry.createRegistry
Ответ 1
Это то же самое... rmiregistry
- отдельная программа, которую вы можете запускать из командной строки или script, а LocateRegistry.createRegistry
делает то же самое программно.
По моему опыту, для "реальных" серверов вы захотите использовать rmiregistry
, чтобы вы знали, что он всегда работает независимо от того, запущено или нет клиентское приложение. createRegistry
очень полезен для тестирования, так как вы можете при необходимости запустить и остановить реестр из своего теста.
Ответ 2
Если сначала запустить 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-реестр могут обращаться к одной и той же файловой системе. Если это неверно или spring codebase используется другим способом (например, HTTP), вы можете изменить CodeBaseResolver в соответствии с вашими потребностями.
Ответ 3
Если вы пишете автономное приложение java, вы хотите запустить свою собственную rmiregistry, но если вы пишете приложение J2EE, которое явно работает внутри контейнера J2EE, вы хотите "LocateRegistry", поскольку в приложении уже есть приложение сервер!
Ответ 4
Если вы используете Spring для экспорта своих служб RMI, он автоматически запускает реестр, если он еще не запущен. См. RmiServiceExporter