【Java EE 学习 78 中】【数据采集系统第十天】【Spring远程调用】

1、远程调用概述

  1.远程调用的定义

    在一个程序中就像调用本地中的方法同样调用另一个远程程序中的方法,可是整个过程对本地彻底透明,这就是远程调用。spring已经可以很是成熟的完成该项功能了。html

  2.spring远程调用的原理

    客户端调用本地接口中的一个方法调用的时候将会被客户端代理拦截,并向远程服务器发起一个servlet请求(服务器中的web.xml文件中专门配置了该Servlet),服务器接受请求,并根据spring配置文件和请求的url找到对应的“导出器”,导出器将pojo转换成控制器controller,并最终完成请求的任务,并将结果以流的形式返回到客户端,客户端由此拿到额执行结果的数据。java

    服务端和客户端之间通讯使用的是序列化对象类型,以后会说明该问题。git

  3.使用远程调用的好处是什么

    实现了远程调用的程序可以对外提供一种“服务”,其它程序可以没必要重写它的功能模块就可以实现某个功能,其它程序所须要作的仅仅只是一句调用的代码而已。这样作可以极大的提升开发效率。github

    以当前的数据采集系统为例,已经实现了对数据的统计功能,可是其它网站想要重用你的代码就很困难了,好比其它网站在咱们的数据采集系统中新建了调查,调查完毕以后其它网站想要获取统计结果数据并在本身的应用程序中进行统计,这时候获取统计数据就是比较重要的事情了web

  4.获取统计结果的方法

  (1).在数据采集系统中使用“导出到XML”文件或者“导出到Excel文件”的功能将数据导出到文件中,而后在本身的应用程序中对这些数据进行分析。使用这种方式不只须要分析数据,还要本身进行统计代码的书写,是一种很是笨的方法。spring

  (2).使用数据采集系统提供的公共接口,直接远程调用对应接口中的方法,实现过程对本地透明。获取的数据是已经彻底封装好的数据,直接使用便可。可是使用这种方式须要两个程序都使用spring环境(java环境),并且客户端须要拥有接口和相关bean的class文件。express

  5.总结。

    综合以上两方面使用第二种方式是更为理想的方式,可是缺点也是很明显的,那就是强制两端都使用spring环境。可以有折中方案呢?即可以有相似于远程调用的功能,可是不用强制限制客户端使用spring或者java环境,解决方案就是使用WebService,使用WebService虽然解决了以上的矛盾问题,可是它自己也有缺点,那就是效率比较低。从中能够看出使用远程调用和使用WebService各有优劣之处,应当根据不一样的环境使用不一样的方法。apache

    当两端程序都是用Spring环境的状况下,使用远程调用提供外部服务;当其中有一端不使用Spring的时候,使用WebServiceapi

2、Spring实现远程调用Demo案例

    源代码地址:https://github.com/kdyzm/day78_spring_rpc_demo/blob/master/readme.md浏览器

  1.在Eclipse下建立一个Web项目,RPC_Server和一个普通java项目:RPC_Client

  2.在RPC_Server上配置远程调用服务

    首先将Spring的相关jar包加上:特别是下面强调部分的jar包,是远程实现远程调用的核心jar包。

    com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
    com.springsource.net.sf.cglib-2.2.0.jar
    com.springsource.org.aopalliance-1.0.0.jar
    com.springsource.org.apache.commons.codec-1.3.0.jar
    com.springsource.org.apache.commons.logging-1.1.1.jar
    com.springsource.org.aspectj.tools-1.6.6.RELEASE.jar
    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
    com.springsource.org.quartz-1.6.2.jar
    commons-lang-2.5.jar
    log4j.jar
    org.springframework.aop-3.1.0.RELEASE.jar
    org.springframework.asm-3.1.0.RELEASE.jar
    org.springframework.aspects-3.1.0.RELEASE.jar
    org.springframework.beans-3.1.0.RELEASE.jar
    org.springframework.context-3.1.0.RELEASE.jar
    org.springframework.context.support-3.1.0.RELEASE.jar
    org.springframework.core-3.1.0.RELEASE.jar
    org.springframework.expression-3.1.0.RELEASE.jar
    org.springframework.jdbc-3.1.0.RELEASE.jar
    org.springframework.orm-3.1.0.RELEASE.jar
    org.springframework.transaction-3.1.0.RELEASE.jar
    org.springframework.web-3.1.0.RELEASE.jar
    org.springframework.web.servlet-3.1.0.RELEASE.jar
    slf4j-api-1.5.8.jar
    slf4j-log4j12.jar

  3.配置web.xml文件(分发器配置),加入一个Servlet的配置

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
 3   <display-name>RPC_Server</display-name>
 4   <servlet>
 5       <servlet-name>dispatcherServlet</servlet-name>
 6       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 7       <init-param>
 8           <param-name>contextConfigLocation</param-name>
 9           <param-value>classpath:applicationContext.xml</param-value>
10       </init-param>
11   </servlet>
12   <servlet-mapping>
13       <servlet-name>dispatcherServlet</servlet-name>
14       <url-pattern>*.service</url-pattern>
15   </servlet-mapping>
16   <welcome-file-list>
17     <welcome-file>index.html</welcome-file>
18     <welcome-file>index.htm</welcome-file>
19     <welcome-file>index.jsp</welcome-file>
20     <welcome-file>default.html</welcome-file>
21     <welcome-file>default.htm</welcome-file>
22     <welcome-file>default.jsp</welcome-file>
23   </welcome-file-list>
24 </web-app>

   4.建立WelcomeService接口和WelcomeServiceImpl实现类

1 package com.kdyzm.rpc.server;
2 
3 public interface WelcomeService {
4     public String say(String name);
5 }
 1 package com.kdyzm.rpc.server;
 2 
 3 public class WelcomServiceImpl implements WelcomeService{
 4 
 5     @Override
 6     public String say(String name) {
 7         System.out.println("你好,"+name);
 8         return name;
 9     }
10 }

  5.将Service注入spring容器,最重要的还要配置好导出器

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
 4     xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/beans/spring-beans-2.5.xsd
 6         http://www.springframework.org/schema/context file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/context/spring-context-2.5.xsd
 7         http://www.springframework.org/schema/aop file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/aop/spring-aop-2.5.xsd
 8         http://www.springframework.org/schema/tx file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/tx/spring-tx-2.5.xsd">
 9         <bean id="welcomeService" class="com.kdyzm.rpc.server.WelcomServiceImpl"></bean>
10         <bean class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" name="/ws.service">
11             <property name="serviceInterface">
12                 <value>com.kdyzm.rpc.server.WelcomeService</value>
13             </property>        
14             <property name="service" ref="welcomeService"></property>                
15         </bean>
16 </beans>

  6.浏览器测试

    (1)结果显示

    将服务端的程序发布到tomcat,在浏览器上访问http://localhost:8080/RPC_Server/ws.service,观察浏览器显示结果。

  

    若是显示这样的信息表示服务端已经配置成功了。为何会是这样呢?由于服务端提供的不是http服务,而是应用程序的服务,必须使用正确的客户端才能正确的访问。

    (2)缘由分析

      先作好7.中的客户端程序,而后进行下面的流程。

      之因此会出现这种现象,是由于远程调用的时候客户端和服务端之间的数据传递使用的是序列化对象的形式,可使用eclipse自带的TCP/IP监视器查看,关于它的用法就是将客户端的请求端口改为监视端口,该监视器会将从指定监听端口上监听到的流量转发到实际请求的端口号,服务器响应的时候则反过来。

      

    最终捕获到到的请求和相应信息:

  7.RPC_Client配置

    (1)添加类库

      将RPC_Server中的依赖类库完完整整的拷贝到RPC_Client中。而后将接口WelcomeService也拷贝到该项目中,可是实现类WelcomeServiceImpl就不要拷贝到项目中了。

    (2)配置Spring的配置文件

      实际上客户端的配置文件只须要配置一处地方就能够了,配置的是客户端代理。

1 <bean id="wsClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
2     <property name="serviceInterface">
3         <value>com.kdyzm.rpc.client.WelcomeService</value>
4     </property>
5     <property name="serviceUrl">
6         <value>http://localhost:8080/RPC_Server/ws.service</value>
7     </property>
8 </bean>

    该配置实际上创建了本地接口和远程Servlet之间的一个映射关系,当在本地访问配置的Service接口的时候,代理会向远程服务器发起远程访问,访问的正是以前在服务器中配置的servlet访问地址。

  完整的配置为:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
 4     xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/beans/spring-beans-2.5.xsd
 6         http://www.springframework.org/schema/context file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/context/spring-context-2.5.xsd
 7         http://www.springframework.org/schema/aop file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/aop/spring-aop-2.5.xsd
 8         http://www.springframework.org/schema/tx file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/tx/spring-tx-2.5.xsd">
 9         <!-- 配置客户端代理 -->
10         <bean id="wsClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
11             <property name="serviceInterface">
12                 <value>com.kdyzm.rpc.client.WelcomeService</value>
13             </property>
14             <property name="serviceUrl">
15                 <value>http://localhost:8080/RPC_Server/ws.service</value>
16             </property>
17         </bean>
18 </beans>

    (3)接下来书写测试类,测试类很是简单,只须要拿到注入的Service对象,而后调用接口中的方法便可。

1 public class Main{
2 public static void main(String args[]){
3  ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
4  WelcomeService service=(WelcomeService) context.getBean("wsClient");
5  service.say("狗蛋");  
6  }
7 }

    (4)运行测试程序,查看客户端和服务端控制台的打印结果

      这里是显示的是tomcat打印的结果,也就是说客户端已经正确访问了服务端的程序。调用了服务端的方法。

      到这里spring远程调用的实现已经完成了。

3、将数据采集系统中的SurveyService接口配置为公共的服务接口。

  已经有了以前的例子,将当前的数据采集系统中的SurveyService配置成为接口,实现spring的远程调用也就很是简单了。

  1.首先仍是在web.xml配置文件中配置分发器

 1 <!-- 配置spring远程调用使用的分发器 -->
 2 <servlet>
 3     <servlet-name>dispatcherServlet</servlet-name>
 4     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 5     <init-param>
 6          <param-name>contextConfigLocation</param-name>
 7           <param-value>classpath:spring/applicationContext.xml</param-value>
 8       </init-param>
 9 </servlet>
10 <servlet-mapping>
11     <servlet-name>dispatcherServlet</servlet-name>
12     <url-pattern>*.service</url-pattern>
13 </servlet-mapping>

  2.将服务类归入Spring容器管理。

    服务接口是SurveyService,实现类是SurveyServiceImpl,在项目中以前都已经写好了,并且不须要使用XML的形式归入Spring容器管理,它们都是用注解的形式归入了Spring容器中并进行了配置。

  3.配置spring配置文件,这里只须要进行一个导出器的配置便可。

1 <!-- 配置Spring远程调用服务端 -->
2 <bean name="/surveyService.service" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
3     <property name="serviceInterface" value="com.kdyzm.service.SurveyService"></property>
4     <property name="service" ref="surveyService"></property>
5 </bean>

  4.浏览器测试,显示500错误,表示配置spring的远程调用没有问题。

  

  到此为止,服务端已经配置完成。

  5.客户端的配置

    (1)为了简化处理,将相关类可以直接使用,直接将服务端加入到客户端的classpath,这样就可以直接使用服务端自定义的类了。

    方法:项目右键->build path->config build path->projects,选择LSN_surveypark项目,这样LSN_surveypark项目就变成了RPC_Client的依赖项目了。

    (2)配置spring配置文件。

1 <bean id="surveyClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
2     <property name="serviceInterface">
3         <value>com.kdyzm.service.SurveyService</value>
4     </property>
5     <property name="serviceUrl">
6         <value>http://localhost:8080/LSN_ServyPark/surveyService.service</value>
7     </property>
8 </bean>

    (3)这样客户端就配置完成了。

  6.直接在客户端进行测试便可。

  这里以获取全部可用的调查为例进行测试,注意各个Bean对象最好都是先实现Serialize接口,这样才能进行网络传输。不然的话服务端首先会报错,由于没法序列化。

  测试代码:

 1 package com.kdyzm.rpc.client;
 2 
 3 import java.util.Collection;
 4 
 5 import org.springframework.context.ApplicationContext;
 6 import org.springframework.context.support.ClassPathXmlApplicationContext;
 7 
 8 import com.kdyzm.domain.Page;
 9 import com.kdyzm.domain.Question;
10 import com.kdyzm.domain.Survey;
11 import com.kdyzm.service.SurveyService;
12 
13 public class Main {
14     public static void main(String[] args) {
15         ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
16         /*WelcomeService service=(WelcomeService) context.getBean("wsClient");
17         service.say("狗蛋");*/
18         SurveyService surveyService=(SurveyService) context.getBean("surveyClient");
19         Collection<Survey>surveys=surveyService.getAllAvailableSurveys();
20         for(Survey survey:surveys){
21             System.out.println(survey.getSurveyId()+"_"+survey.getTitle());
22             //使用transient关键字实现对字段的屏蔽(不序列化)
23             if(survey.getPages()!=null)
24             for(Page page:survey.getPages()){
25                 System.out.println("\t"+page.getPageId()+"_"+page.getOrderNo());
26                 for(Question question:page.getQuestions()){
27                     System.out.println("\t\t"+question.getQuestionId()+"_"+question.getTitle());
28                 }
29             }
30         }
31     }
32 }

    运行程序,显示查看控制台显示的结果信息。

  

    显示的结果是一共有8个可用的调查,这些打印结果都是封装到对象以后传输过来的。以前的错误信息提示就忽略便可,是jar包冲突致使的问题,不影响测试结果。

4、spring远程调用功能完成。

相关文章
相关标签/搜索