spring rmi 了解

来源:互联网 发布:绵阳互惠软件 编辑:程序博客网 时间:2024/04/28 03:29

spring rmi 简单使用

相关概述

RMI是Java的一组拥护开发分布式应用程序API。RMI使用Java语言接口定义了远程对象,它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。简单地说,这样使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方法调用,由于J2EE是分布式程序平台,它以RMI机制实现程序组件在不同操作系统之间的通信。比如,一个EJB可以通过RMI调用Web上另一台机器上的EJB远程方法。
RMI(Remote Method Invocation,远程方法调用)是用Java在JDK1.1中实现的,它大大增强了Java开发分布式应用的能力。Java作为一种风靡一时的网络开发语言,其巨大的威力就体现在它强大的开发分布式网络应用的能力上,而RMI就是开发百分之百纯Java的网络分布式应用系统的核心解决方案之一。其实它可以被看作是RPC的Java版本。但是传统RPC并不能很好地应用于分布式对象系统。而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。
RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信。JRMP是专为Java的远程对象制定的协议。因此,Java RMI具有Java的“Write Once,Run Anywhere”的优点,是分布式应用系统的百分之百纯Java解决方案。用Java RMI开发的应用系统可以部署在任何支持JRE(Java Run Environment Java,运行环境)的平台上。但由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信。
Java Remote Method Invocation ( RMI -- Java远程方法调用)允许您使用Java编写分布式对象。本文将介绍RMI的优点以及如何将其连接到现有的和原有的系统中,以及与用Java 编写的组件的连接。
RMI为采用Java对象的分布式计算提供了简单而直接的途径。这些对象可以是新的Java对象,也可以是围绕现有API的简单的Java包装程序。Java体现了“编写一次就能在任何地方运行的模式。而RMI可将Java模式进行扩展,使之可在任何地方运行”。
因为RMI是以Java为核心的,所以,它将Java的安全性和可移植性等强大功能带给了分布式计算。您可将代理和梢?务逻辑等属性移动到网络中最合适的地方。如果您要扩展Java在系统中的使用,RMI将使您充分利用其强大功能。
RMI可利用标准Java本机方法接口JNI[1]与现有的和原有的系统相连接。RMI还可利用标准JDBC包与现有的关系数据库连接。RMI/JNI和RMI/JDBC相结合,可帮助您利用RMI与目前使用非Java语言的现有服务器进行通信,而且在您需要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。

RMI系统运行机理

RMI应用程序通常包括两个独立的程序服务器程序和客户机程序。典型的服务器应用程序将创建多个远程对象,使这些远程对象能够被引用,然后等待客户机调用这些远程对象的方法。而典型的客户机程序则从服务器中得到一个或多个远程对象的引用,然后调用远程对象的方法。RMI为服务器和客户机进行通信和信息传递提供了一种机制。
在与远程对象的通信过程中,RMI使用标准机制:stub和skeleton。远程对象的stub担当远程对象的客户本地代表或代理人角色。调用程序将调用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。在RMI中,远程对象的stub与该远程对象所实现的远程接口集相同。调用stub的方法时将执行下列操作:
(1) 初始化与包含远程对象的远程虚拟机的连接;
(2) 对远程虚拟机的参数进行编组(写入并传输);
(3) 等待方法调用结果;
(4) 解编(读取)返回值或返回的异常;
(5) 将值返回给调用程序。为了向调用程序展示比较简单的调用机制,stub将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以有相应的skeleton(在JDK1.2环境中无需使用skeleton)。Skeleton负责将调用分配给实际的远程对象实现。它在接收方法调用时执行下列操作:(1) 解编(读取)远程方法的参数;(2) 调用实际远程对象实现上的方法;(3) 将结果(返回值或异常)编组(写入并传输)给调用程序。stub和skeleton由rmic编译器生成。
利用RMI编写分布式对象应用程序需要完成以下工作:(1) 定位远程对象。应用程序可使用两种机制中的一种得到对远程对象的引用。它既可用RMI的简单命名工具rmiregistry来注册它的远程对象,也可以将远程对象引用作为常规操作的一部分来进行传递和返回。(2)与远程对象通信。远程对象间通信的细节由RMI处理,对于程序员来说,远程通信看起来就像标准的Java方法调用。(3)给作为参数或返回值传递的对象加载类字节码。因为RMI允许调用程序将纯Java对象传给远程对象,所以,RMI将提供必要的机制,既可以加载对象的代码又可以传输对象的数据。在RMI分布式应用程序运行时,服务器调用注册服务程序以使名字与远程对象相关联。客户机在服务器上的注册服务程序中用远程对象的名字查找该远程对象,然后调用它的方法。
 
用Spring的RMI支持,你可以通过RMI基础设施透明的暴露你的服务。设置好Spring的RMI支持后,你会看到一个和远程EJB接口类似的配置,只是没有对安全上下文传递和远程事务传递的标准支持。这可大大简化rmi的使用

1.使用RmiServiceExporter暴露服务

 

   使用RmiServiceExporter,我们可以把对象的接口暴露成RMI对象。可以使用 RmiProxyFactoryBean 或者在传统RMI服务中使用普通RMI来访问该接口。RmiServiceExporter 显式地支持使用RMI调用器暴露任何非RMI的服务。当然,我们首先需要在Spring容器中设置我们的服务:

新建一个userDao的接口 及userDaoImpl实现类

userDao

package com.soyen.dao;

public interface UserDao {
 public String login(String username,String password);
}

 

 

UserDaoImpl

package com.soyen.dao.impl;

import com.soyen.dao.UserDao;

public class UserDaoImpl implements UserDao {

 public String login(String username, String password) {
  return "你好" + username + "你的密码为:" + password;
 }

}

 

 现将服务端配置代码贴上

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 <bean id="userDao" class="com.soyen.dao.impl.UserDaoImpl"></bean>
  <!-- 将类暴露成为一个RMI服务 -->
 <bean id="rmiService" class="org.springframework.remoting.rmi.RmiServiceExporter">  
    <!-- 服务类 -->
  <property name="service" ref="userDao" />  
    <!-- 服务名 -->
  <property name="serviceName" value="UserService" />  
        <!-- 服务接口 -->
  <property name="serviceInterface" value="com.soyen.dao.UserDao" />
         <!-- 服务端口默认为1199-->
  <property name="registryPort" value="1091" />
 </bean>
</beans>

 
客户端采用java控制台程序测试,须将服务端接口提供给客户端
 

import org.springframework.remoting.rmi.RmiProxyFactoryBean;

import com.soyen.dao.UserDao;


public class TestRmi {
public static void main(String[] args) {
  
  
  RmiProxyFactoryBean factory = new RmiProxyFactoryBean();  
        factory.setServiceInterface(UserDao.class);  
        factory.setServiceUrl("rmi://localhost:1091/UserService");  
        factory.afterPropertiesSet();  
          
        UserDao userService = (UserDao)factory.getObject();  
        String msg=userService.login("zhangsan", "12344"); 
        System.out.println(msg);      
      

  
 }

}

执行完毕则可调用
 
若客户端采用配置方式则可使用一下配置
 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 <bean id="rmiService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">  
    <property name="serviceInterface" value="com.soyen.dao.UserDao" ></property>
    <property name="serviceUrl" value="rmi://localhost:1091/UserService"></property>
 </bean>
</beans>
 

 

Spring Rmi远程方法调用

一: 服务端  暴露服务

package com.xx.service;

/**
 * 定义远程服务接口
 * 1.可以不继承java.rmi.Remote接口
 * 2.方法可以不抛出java.rmi.RemoteException异常
 *
 */
public interface ISayHelloService {
 
 public String doSayHello(String name);
}


package com.xx.service.impl;

import com.xx.service.ISayHelloService;

/**
 * 远程接口实现
 */
public class ChinaSayHelloServiceImpl implements ISayHelloService {

 public String doSayHello(String name) {
  return "hello " + name;
 }
}
 

package com.xx.service;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 服务端 
 * 暴露远程服务
 */
public class ServerMain {
 
 public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-server.xml"}, true);
  System.out.println("==============服务启动成功 ==============");
 }
}

spring配置文件 applicationContext-server.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd">
  
  <bean id="chinaSayHelloService" class="com.xx.service.impl.ChinaSayHelloServiceImpl" />
  
  <bean id="chinaSayHelloServiceRmi" class="org.springframework.remoting.rmi.RmiServiceExporter" >
   <property name="serviceName"   value="chinaSayHelloService" />
   <property name="service"    ref="chinaSayHelloService"/>
   <property name="serviceInterface"  value="com.xx.service.ISayHelloService"/>
   <property name="registryPort"   value="9999"/>
  </bean>
</beans>

二:客户端  远程方法调用

package com.xx.service;

import java.net.UnknownHostException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 *  客户端
 */
public class ClientMain {
 
 public static void main(String[] args) throws UnknownHostException {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext-client.xml");
  ISayHelloService object = applicationContext.getBean("chinaSayHelloServiceRmi", ISayHelloService.class);
  System.out.println(object.doSayHello("张三"));
 }
}

spring配置文件 applicationContext-client.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd">  
         
        <bean id="chinaSayHelloServiceRmi" class="org.springframework.remoting.rmi.RmiProxyFactoryBean" > 
            <property name="serviceUrl" value="rmi://192.168.3.104:9999/chinaSayHelloService" /> 
            <property name="serviceInterface"    value="com.xx.service.ISayHelloService"/> 
        </bean> 
</beans> 
 

看了《J2EE without EJB》的remote章节,忍不住写点代码试试,看看Spring的实现到底多巧妙。

1.先测试RMI服务的发布,测试代码如下:

java 代码
//MyService.java: remote interface for RMI   
package test.spring.remote.rmi;   
  
public interface MyService extends java.rmi.Remot {   
    public void doSomething() throws java.rmi.RemoteException;   
}   
  
  
//MyBusinessInterface.java: My own business interface, must has the same methods as MyService   
package test.spring.remote.rmi;   
  
public interface MyBusinessInterface {   
    public void doSomething();   
}   
  
  
//MyServiceImpl.java: the service implement class   
package test.spring.remote.rmi;   
  
public class MyServiceImpl implements MyService, MyBusinessInterface {   
    public void doSomething() {   
        System.out.println("MyServiceImpl.doSomething()");   
    }   
}  

 

Spring的context配置文件如下:


xml 代码
//spring-remote.xml   
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"   
    "http://www.springframework.org/dtd/spring-beans.dtd">  
  
<beans>  
    <bean id="myService" class="test.spring.remote.rmi.MyServiceImpl" />  
    <bean id="serviceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">  
        <property name="serviceName">  
            <value>myService</value>  
        </property>  
        <property name="service">  
            <ref bean="myService" />  
        </property>  
        <property name="serviceInterface">  
            <value>test.spring.remote.rmi.MyService</value>  
        </property>  
        <property name="registryPort">  
            <value>1199</value>  
        </property>  
    </bean>  
</beans>  

再写一个测试程序,如下:

java 代码
//TestSpringRmi.java   
package test.spring.remote.rmi;   
  
import org.springframework.context.ApplicationContext;   
import org.springframework.context.support.ClassPathXmlApplicationContext;   
  
public class TestSpringRmi {   
    public static void main(String[] args) {   
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-remote.xml");   
    }   
}  

运行TestSpringRmi,报错如下:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serviceExporter' defined in class path resource [spring-remote.xml]: Initialization of bean failed; nested exception is java.rmi.StubNotFoundException: Stub class not found: test.spring.remote.rmi.MyServiceImpl_Stub; nested exception is:
 java.lang.ClassNotFoundException: test.spring.remote.rmi.MyServiceImpl_Stub
java.rmi.StubNotFoundException: Stub class not found: test.spring.remote.rmi.MyServiceImpl_Stub; nested exception is:
 java.lang.ClassNotFoundException: test.spring.remote.rmi.MyServiceImpl_Stub

