linux怎么查看本机内存大小
385
2022-09-07
Spring最佳实践 –Spring 提供的远程访问(一)
Spring核心技术与最佳实践 –Spring 提供的远程访问
如果需要将应用程序的某些功能暴露给远程客户端访问,就需要某种远程调用机制。除了Java内置的标准的RMI远程调用机制外,还有众多可供选用的远程调用方案。Spring框架提供了对各种远程访问的良好支持,包括RMI、HTTP远程调用和WEB服务。
8.1RMI远程调用
RMI是Java标准的远程方法调用接口,即Rmote Method Invocation的缩写。RMI从JDK1.1就引入了,它基于Java的序列化机制实现远程方法调用。下面我们分别来看看手动使用RMI和在Spring中使用RMI的异同。
8.1.1 实现RMI
让我们来看一个常见的例子。假定应用程序已经设计并实现了一个用户管理的模块,允许创建新用户并让用户登陆,定义RMI工程结构如下:
User 对象代表一个用户。
public classUserimplements Serializable {
private Stringusername; private Stringpassword;
public User(String username, String password) { this.username this.password }
public String getUsername() { returnusername; } public String getPassword() { returnpassword; } }
|
UserService定义了用户服务的接口
public interface UserService {
User login(String username, String password);
void create(String username, String password); } |
并且有一个默认的实现类。
public class UserServiceImpl implements UserService {
private Map<String, String>users =new HashMap<String, String>();
@Override public void create(String username, String password) { if(username==null || password==null){ throw new IllegalArgumentException("Invalid args."); } if(users.get(username)!=null){ throw new RuntimeException("User exist!"); } users.put(username, password); }
@Override public User login(String username, String password) { if(username==null || password==null){ throw new IllegalArgumentException("Invalid args."); } if(password.equals(users.get(username))){ return new User(username, password); } throw new RuntimeException("Login failed."); } }
|
这个模块在应用程序中运行良好,现在,我们希望将它暴露为远程服务,以便其他远程应用程序也能够通过这个模块来管理用户。为了以RMI的方式来实现这个远程服务,我们先看看实现RMI的必要条件。
(1) 服务接口必须从Remote派生,并且在每个远程方法中抛出RemoteException,很明显UserServcie不符合这个条件。
(2) 实现类处理实现服务接口外,还必须从UnicatRemoteObject派生,因此,UserServiceImpl也不符合这个条件。
(3)
所有的方法的参数和返回值都必须是基本类型,或者实现了Serialzable接口,或者实现了Remote接口,幸运的是,对User对象添加一个Serializable接口是非常容易的。
为了实现RMI远程调用,按照传统的RMI调用方式,我们不得不修改UserService接口。然后,如果让UserService接口的每个方法都抛出RemoteException,由于RemoteException是CheckedExcpetion,势必造成应用程序其他派生依赖UserService接口的代码无法编译通过,其代价是巨大的。因此,只好考虑定义另一个RmiUserService接口。
public interface RmiUserService extends Remote {
User login(String username, String password) throws RemoteException;
void create(String username, String password)throws RemoteException; }
|
然后,为RmiUserService接口在编写一个实现类。
public classRmiUserServiceImplextends UnicastRemoteObjectimplements RmiUserService {
service = new UserServiceImpl();
public RmiUserServiceImpl()throws RemoteException { }
@Override public void create(String username, String password) throws RemoteException { service.create(username, password); }
@Override public User login(String username, String password)throws RemoteException { returnservice.login(username, password); }
public static void main(String[] args)throws Exception { LocateRegistry.createRegistry(1099); // Naming.bind("rmi://localhost:1099/UserService",new RmiUserServiceImpl()); System.out.println("服务端已经启动............"); } }
|
现在,我们终于可以编写客户端来调用这个RmiUserService了。编写一个Client类来带哦用RMI服务。
public class Client { public static void main(String[] args)throws Exception { RmiUserService service = (RmiUserService)Naming.lookup("rmi://localhost:1099/UserService"); "new_user","a_test"); "new_user","a_test"); if(user!=null){ System.out.println("------------------------------"); System.out.println("用户名:" + user.getUsername() +"登陆成功!"); System.out.println("------------------------------"); } } } |
首先启动RMI 服务(RmiUserServiceImpl.java),然后再启动客户端(Client.java).,客户端运行后,控制台打印如下信息:
------------------------------ 用户名:new_user登陆成功! ------------------------------ |
从上面的例子可以看到,为了实现一个简单的RMI远程调用,我们不得不遵循RMI规范,定义规范符合RMI调用的接口和实现类,还必须手动编译出Stub类共客户端使用。这实在是台麻烦了,为了避免这么复杂的步骤,我们看看用Spring能否简化RMI的调用。
8.1.2 在Spring中输出RMI
我们新建一个RMI_Spring工程,复制User、UserService、UserServiceImpl者3个类,然后我们看看如何在Spring中将UserService作为一个RMI服务输出。
令我们非常兴飞的是,不用编写一行代码,就可以直接在Spring的XML配置文件中将UserService作为RMI服务输出。
<beanid="userService"class="com.zsw.service.UserServiceImpl"/>
<beanid="rmiService"class="org.springframework.remoting.rmi.RmiServiceExporter"> <propertyname="serviceName"value="UserService"/> <propertyname="service"ref="userService"/> <propertyname="serviceInterface"value="com.zsw.service.UserService"/> <propertyname="registryPort"value="1099"/> </bean> |
唯一的代码是编写Main.java中的main()方法启动的Spring容器。
public static void main(String[] args) { new ClassPathXmlApplicationContext("server.xml"); System.out.println("Server has bean started!"); } |
编写客户端稍微麻烦一点,需要用到Spring框架提供的RmiProxyFactoryBean.
public static void main(String[] args)throws Exception {
RmiProxyFactoryBean factory =new RmiProxyFactoryBean(); factory.setServiceInterface(UserService.class); "rmi://localhost:1099/UserService"); factory.afterPropertiesSet(); UserService userService = (UserService)factory.getObject();
"test","password"); System.out.println(userService.login("test","password")); try { "test","bad-password"); }catch(Exception e) { System.err.println(e.getMessage()); } } |
先执行Main中的main()方法,启动RMI服务,在启动客户的进行调用测试,输出结果如下:
信息: Binding service 'UserService' to RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[169.254.53.167:1099](local),objID:[0:0:0, 0]]]] Server has bean started! |
com.zsw.entity.User@173831b Login failed. |
令人非常兴奋的是,整个过程没有引入任何RMI必须的接口,虽然客户段调用代码稍微复杂,并且需要Spring的jar包支持,但是,由于不需要手动调用rmic生成Stub类,整个带哦用过程被大大简化了。
8.1.3 访问RMI
事实上,如果客户端也在Spring容器中启动,则完全可以在XML配置文件中定义UserService并直接使用。
<bean id="userService" class="com.zsw.service.UserServiceImpl" /> <bean id="rmiService" class="org.springframework.remoting.rmi.RmiServiceExporter"> <property name="serviceName" value="UserService" /> <property name="service" ref="userService" /> <property name="serviceInterface" value="com.zsw.service.UserService" /> <property name="registryPort" value="1099" /> </bean> |
这样,客户端完全不必编写远程服务接口的代码,就可以直接将userService注入到需要的组件中去。
对于传统的RMI调用,服务段发布了RMI服务后,还必须通过rmic编译出存根共客户端使用,这个调用过程如下:
Stub的作用便是将客户端对RmiService接口的调用转变成实际的远程调用,在Spring中,由于使用了JDK动态代理,因此可以在运行期动态实现RmiServiceStub,免去了使用rmic编译存根的麻烦。
如果RMI服务不是由Spring包装后启动的,而是直接以Remote接口实现,例如,在RMI工程中的RmiUserServiceImpl类实现了RMI服务,能否在Spring中调用呢?答案是肯定的,只需要将Client代码修改为实际的RMI服务接口。
public static void main(String[] args) throws Exception { BeanFactory context = new ClassPathXmlApplicationContext("client.xml"); "userService");
"test", "password"); System.out.println(userService.login("test", "password")); try { "test", "bad-password"); }catch(Exception e) { System.err.println(e.getMessage()); } } |
执行,跟RmiProxyFactoryBean执行的效果一样。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~