论坛首页 Java企业应用论坛

Spring HttpInvoker的简单封装:简洁的api

浏览 7707 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-11-19  


Spring HttpInvoker的简单封装,优点如下:

1、客户端无xml配置
2、客户端基于接口调用,无需知道接口的服务名。
(大众型使用方式:接口可以打成jar分发到服务端和客户端程序使用。我是使用maven构建项目,所以建立了一个service-code项目供serviceProject和clientProject引用)
3、服务端只需配置简单的2步骤,后面介绍。

封装文件一共7个类,先分别说下客户端和服务端的使用配置,再解释原理:

SpringMVCApplicationContextUtil.java

SpringRemoteServiceContext.java

RemoteServiceDescService.java

DefaultRemoteServiceDescServiceImpl.java

HttpInvokerRemoteServiceDescContainer.java

HttpInvokerClientServiceFactory.java

RemoteConnectFailureExceptionExt.java

 

客户端调用服务的示例代码如下:

HttpInvokerClientServiceFactory.init();//只需初始化一次
String serverURL = "http://localhost:8080/liangfa-analysis";//服务地址
//获得远程服务TestService,就像是调用本地服务那样(自行捕获通信异常)
TestService testService = HttpInvokerClientServiceFactory.getService(serverURL, TestService.class);
//调用服务
testService.sayHello();
//客户端api是不是很简单?? 只需要一个HttpInvokerClientServiceFactory就搞定了 : )

 

 

服务端的2处配置:

1、配置远程服务文件remoting-servlet.xml,除了配置业务有关的服务(如TestService),需要加入RemoteServiceDescService,服务名约定为“/remote/remoteServiceDesc”

(HttpInvokerRemoteServiceDescContainer中有常量配置)

<!--暴漏远程服务:服务列表描述 required-->
    <bean name="RemoteServiceDescService" class="com.ksource.liangfa.ws.core.util.remote.DefaultRemoteServiceDescServiceImpl">
    </bean>
    <bean name="/remote/remoteServiceDesc" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
        <property name="service" ref="RemoteServiceDescService"></property>
        <property name="serviceInterface" value="com.ksource.liangfa.ws.core.util.remote.RemoteServiceDescService"/>
    </bean>

    <!--测试服务-->
    <bean name="/remote/TestService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
        <property name="service" ref="RemoteTestService"></property>
        <property name="serviceInterface" value="com.ksource.liangfa.ws.core.service.datacenter.TestService"/>
    </bean>
......其他服务.......

 
2、初始化服务描述的上下文(remoting-servlet.xml)

SpringRemoteServiceContext.initContext(SpringMVCApplicationContextUtil.getWebApplicationContext(config.getServletContext(), "springServletName"));

 

我的做法是在项目的初始化servlet(SystemInit.init)里调用,如下:
配置web.xm,在项目启动servlet里

<servlet>
        <servlet-name>SystemInit</servlet-name>
        <servlet-class>com.ksource.syscontext.SystemInit</servlet-class>
        <load-on-startup>3</load-on-startup>
  </servlet>

 

 

 public class SystemInit extends HttpServlet {
  
      @Override
      public void init(ServletConfig config) throws ServletException {
		SpringRemoteServiceContext.initContext(SpringMVCApplicationContextUtil.getWebApplicationContext(config.getServletContext(), "springServlet"));
	.......
	.......
	}
  }

 

   
实现思路:

SpringMVCApplicationContextUtil用于获取spring mvc的bean容器,这个容器包含了spring mvc配置文件里配置的bean(同样包含remoting-servlet.xml)

DefaultRemoteServiceDescServiceImpl本身也是暴漏的服务,但他的作用主要是用于描述服务端暴漏的服务,有点类WSDL的意味。

SpringRemoteServiceContext通过bean容器找出暴漏的服务,保存到map中

public class SpringRemoteServiceContext {
 private static Map<String, HttpInvokerServiceExporter> servicesMap;
 public static void initContext(
   ApplicationContext applicationContext) {
  servicesMap = applicationContext.getBeansOfType(HttpInvokerServiceExporter.class);
 }
 public static Map<String, HttpInvokerServiceExporter> getServicesMap(){
  return servicesMap;
 }
}

 
HttpInvokerRemoteServiceDescContainer用于保存一个服务端的全部接口的服务地址。

HttpInvokerClientServiceFactory用于根据接口名称,来获取远程服务对象。
HttpInvokerClientServiceFactory.getService(String serverUrl, Class<T> clazz)方法,会根据
serverUrl来获取HttpInvokerRemoteServiceDescContainer对象,然后通过服务描述来创建HttpInvokerProxyFactoryBean对象,然后HttpInvokerProxyFactoryBean.getObject()来得到远程服务。

 

大致思路如上,整理的比较乱,可参见源码,包名可自行修改后使用。

   发表时间:2012-11-19  