咦?Spring不是号称不需要自己生成stub么?怎么会出现“Stub class not found”呢?
祭出google,从spring官方论坛搜到一个帖子:http://forum.springframework.org/showthread.php?t=19185,里面有条回复是:

I found the answer:
The class org.springframework.remoting.rmi.RmiInvocationWrap per_Stub is present in spring.jar, but not in the source tree as a Java file. Since I was running against the compiled Spring Java files, rather than the jar, it did not find it.

晕倒,Spring不会这么弱智吧,难道我以后使用的时候还得把jar包解压到class目录下?
不甘心,再搜,找到这个帖子:http://forum.springframework.org/showthread.php?t=12685

在Juergen Hoeller的回复提示下,我再去看了jpetstore的配置文件,原来用以发布rmi的接口应该是pojo形式的MyBusinessInterface,而不是那个继承自Remote的MyService,修改自己的context配置文件:


xml 代码
//spring-remote.xml   
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"   
    "http://www.springframework.org/dtd/spring-beans.dtd">  
  
<beans>  
    <bean id="myService" class="test.spring.remote.rmi.MyServiceImpl" />  
    <bean id="serviceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">  
        <property name="serviceName">  
            <value>myService</value>  
        </property>  
        <property name="service">  
            <ref bean="myService" />  
        </property>  
        <property name="serviceInterface">  
            <!-- <value>test.spring.remote.rmi.MyService</value> -->  
            <value>test.spring.remote.rmi.MyBusinessInterface</value>  
        </property>  
        <property name="registryPort">  
            <value>1199</value>  
        </property>  
    </bean>  
</beans>  
再运行TestSpringRmi,成功了。console打印:
03-02 14:51:56 INFO  [RmiServiceExporter.java:236] Binding RMI service 'myService' to registry at port '1199'


2.再继续测试客户端调用,先修改context配置如下:


xml 代码
//spring-remote.xml   
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"   
    "http://www.springframework.org/dtd/spring-beans.dtd">  
  
<beans>  
    <bean id="rmiService"  
        class="org.springframework.remoting.rmi.RmiProxyFactoryBean">  
        <property name="serviceInterface">  
            <value>test.spring.remote.rmi.MyBusinessInterface</value>  
        </property>  
        <property name="serviceUrl">  
            <value>rmi://localhost:1199/myService</value>  
        </property>  
    </bean>  
    <bean id="myService" class="test.spring.remote.rmi.MyServiceImpl" />  
    <bean id="serviceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">  
        <property name="serviceName">  
            <value>myService</value>  
        </property>  
        <property name="service">  
            <ref bean="myService" />  
        </property>  
        <property name="serviceInterface">  
            <!-- <value>test.spring.remote.rmi.MyService</value> -->  
            <value>test.spring.remote.rmi.MyBusinessInterface</value>  
        </property>  
        <property name="registryPort">  
            <value>1199</value>  
        </property>  
    </bean>  
</beans>  
再修改测试代码,添加客户端调用:


java 代码
//TestSpringRmi.java   
package test.spring.remote.rmi;   
  
import org.springframework.context.ApplicationContext;   
import org.springframework.context.support.ClassPathXmlApplicationContext;   
  
public class TestSpringRmi {   
    public static void main(String[] args) {   
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-remote.xml");   
        MyBusinessInterface service = (MyBusinessInterface) context.getBean("rmiService");   
        service.doSomething();   
    }   
}  
运行TestSpringRmi,报错如下:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'rmiService' defined in class path resource [spring-remote.xml]: Initialization of bean failed; nested exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
 java.net.ConnectException: Connection refused: connect
java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
 java.net.ConnectException: Connection refused: connect

仔细检查,原来自己把生成rmi客户端的bean映射放到了发布rmi服务的serviceExporter之前了,调换一下顺序:

    xml 代码

//spring-remote.xml   
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"   
    "http://www.springframework.org/dtd/spring-beans.dtd">  
  
<beans>  
    <bean id="myService" class="test.spring.remote.rmi.MyServiceImpl" />  
    <bean id="serviceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">  
        <property name="serviceName">  
            <value>myService</value>  
        </property>  
        <property name="service">  
            <ref bean="myService" />  
        </property>  
        <property name="serviceInterface">  
            <!-- <value>test.spring.remote.rmi.MyService</value> -->  
            <value>test.spring.remote.rmi.MyBusinessInterface</value>  
        </property>  
        <property name="registryPort">  
            <value>1199</value>  
        </property>  
    </bean>  
    <bean id="rmiService"  
        class="org.springframework.remoting.rmi.RmiProxyFactoryBean">  
        <property name="serviceInterface">  
            <value>test.spring.remote.rmi.MyBusinessInterface</value>  
        </property>  
        <property name="serviceUrl">  
            <value>rmi://localhost:1199/myService</value>  
        </property>  
    </bean>  
</beans>  
运行TestSpringRmi,结果如下:
03-02 15:01:24 INFO  [RmiServiceExporter.java:236] Binding RMI service 'myService' to registry at port '1199'
03-02 15:01:24 INFO  [RmiClientInterceptor.java:128] RMI stub [rmi://localhost:1199/myService] is an RMI invoker
MyServiceImpl.doSomething()


经过一番浅尝辄止,初步得出几个结论:
1.Spring对RMI的支持果然很不错,在Cglib等工具的支持下,使用RMI终于可以同Naming、rmic和stub告别了。
2.用以发布RMI的接口不能从java.rmi.Remote继承而来,否则就会出现“Stub class not found”的错误,原因有待深究。
3.Spring的BeanFactory创建bean实例是有序的,向RMI、JNDI、WebService等注册服务性质的应用,同一应用中的客户端要根据其依赖性调整配置顺序。


 

JNDI的使用方式

服务端注册

[java] view plaincopyprint?    <bean class="org.springframework.remoting.rmi.JndiRmiServiceExporter"> 
    <property name="service" ref="creditService" /> 
    <property name="serviceInterface" value="com.common.CreditRemoteService" /> 
    <property name="jndiName" value="CreditService" /> 
</bean> 

  <bean class="org.springframework.remoting.rmi.JndiRmiServiceExporter">
  <property name="service" ref="creditService" />
  <property name="serviceInterface" value="com.common.CreditRemoteService" />
  <property name="jndiName" value="CreditService" />
 </bean>
客户端调用xml配置

[java] view plaincopyprint?<bean id="creditService" class="org.springframework.remoting.rmi.JndiRmiProxyFactoryBean" 
        scope="prototype" lazy-init="true"> 
    <property name="serviceInterface" value="com.common.CreditRemoteService" /> 
    <property name="lookupStubOnStartup" value="false"/> 
    <property name="refreshStubOnConnectFailure" value="true"/> 
    <property name="jndiName" value="CreditService" />  
    <property name="jndiEnvironment"> 
        <props> 
            <prop key="java.naming.provider.url">${com.jndi.creditServiceUrl}</prop> 
            <prop key ="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop> 
        </props> 
    </property> 
</bean> 

 <bean id="creditService" class="org.springframework.remoting.rmi.JndiRmiProxyFactoryBean"
   scope="prototype" lazy-init="true">
  <property name="serviceInterface" value="com.common.CreditRemoteService" />
     <property name="lookupStubOnStartup" value="false"/>
     <property name="refreshStubOnConnectFailure" value="true"/>
  <property name="jndiName" value="CreditService" />
  <property name="jndiEnvironment">
   <props>
    <prop key="java.naming.provider.url">${com.jndi.creditServiceUrl}</prop>
    <prop key ="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
   </props>
  </property>
 </bean>
 

 

Spring RMI 源码浅析-RmiServiceExporter 导出服务
分类: 源码学习 2012-03-29 17:25 317人阅读 评论(0) 收藏 举报
Java Rmi

1.接口必须继承java.rmi.Remote接口

2.方法必须抛出java.rmi.RemoteException异常

Spring Rmi

1.可以不实现java.rmi.Remote接口 

2.方法可以不抛出异常  

问题:在Spring 内部是怎么实现的?

在Spring中 是通过org.springframework.remoting.rmi.RmiServiceExporte 在服务端导出一个服务

RmiServiceExporter定义

[java] view plaincopyprint?public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean { 

[java] view plaincopyprint?public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean { 

public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean {
}实现了 InitializingBean接口  Spring会在bean的实例化阶段 调用 InitializingBean 的afterPropertiesSet 方法

bean的实例化 会在什么时候触发 取决于配置 例如lazy-init 

RmiServiceExporter afterPropertiesSet 方法实现

[java] view plaincopyprint?public void afterPropertiesSet() throws RemoteException { 
    prepare(); 

[java] view plaincopyprint?public void afterPropertiesSet() throws RemoteException { 
    prepare(); 

 public void afterPropertiesSet() throws RemoteException {
  prepare();
 }prepare方法

[java] view plaincopyprint?public void prepare() throws RemoteException { 
    //检查配置中的 service对象   如果为null 抛出异常   
    checkService(); 
    //检查服务名称  
    if (this.serviceName == null) { 
        throw new IllegalArgumentException("Property 'serviceName' is required"); 
    } 
    // Check socket factories for exported object.  
    // 略....  
 
    // Determine RMI registry to use.  
    if (this.registry == null) { 
        //获得注册器  
        this.registry = getRegistry(this.registryHost, this.registryPort, 
            this.registryClientSocketFactory, this.registryServerSocketFactory); 
    } 
    // 获得要导出的服务对象  
    // getObjectToExport方法  在父类RmiBasedExporter中定义   
    // 1.如果实现了jdk Remote接口 那就是一个标准的RMI 类型转换后 直接返回  
    // 2.没有实现jdk Remote接口  返回spring包装对象RmiInvocationWrapper调用器  RmiInvocationWrapper实现了jdk Remote接口  
    // RmiInvocationWrapper 中有两个属性  1.wrappedObject 自己定义的远程对象[service属性]    
    // 2.RmiBasedExporter 也就是当前导出对象 this 在客户端调用的时候  会触发invoke方法  
    this.exportedObject = getObjectToExport(); 
 
    // 导出服务对象   jdk UnicastRemoteObject实现  
    if (this.clientSocketFactory != null) { 
        UnicastRemoteObject.exportObject( 
                this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory); 
    } 
    else { 
        UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort); 
    } 
 
    // Bind RMI object to registry.  
    // 把RMI远程服务对象和注册器绑定 jdk实现  
    try { 
        if (this.replaceExistingBinding) { 
            //替换指定serviceName的远程对象  
            this.registry.rebind(this.serviceName, this.exportedObject); 
        } 
        else { 
            //绑定对象  
            this.registry.bind(this.serviceName, this.exportedObject); 
        } 
    } 
    catch (AlreadyBoundException ex) { 
        // Already an RMI object bound for the specified service name...  
        unexportObjectSilently(); 
        throw new IllegalStateException( 
                "Already an RMI object bound for name '"  + this.serviceName + "': " + ex.toString()); 
    } 
    catch (RemoteException ex) { 
        // Registry binding failed: let's unexport the RMI object as well.  
        unexportObjectSilently(); 
        throw ex; 
    } 

[java] view plaincopyprint?public void prepare() throws RemoteException { 
    //检查配置中的 service对象   如果为null 抛出异常   
    checkService(); 
    //检查服务名称  
    if (this.serviceName == null) { 
        throw new IllegalArgumentException("Property 'serviceName' is required"); 
    } 
    // Check socket factories for exported object.  
    // 略....  
 
    // Determine RMI registry to use.  
    if (this.registry == null) { 
        //获得注册器  
        this.registry = getRegistry(this.registryHost, this.registryPort, 
            this.registryClientSocketFactory, this.registryServerSocketFactory); 
    } 
    // 获得要导出的服务对象  
    // getObjectToExport方法  在父类RmiBasedExporter中定义   
    // 1.如果实现了jdk Remote接口 那就是一个标准的RMI 类型转换后 直接返回  
    // 2.没有实现jdk Remote接口  返回spring包装对象RmiInvocationWrapper调用器  RmiInvocationWrapper实现了jdk Remote接口  
    // RmiInvocationWrapper 中有两个属性  1.wrappedObject 自己定义的远程对象[service属性]    
    // 2.RmiBasedExporter 也就是当前导出对象 this 在客户端调用的时候  会触发invoke方法  
    this.exportedObject = getObjectToExport(); 
 
    // 导出服务对象   jdk UnicastRemoteObject实现  
    if (this.clientSocketFactory != null) { 
        UnicastRemoteObject.exportObject( 
                this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory); 
    } 
    else { 
        UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort); 
    } 
 
    // Bind RMI object to registry.  
    // 把RMI远程服务对象和注册器绑定 jdk实现  
    try { 
        if (this.replaceExistingBinding) { 
            //替换指定serviceName的远程对象  
            this.registry.rebind(this.serviceName, this.exportedObject); 
        } 
        else { 
            //绑定对象  
            this.registry.bind(this.serviceName, this.exportedObject); 
        } 
    } 
    catch (AlreadyBoundException ex) { 
        // Already an RMI object bound for the specified service name...  
        unexportObjectSilently(); 
        throw new IllegalStateException( 
                "Already an RMI object bound for name '"  + this.serviceName + "': " + ex.toString()); 
    } 
    catch (RemoteException ex) { 
        // Registry binding failed: let's unexport the RMI object as well.  
        unexportObjectSilently(); 
        throw ex; 
    } 

 public void prepare() throws RemoteException {
  //检查配置中的 service对象   如果为null 抛出异常
  checkService();
  //检查服务名称
  if (this.serviceName == null) {
   throw new IllegalArgumentException("Property 'serviceName' is required");
  }
  // Check socket factories for exported object.
  // 略....

  // Determine RMI registry to use.
  if (this.registry == null) {
   //获得注册器
   this.registry = getRegistry(this.registryHost, this.registryPort,
    this.registryClientSocketFactory, this.registryServerSocketFactory);
  }
  // 获得要导出的服务对象
  // getObjectToExport方法  在父类RmiBasedExporter中定义
  // 1.如果实现了jdk Remote接口 那就是一个标准的RMI 类型转换后 直接返回
  // 2.没有实现jdk Remote接口  返回spring包装对象RmiInvocationWrapper调用器  RmiInvocationWrapper实现了jdk Remote接口
  // RmiInvocationWrapper 中有两个属性  1.wrappedObject 自己定义的远程对象[service属性] 
  // 2.RmiBasedExporter 也就是当前导出对象 this 在客户端调用的时候  会触发invoke方法
  this.exportedObject = getObjectToExport();

  // 导出服务对象   jdk UnicastRemoteObject实现
  if (this.clientSocketFactory != null) {
   UnicastRemoteObject.exportObject(
     this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory);
  }
  else {
   UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort);
  }

  // Bind RMI object to registry.
  // 把RMI远程服务对象和注册器绑定 jdk实现
  try {
   if (this.replaceExistingBinding) {
    //替换指定serviceName的远程对象
    this.registry.rebind(this.serviceName, this.exportedObject);
   }
   else {
    //绑定对象
    this.registry.bind(this.serviceName, this.exportedObject);
   }
  }
  catch (AlreadyBoundException ex) {
   // Already an RMI object bound for the specified service name...
   unexportObjectSilently();
   throw new IllegalStateException(
     "Already an RMI object bound for name '"  + this.serviceName + "': " + ex.toString());
  }
  catch (RemoteException ex) {
   // Registry binding failed: let's unexport the RMI object as well.
   unexportObjectSilently();
   throw ex;
  }
 }checkService方法

[java] view plaincopyprint?protected void checkService() throws IllegalArgumentException { 
    if (getService() == null) { 
        throw new IllegalArgumentException("Property 'service' is required"); 
    } 

[java] view plaincopyprint?protected void checkService() throws IllegalArgumentException { 
    if (getService() == null) { 
        throw new IllegalArgumentException("Property 'service' is required"); 
    } 

 protected void checkService() throws IllegalArgumentException {
  if (getService() == null) {
   throw new IllegalArgumentException("Property 'service' is required");
  }
 }[java] view plaincopyprint?protected Remote getObjectToExport() { 
    //自定义的远程对象 实现了 jdk Remote  
    if (getService() instanceof Remote && 
            (getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface()))) {              return (Remote) getService(); 
    } 
    else { 
        //  没有实现  Remote接口  spring在此处包装了我们自定义的远程服务对象  
        // getProxyForService方法 返回一个代理对象  
        return new RmiInvocationWrapper(getProxyForService(), this); 
    } 

[java] view plaincopyprint?protected Remote getObjectToExport() { 
    //自定义的远程对象 实现了 jdk Remote  
    if (getService() instanceof Remote && 
            (getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface()))) {              return (Remote) getService(); 
    } 
    else { 
        //  没有实现  Remote接口  spring在此处包装了我们自定义的远程服务对象  
        // getProxyForService方法 返回一个代理对象  
        return new RmiInvocationWrapper(getProxyForService(), this); 
    } 

 protected Remote getObjectToExport() {
  //自定义的远程对象 实现了 jdk Remote
  if (getService() instanceof Remote &&
    (getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface()))) {    return (Remote) getService();
  }
  else {
   //  没有实现  Remote接口  spring在此处包装了我们自定义的远程服务对象
   // getProxyForService方法 返回一个代理对象
   return new RmiInvocationWrapper(getProxyForService(), this);
  }
 }RmiInvocationWrapper定义  实现了RmiInvocationHandler接口  而RmiInvocationHandler接口继承了Remote 接口

[java] view plaincopyprint?class RmiInvocationWrapper implements RmiInvocationHandler { 
 
    private final Object wrappedObject; 
 
    private final RmiBasedExporter rmiExporter; 
 
    public RmiInvocationWrapper(Object wrappedObject, RmiBasedExporter rmiExporter) { 
        Assert.notNull(wrappedObject, "Object to wrap is required"); 
        Assert.notNull(rmiExporter, "RMI exporter is required"); 
        this.wrappedObject = wrappedObject; 
        this.rmiExporter = rmiExporter; 
    } 
 
 
    public String getTargetInterfaceName() { 
        Class ifc = this.rmiExporter.getServiceInterface(); 
        return (ifc != null ? ifc.getName() : null); 
    } 
 
    /***
     * 非标准的RMI调用远程方法的中转站
     * invocation封装了方法名 参数名
     */ 
    public Object invoke(RemoteInvocation invocation) 
        throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { 
        //会在客户端调用远程方法时触发,chuwrappedObject是 我们定义的远程对象   
        return this.rmiExporter.invoke(invocation, this.wrappedObject); 
    } 

[java] view plaincopyprint?class RmiInvocationWrapper implements RmiInvocationHandler { 
 
    private final Object wrappedObject; 
 
    private final RmiBasedExporter rmiExporter; 
 
    public RmiInvocationWrapper(Object wrappedObject, RmiBasedExporter rmiExporter) { 
        Assert.notNull(wrappedObject, "Object to wrap is required"); 
        Assert.notNull(rmiExporter, "RMI exporter is required"); 
        this.wrappedObject = wrappedObject; 
        this.rmiExporter = rmiExporter; 
    } 
 
 
    public String getTargetInterfaceName() { 
        Class ifc = this.rmiExporter.getServiceInterface(); 
        return (ifc != null ? ifc.getName() : null); 
    } 
 
    /***
     * 非标准的RMI调用远程方法的中转站
     * invocation封装了方法名 参数名
     */ 
    public Object invoke(RemoteInvocation invocation) 
        throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { 
        //会在客户端调用远程方法时触发,chuwrappedObject是 我们定义的远程对象   
        return this.rmiExporter.invoke(invocation, this.wrappedObject); 
    } 

class RmiInvocationWrapper implements RmiInvocationHandler {

 private final Object wrappedObject;

 private final RmiBasedExporter rmiExporter;

 public RmiInvocationWrapper(Object wrappedObject, RmiBasedExporter rmiExporter) {
  Assert.notNull(wrappedObject, "Object to wrap is required");
  Assert.notNull(rmiExporter, "RMI exporter is required");
  this.wrappedObject = wrappedObject;
  this.rmiExporter = rmiExporter;
 }


 public String getTargetInterfaceName() {
  Class ifc = this.rmiExporter.getServiceInterface();
  return (ifc != null ? ifc.getName() : null);
 }

 /***
  * 非标准的RMI调用远程方法的中转站
  * invocation封装了方法名 参数名
  */
 public Object invoke(RemoteInvocation invocation)
     throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  //会在客户端调用远程方法时触发,chuwrappedObject是 我们定义的远程对象
  return this.rmiExporter.invoke(invocation, this.wrappedObject);
 }
}RmiInvocationHandler接口继承了 jdk Remote

[java] view plaincopyprint?public interface RmiInvocationHandler extends Remote { 

[java] view plaincopyprint?public interface RmiInvocationHandler extends Remote { 

public interface RmiInvocationHandler extends Remote {
}[java] view plaincopyprint?protected Object getProxyForService() { 
        //检查配置中的 service对象   如果为null 抛出异常  
        checkService(); 
        //检查serviceInterface属性    
        checkServiceInterface(); 
        ProxyFactory proxyFactory = new ProxyFactory(); 
        proxyFactory.addInterface(getServiceInterface()); 
        if (this.registerTraceInterceptor != null ? 
                this.registerTraceInterceptor.booleanValue() : this.interceptors == null) { 
            proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName())); 
        } 
        if (this.interceptors != null) { 
            AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); 
            for (int i = 0; i < this.interceptors.length; i++) { 
                proxyFactory.addAdvisor(adapterRegistry.wrap(this.interceptors[i])); 
            } 
        } 
        proxyFactory.setTarget(getService()); 
        // 生成代理对象  到底是jdk实现 还是cglib实现  取决于 到底有没有实现接口  
        return proxyFactory.getProxy(getBeanClassLoader()); 
    } 
[java] view plaincopyprint?protected Object getProxyForService() { 
        //检查配置中的 service对象   如果为null 抛出异常  
        checkService(); 
        //检查serviceInterface属性    
        checkServiceInterface(); 
        ProxyFactory proxyFactory = new ProxyFactory(); 
        proxyFactory.addInterface(getServiceInterface()); 
        if (this.registerTraceInterceptor != null ? 
                this.registerTraceInterceptor.booleanValue() : this.interceptors == null) { 
            proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName())); 
        } 
        if (this.interceptors != null) { 
            AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); 
            for (int i = 0; i < this.interceptors.length; i++) { 
                proxyFactory.addAdvisor(adapterRegistry.wrap(this.interceptors[i])); 
            } 
        } 
        proxyFactory.setTarget(getService()); 
        // 生成代理对象  到底是jdk实现 还是cglib实现  取决于 到底有没有实现接口  
        return proxyFactory.getProxy(getBeanClassLoader()); 
    } 

protected Object getProxyForService() {
  //检查配置中的 service对象   如果为null 抛出异常
  checkService();
  //检查serviceInterface属性 
  checkServiceInterface();
  ProxyFactory proxyFactory = new ProxyFactory();
  proxyFactory.addInterface(getServiceInterface());
  if (this.registerTraceInterceptor != null ?
    this.registerTraceInterceptor.booleanValue() : this.interceptors == null) {
   proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));
  }
  if (this.interceptors != null) {
   AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
   for (int i = 0; i < this.interceptors.length; i++) {
    proxyFactory.addAdvisor(adapterRegistry.wrap(this.interceptors[i]));
   }
  }
  proxyFactory.setTarget(getService());
  // 生成代理对象  到底是jdk实现 还是cglib实现  取决于 到底有没有实现接口
  return proxyFactory.getProxy(getBeanClassLoader());
 }总结:1.spring 容器发布一个远程服务 是通过InitializingBean接口驱动起来的

            2.spring 包装了JDK Rmi  也就是说 服务端是spring暴露   客户端也可以用Jdk rmi调用 没有任何问题

            3,spring对没有实现Remote接口的远程服务 用RmiInvocationWrapper做了包装  RmiInvocationWrapper实现了Remote接口

 

RMI@SPRING的常见问题解决

RMI 问题 1 背景:

用 ./shutdown.sh 关闭 rmi 服务器的 tomcat ,然后 ./startup.sh 启动,客户端连接总是会导致如下错误:

org.springframework.remoting.RemoteLookupFailureException: Lookup of RMI stub failed; nested exception is java.rmi.UnmarshalException: error unmarshalling return; nested exception is:

        java.io.EOFException

com.ffcs.ieie.communicate.ieiemp.IeiempException: org.springframework.remoting.RemoteLookupFailureException: Lookup of RMI st

ub failed; nested exception is java.rmi.UnmarshalException: error unmarshalling return; nested exception is:

        java.io.EOFException

RMI 问题 1 分析:

The cause of the problem is the fact that Spring creates an RMIRegistry with the classloader of the server webapp. Then, when restarting the server, the RMIRegistry is not shut down. After the restart the Registry keeps its references to the old Stubs which do not exist anymore.
There are two solutions:
1) Start the rmiregistry in a seperate process without the classpath of the server app.
2) (better approach) Let spring start the RMIRegistry throu RmiRegistryFactoryBean, which shuts it down correctly.

参考: http://forum.springframework.org/showthread.php?t=33073

RMI 问题 1 解决:

将服务器中的 spring 配置代码,如下:

    < bean id = "incomingService" class = "com.ffcs.ieiemp.ieie.rmi.IncomingService" />

    < bean id = "rmiService" class = "org.springframework.remoting.rmi.RmiServiceExporter" >

        < property name = "serviceName" value = "IncomingService" />

        < property name = "service" ref = "incomingService" />

        < property name = "serviceInterface" value = "com.ffcs.ieiemp.ieie.rmi.Incoming" />

        < property name = "registryPort" value = "1099" />

</ bean >

替换为:

    <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">

       <property name="port" value="1099"/>

    </bean>

    < bean id = "incomingService" class = "com.ffcs.ieiemp.ieie.rmi.IncomingService" />

    < bean id = "rmiService" class = "org.springframework.remoting.rmi.RmiServiceExporter" >

        < property name = "serviceName" value = "IncomingService" />

        < property name = "service" ref = "incomingService" />

        < property name = "serviceInterface" value = "com.ffcs.ieiemp.ieie.rmi.Incoming" />

        <!-- <property name="registryPort" value="1099"/> -->

        <property name="registry" ref="registry"/>

</ bean >

 

RMI 问题 2 背景:

          RMI服务器重启,总是会出现客户端连接拒绝的问题。

RMI 问题2 分析:

          服务器重启会影响到客户端?说明客户端有保存着重启之前的服务器连接相关记录。经研究发现,客户端Stub 有缓存,所以只要刷新缓存即可解决问题。参考: http://forum.springsource.org/showthread.php?t=61575

RMI 问题2 解决:

          在客户端连接代码中增加红色标识的代码:

       RmiProxyFactoryBean factory=newRmiProxyFactoryBean();

       factory.setServiceInterface(Incoming.class);

       factory.setServiceUrl(url);

       //XXXvincan: 解决重启 rmi 的服务器后会出现拒绝连接或找不到服务对象的错误

       factory.setLookupStubOnStartup(false );

       factory.setRefreshStubOnConnectFailure(true );

       factory.afterPropertiesSet();

    Incoming service=(Incoming)factory.getObject();

 

因为RMI stub被连接到特定的端点,不仅仅是为每个调用打开一个给定的目标地址的连接,所以如果重新启动RMI端点主机的服务器,那么就需要重新注册这些stub,并且客户端需要再次查询它们。
     虽然目标服务的重新注册在重新启动时通常会自动发生,不过此时客户端保持的stub将会变的陈旧,且客户端不会注意这些,除非他们再次尝试调用stub上的方法,而这也将throw一个连接失败的异常。
      为了避免这种情形,Spring的RmiProxyFactoryBean提供了一个refreshStubOnConnectFailure的bean属性,如果调用失败,并且连接异常的话,将它设定为true来强制重新自动查询stub。
<bean id="reportService"
  class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
  <property name="serviceUrl">
   <value>serviceurl</value>
  </property>
  <property name="serviceInterface">
   <value>ServiceIntfc</value>
  </property>
  <property name="refreshStubOnConnectFailure">
    <value>true</value>
  </property>

</bean>
      stub查询的另一个问题是,目标RMI服务器和RMI注册项在查询时要为可用的。如果客户端在服务器启动之前,尝试查询和缓存该服务stub,那么客户端的启动将会失败(即使还不需要该服务)。
     为了能够惰性查询服务stub,设定RmiProxyFactoryBean的lookupStubOnStarup标志为false。然后在第一次访问时查询该stub,也就是说,当代理上的第一个方法被调用的时候去主动查询stub,同时被缓存。这也有一个缺点,就是直到第一次调用,否则无法确认目标服务是否实际存在。
<bean id="reportService"
  class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
  <property name="serviceUrl">
   <value>serviceurl</value>
  </property>
  <property name="serviceInterface">
   <value>Service</value>
  </property>
  <property name="lookupStubOnStartup">
     <value>false</value>
  </property>
  <property name="refreshStubOnConnectFailure">
    <value>true</value>
  </property>

</bean>

还有一个属性就是cacheStub,当它设置为false的时候,就完全避免了stub的缓存,但影响了性能。需要的时候还是可以试试

 

RMI 问题3 背景:

         两台服务器, JDK 都是 1.6 ,在一个局域网内,内网 IP 分别为 192.168.39.11 , 192.168.39.164 ,对应的的还有外网 IP 。写了一个 RMI 服务器和客户端,在本地调试没有问题。把服务器端布署到 11 这台服务器上后,在 164 客户端连接却总是抛错:
java.rmi.ConnectException: Connection refused to host:127.0.0.1; nested exception is:
java.net.ConnectException: Connection refused: connect
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod
(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at $Proxy0.call(Unknown Source)
at RMI.Client.callRMI(Client.java:39)
at RMI.Client.main(Client.java:53)
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(Unknown Source)
at java.net.PlainSocketImpl.connectToAddress(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket
(Unknown Source)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket
(Unknown Source)
... 9 more
也就是 IP 被指向 127.0.0.1 了,而客户端发起连接的时候 IP 绝对是写的 IP 是 192.168.39.11 。

RMI 问题3 分析:

这就是典型的服务器有多个ip 引起的 rmi 连接问题。

RMI 问题3 解决:

        解决方法有三:

1.      服务器端添加代码:System.setProperty("java.rmi.server.hostname" , "192.168.39.11" );

可写在全局监听器里成为全局变量。

2.  RMI 服务器上 root 身份登录,输入 Vi /etc/hosts ,在第一行添加 192.168.39.11     ieie

3.  若是用spring, 则在 RmiServiceExporter 中添加属性 <property name="registryHost"  value="192.168.39.11" />

 

 

 通过spring设置java系统属性

 

在做RMI的时候需要如遇到rmi所在服务是多网卡时,需要对系统属性java.rmi.server.hostname进行设置 ,以前我们会扩展spring的listener

[java] view plaincopyprint?
  1. public class SpringContextLoaderListener extends ContextLoaderListener {  
  2.   
  3.     @Override  
  4.     public void contextInitialized(ServletContextEvent event) {  
  5.         //初始化之前在设置java.rmi.server.hostname  
  6.                 System.setProperty("java.rmi.server.hostname""ip");  
  7.         super.contextInitialized(event);  
  8.     }  
  9.   
  10. }  


虽然这样没问题, 但是spring为我们提供了更简单快捷的方式,只需要配置就行了


[html] view plaincopyprint?
  1. <bean  
  2.     class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">  
  3.     <property name="targetObject" value="#{@systemProperties}" />  
  4.     <property name="targetMethod" value="putAll" />  
  5.     <property name="arguments">  
  6.         <props>  
  7.             <prop key="java.rmi.server.hostname">${java.rmi.server.hostname}</prop>  
  8.         </props>  
  9.     </property>  
  10. </bean>  

注意:java.rmi.server.hostname这个是属性文件里的KEY,比如:在设置数据库连接的文件里就可以.

jdbc.properties

在jdbc.properties属性文件中定义属性值:
    jdbc.driverClassName= com.mysql.jdbc.Driver
    jdbc.url= jdbc:mysql://localhost:3306/manger 
    jdbc.username=root
    jdbc.password=1234

    java.rmi.server.hostname=127.0.0.1
    提示 经常有开发者在${xxx}的前后不小心键入一些空格,这些空格字符将和变量合并后作为属性的值。如: <property name="username" value=" ${jdbc.username} "></property> 的属性配置项,在前后都有空格,被解析后,username的值为“ 1234 ”,这将造成最终的错误,因此需要特别小心。

Spring配置数据源及加载配置文件

 

<!-- jdbc properties -->
 <bean id="jdbcConfigurer"  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
  <property name="locations" value="/WEB-INF/jdbc.properties"/>  
  </bean>
    <!-- jdbc data source -->
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"      
          scope="singleton" name="c3po_datasource">     
     <property name="driverClass" value="${jdbc.driver}"/>     
     <property name="jdbcUrl" value="${jdbc.url}"/>     
     <property name="user" value="${jdbc.user}"/>     
     <property name="password" value="${jdbc.password}"/>     
 </bean>


 

Failed to start jmx connector: Cannot bind to URL(jmxrmi)

Failed to start jmx connector: Cannot bind to URL[rmi://localhost:1099/jmxrmi] 

INFO  ActiveMQ JMS Message Broker (localhost, ID:cnshawlt1129-1495-1316140863881-0:0) started
INFO  Connector vm://localhost Started
 WARN  Failed to start jmx connector: Cannot bind to URL [rmi://localhost:1099/jmxrmi]: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
 java.net.ConnectException: Connection refused: connect]

 

 

Xml代码 
1. <bean id="broker" class="org.apache.activemq.xbean.BrokerFactoryBean"> 
2.   <property name="config" value="classpath:org/apache/activemq/xbean/activemq.xml" /> 
3.   <property name="start" value="true" /> 
4. </bean> 
5.  
6. <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="broker"> 
7.   <property name="brokerURL" value="vm://localhost"/> 
8. </bean> 
   如果persistent是true,那么ActiveMQ会在当前目录下创建一个缺省值是activemq-data的目录用于持久化保存数据。需要注意的是,如果程序中启动了多个不同名字的VM broker,那么可能会有如下警告:Failed to start jmx connector: Cannot bind to URL [rmi://localhost:1099/jmxrmi]: javax.naming.NameAlreadyBoundException…可以通过在transportOptions中追加 broker.useJmx=false来禁用JMX来避免这个警告

2.2.2 TCP Transport
TCP transport 允许客户端通过TCP socket连接到远程的broker。以下是配置语法:
tcp://hostname:port?transportOptions
Transport Options的可选值如下:

Option NameDefault ValueDescriptionminmumWireFormatVersion0The minimum version wireformat that is allowedtracefalseCauses all commands that are sent over the transport to be loggeduseLocalHosttrueWhen true, it causes the local machines name to resolve to “localhost”.socketBufferSize64 * 1024Sets the socket buffer size in bytessoTimeout0sets the socket timeout in millisecondsconnectionTimeout30000A non-zero value specifies the connection timeout in milliseconds. A zero value means wait forever for the connection to be established. Negative values are ignored.wireFormatdefaultThe name of the WireFormat to usewireFormat.* All the properties with this prefix are used to configure the wireFormat. See Configuring Wire Formats for more information

例如:tcp://localhost:61616?trace=false