楼主是否有研究过暴露服务端服务用annotation实现呢?因为每次暴露服务都要到配置文件加上那么一段xml配置,感觉很罗嗦,是否有更简便到方法?多谢。。。
0 请登录后投票
   发表时间:2012-11-20  
优点是客户端无xml配置,我觉得客户端用还是用HttpInvokerProxyFactoryBean加上注解比这样调用要方便很多,不用每次使用的时候还得写一段这样的代码
HttpInvokerClientServiceFactory.init();//只需初始化一次 
String serverURL = "http://localhost:8080/liangfa-analysis";//服务地址 
//获得远程服务TestService,就像是调用本地服务那样(自行捕获通信异常) 
TestService testService = HttpInvokerClientServiceFactory.getService(serverURL, TestService.class);
0 请登录后投票
   发表时间:2012-11-20  
yangpeihai 写道
楼主是否有研究过暴露服务端服务用annotation实现呢?因为每次暴露服务都要到配置文件加上那么一段xml配置,感觉很罗嗦,是否有更简便到方法?多谢。。。

我实现了一个用annotation暴露服务的方法,http://code.google.com/p/spring-json-rpc/source/browse/#svn%2Ftrunk%2Fsrc%2Fmain%2Fjava%2Fcom%2Ftaofang%2Fjsonrpc%2Fscanner
你可以自己把beandefinition改成你需要的exporter,例如HttpInvokerexporter,然后命名一个你想要的annotation
0 请登录后投票
   发表时间:2012-11-20  
HttpInvokerClientServiceFactory.init();在客户端系统起来后,只需要执行一次。
之后,你调用服务的时候就像从是从spring容器里getBean一样,代码已经很少了。

服务端暴漏可以实现,xiaoZ5919兄的扫描就是思路
0 请登录后投票
   发表时间:2012-11-20   最后修改:2012-11-20
还有个人问题,客户端如果也用注解实现的话,就意味着服务地址需要写在注解里。
万一服务地址更换了,怎么办....

另外HttpInvokerProxyFactoryBean本身是支持多服务端的,我封装的最初需求是,比如我想到a、b、c.多个应用里调用同一服务testService来完成类似数据汇总的功能,而a、b、c的服务器地址我一般是配置在数据库中,我不能通过xml来配置。

基于注解适应一种场景,基于api调用又是另一种适用场景
0 请登录后投票
   发表时间:2012-11-20  
服务地址 换了 就修改xml配置文件。写在数据库中难道还需要每次请求的时候先从数据库中查询一下服务地址。还是查询了再缓存。如是这样,可以借鉴dubbo的服务注册中心。
0 请登录后投票
   发表时间:2012-11-20  
xiaoZ5919 写道
服务地址 换了 就修改xml配置文件。写在数据库中难道还需要每次请求的时候先从数据库中查询一下服务地址。还是查询了再缓存。如是这样,可以借鉴dubbo的服务注册中心。


是类似的,数据中心注册
0 请登录后投票
   发表时间:2012-11-20  
xiaoZ5919 写道
yangpeihai 写道
楼主是否有研究过暴露服务端服务用annotation实现呢?因为每次暴露服务都要到配置文件加上那么一段xml配置,感觉很罗嗦,是否有更简便到方法?多谢。。。

我实现了一个用annotation暴露服务的方法,http://code.google.com/p/spring-json-rpc/source/browse/#svn%2Ftrunk%2Fsrc%2Fmain%2Fjava%2Fcom%2Ftaofang%2Fjsonrpc%2Fscanner
你可以自己把beandefinition改成你需要的exporter,例如HttpInvokerexporter,然后命名一个你想要的annotation


我打开页面显示“No files in the selected directory. Select a subdirectory, if one exists, or another directory from the tree on the left.”
请发一份到我邮箱 47257921@qq.com  非常感谢
0 请登录后投票
   发表时间:2012-11-20  
yangpeihai 写道
xiaoZ5919 写道
yangpeihai 写道
楼主是否有研究过暴露服务端服务用annotation实现呢?因为每次暴露服务都要到配置文件加上那么一段xml配置,感觉很罗嗦,是否有更简便到方法?多谢。。。

我实现了一个用annotation暴露服务的方法,http://code.google.com/p/spring-json-rpc/source/browse/#svn%2Ftrunk%2Fsrc%2Fmain%2Fjava%2Fcom%2Ftaofang%2Fjsonrpc%2Fscanner
你可以自己把beandefinition改成你需要的exporter,例如HttpInvokerexporter,然后命名一个你想要的annotation


我打开页面显示“No files in the selected directory. Select a subdirectory, if one exists, or another directory from the tree on the left.”
请发一份到我邮箱 47257921@qq.com  非常感谢


我写了一篇blog [url]http://xiaoz5919.iteye.com/blog/1730552 [/url]并上传附件了
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics