一、Spring HTTP Invoker简介

Spring HTTP invoker 是 spring 框架中的一个远程调用模型,执行基于 HTTP 的远程调用(意味着可以通过防火墙),并使用 java 的序列化机制在网络间传递对象。这需要在远端和本地都使用Spring才行。客户端可以很轻松的像调用本地对象一样调用远程服务器上的对象,这有点类似于 webservice ,但又不同于 webservice ,区别如下:

WebService Http Invoker
跨平台,跨语言 只支持 java 语言
支持 SOAP ,提供 wsdl 不支持
结构庞大,依赖特定的 webservice 实现,如 xfire等 结构简单,只依赖于 spring 框架本身

说明:

1. 服务器端:通过 HTTP invoker 服务将服务接口的某个实现类提供为远程服务

2. 客户端:通过 HTTP invoker 代理向服务器端发送请求,远程调用服务接口的方法

3. 服务器端与客户端通信的数据均需要序列化

二、配置服务器端和客户端的步骤

配置服务器端

1. 添加 springJAR 文件

2. 创建相应的DTO(如果需要用到的话)

3. 创建服务接口

4. 创建服务接口的具体实现类

5. 公开服务

配置客户端

1. 添加 springJAR 文件

2. 创建相应的DTO(如果需要用到的话)

3. 创建服务接口

4. 访问服务

三、实例讲解

配置服务器端

先来个项目结构图:

1). 添加 springJAR 文件,这就不用说了,直接照着图片添加相应的类库。

2). 创建服务接口和相应的DTO(Data Transmission Object)

这里我们需要调用远端的服务来查询一个User对象,因此需要DTO啦。下面这个User类就是用于在网络中传输的POJO类,也就是DTO啦,因此需要实现Serializable接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.abc.invoke.bean;
 
import java.io.Serializable;
 
public class User implements Serializable {
    private static final long serialVersionUID = -6970967506712260305L;
    private String name;
    private int age;
    private String email;
 
    public int getAge() {
        return age;
    }
     
    public void setAge(int age) {
        this.age = age;
    }
     
    public String getName() {
        return name;
    }
     
    public void setName(String name) {
        this.name = name;
    }
     
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", email=" + email + "]";
    }
}

3). UserService是一个接口,里面定义了服务的方法,这里面的方法将会被客户端调用:

1
2
3
4
5
6
7
package com.abc.invoke.server.service;
 
import com.abc.invoke.bean.User;
 
public interface UserService {
    public User getUserbyName(String name);
}

4). 创建服务接口的具体实现类。这里的UserServiceImpl是实现了UserService方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.abc.invoke.server.service.impl;
 
import com.abc.invoke.bean.User;
import com.abc.invoke.server.service.UserService;
 
public class UserServiceImpl implements UserService {
    public User getUserbyName(String name) {
        User u = new User();
        u.setName(name);
        u.setEmail("abc@abc.com");
        u.setAge(20);
        return u;
    }
}

这里面我没有写DAO等层面的东西,因为那些不是这篇文章要讲述的内容,因而我只是简单的将传给服务端的参数封装到对象里的一个字段就返回了。

5). 公开服务

下面是web.xml文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <display-name>SpringInvoke</display-name>
    <servlet>
        <servlet-name>service</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:service-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>service</servlet-name>
        <url-pattern>/service/*</url-pattern>
    </servlet-mapping>
     
    <!-- 其实下面这个welcome-file-list没啥用,我留着只是为了在起好Tomcat后不会报一个404而已 -->
    <welcome-file-list>
            <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

这里我们使用/service作为service的前缀,那么客户端请求调用时需要加上这个前缀,比如:

http://{host}:{port}/InvokeServer/service/{serviceName}

里面用到的service-servlet文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
    <!-- 这个Bean映射了当URL是/userService时,处理器为userServiceInvoker -->
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/userService">userServiceInvoker</prop>
            </props>
        </property>
    </bean>
 
    <!-- Announce that this interface is a HTTP invoker service. -->
    <bean id="userServiceInvoker" 
        class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
        <property name="service" ref="userServiceImpl" />
        <property name="serviceInterface" value="com.abc.invoke.server.service.UserService" />
    </bean>
    <bean id="userServiceImpl" class="com.abc.invoke.server.service.impl.UserServiceImpl" />
</beans>

注意:

  1. <prop key="/userService">userServiceInvoker</prop>中的/userService是请求的服务的URL中的一部分,就是说这样的URL会被userServiceInvoker处理

  2. 这里将com.abc.invoke.server.service.UserService映射给了com.abc.invoke.server.service.impl.UserServiceImpl类了。

到此为止,服务器算是配置好了,接下来开始配置客户端。

配置客户端

先来看看项目结构图:

1). 添加 springJAR 文件,这也不用说了,直接照着图片添加相应的类库。

2). 创建服务接口和相应的DTO。

特别注意:这个类和Server端声明的DTO要一样,包名和字段名都要一样才行。因为客户端发起请求查询User,服务端处理后先将User序列化后在返回给客户端,而客户端拿到这个User后需要将其反序列化。如果包名或者字段名不同,则会被认为是不同的对象,会反序列化失败,调用也就出错了。我之前就是将User类的包名写得不一样(User类的包名在服务端为com.abc.invoke.server.bean,而在客户端则为com.abc.invoke.client.bean),报了以下错误:

1
2
3
4
5
6
7
8
9
Exception in thread "main" org.springframework.remoting.RemoteAccessException: 
    Could not deserialize result from HTTP invoker remote service [http://localhost:8080/InvokeServer/service/userService]; 
    nested exception is java.lang.ClassNotFoundException: com.abc.invoke.server.bean.User
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.convertHttpInvokerAccessException(HttpInvokerClientInterceptor.java:208)
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.invoke(HttpInvokerClientInterceptor.java:145)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at com.sun.proxy.$Proxy0.getUserbyName(Unknown Source)
    at com.abc.invoke.client.Test.main(Test.java:14)

很明显可以看出,Could not deserialize result from HTTP invoker remote service......,就是因为Server端与Client端的DTO的包名不同导致反序列化失败。

3). 创建服务接口

这也没啥好说的,接口和Server端定义的一样就行,不一样肯定报错。可以直接将DTO和接口定义的类拷贝到客户端即可。这个接口将会被看做是客户端和服务端通信的“契约”。

4). 访问服务

来看看application-context.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
     
    <!-- 客户端使用 HttpInvokerProxyFactoryBean 代理客户端向服务器端发送请求,请求接口为 UserService 的服务 -->
    <bean id="userService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean" >
        <property name="serviceUrl" value="http://localhost:8080/InvokeServer/service/userService"/>
        <property name="serviceInterface" value="com.abc.invoke.client.service.UserService" />
    </bean>
     
</beans>

这里使用了org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean这个类来作为一个service的代理类。注意到serviceUrl属性为http://localhost:8080/InvokeServer/service/userService (当然,我在本机启动的服务端并在本机通过main函数调用service,我在另一台机器上运行Test类的main函数,调用结果正确)。这个localhost:8080应改为实际的IP地址和端口。),这个URL的地址以/service开始,因此会被Server端拦截下来,而URL中的 /userService则为service路径,该路径与在Server端中service-servlet.xml中声明的

1
<prop key="/userService">userServiceInvoker</prop>

路径一致,因此这个调用会被userServiceInvoker处理。

最后再写一个简单的测试类Test.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.abc.invoke.client;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import com.abc.invoke.bean.User;
import com.abc.invoke.client.service.UserService;
 
public class Test {
   public static void main(String[] args) {
       ApplicationContext ac = new ClassPathXmlApplicationContext(
                       "classpath:application-context.xml");
       UserService service = (UserService)ac.getBean("userService");
       User u = service.getUserbyName("Alvis");
       System.out.println(u);
   }
}

这个类也很简单,就是从Spring的Context中取出了定义的userService这个Bean(这其实就是服务端service的一个代理类),然后直接调用该service的方法获得结果并打印。

到此为止,客户端配置完成。

四、启动服务并测试

直接在项目InvokeServer上启动Tomcat,可以看到路径/userService的处理者是userServiceInvoker:

下面是远程调用的执行结果:

从结果中可以看到,我代码里写的名字叫Alvis,用客户端调用服务端的service后,返回的对象中名字是客户端设置的名字,测试成功。

这里是项目源代码,供需要的朋友参考。

参考页面:http://hanqunfeng.iteye.com/blog/868210

Spring Http Invoker使用简介的更多相关文章

  1. Spring HTTP invoker简介

    Spring HTTP invoker简介 Spring HTTP invoker是spring框架中的一个远程调用模型,执行基于HTTP的远程调用(意味着可以通过防火墙),并使用java的序列化机制 ...

  2. 服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)

    我们到底能走多远系列(40) 扯淡:  判断是否加可以效力于这家公司,一个很好的判断是,接触下这公司工作几年的员工,了解下生活工作状态,这就是你几年后的状态,如果满意就可以考虑加入了. 主题: 场景: ...

  3. 第64节:Java中的Spring Boot 2.0简介笔记

    Java中的Spring Boot 2.0简介笔记 spring boot简介 依赖java8的运行环境 多模块项目 打包和运行 spring boot是由spring framework构建的,sp ...

  4. Spring Web MVC框架简介

    Web MVC framework框架 Spring Web MVC框架简介 Spring MVC的核心是`DispatcherServlet`,该类作用非常多,分发请求处理,配置处理器映射,处理视图 ...

  5. Spring IoC 和 DI 简介(二)

    Spring IoC 和 DI 简介 IoC:Inverse of Control(控制反转) 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由 ...

  6. Spring HTTP invoker 入门

    一.简介 Spring开发团队意识到RMI服务和基于HTTP的服务(如,Hessian)之间的空白.一方面,RMI使用JAVA标准的对象序列化机制,很难穿透防火墙.另一方面,Hessian/Burla ...

  7. Spring Security——核心类简介——获得登录用户的相关信息

    核心类简介 目录 1.1     Authentication 1.2     SecurityContextHolder 1.3     AuthenticationManager和Authenti ...

  8. Spring Cloud Config - RSA简介以及使用RSA加密配置文件

    简介 RSA非对称加密有着非常强大的安全性,HTTPS的SSL加密就是使用这种方法进行HTTPS请求加密传输的.因为RSA算法会涉及Private Key和Public Key分别用来加密和解密,所以 ...

  9. Spring系列__02IOC模块简介

    Spring的两大核心功能就是IOC和AOP,这篇文章主要介绍IOC. 简单来说,在面向对象思想下,A类中有一个B类的属性, 那么我们在创建A类时往往需要同时创建一个B类的对象,以便A类对其进行调用. ...

随机推荐

  1. idea【取消多行】

    有时间把idea总结一下 idea打开很多文件时默认收起来就很烦. 这样可以取消多行 效果大概是这样 .酥服哒.

  2. 2.4 CSS定位

    前言 大部分人在使用selenium定位元素时,用的是xpath定位,因为xpath基本能解决定位的需求.css定位往往被忽略掉了,其实css定位也有它的价值,css定位更快,语法更简洁.这一篇css ...

  3. Ubuntu上latex+atom配置

    网上流传的latex+atom大都是windows上的,Ubuntu与windows上的配置方式大同小异,这里写下自己的经验: 分为三个步骤,首先安装texlive,texlive是latex的依赖库 ...

  4. jupyter notebook远程服务器终端连接

    如下图

  5. 九度OJ1122题-吃巧克力

    题目1122:吃糖果 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2453 解决:1957 题目描述: 名名的妈妈从外地出差回来,带了一盒好吃又精美的巧克力给名名(盒内共有 N 块巧克力 ...

  6. 纯C:AES256

    尼玛的WordPress把格式全搞乱了 aes256.h #ifndef _AES256_H_ #define _AES256_H_ #include <stdio.h> #include ...

  7. 51Nod 1459:迷宫游戏(最短路)

    1459 迷宫游戏  基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 你来到一个迷宫前.该迷宫由若干个房间组成,每个房间都有一个得分,第一次进入这个房间, ...

  8. 相对和绝对路径 mkdir cd rm 等命令

     1. 绝对路径和相对路径    个人理解: 绝对路径-----即从根目录开始一直到你需要找的文件或目录的路径 (即任何情况下都以根目录为起点) 相对路径------即从当前目录开始一直找到你需要找的 ...

  9. hdu6441 Find Integer (费马大定理)

    #include<bits/stdc++.h> using namespace std; int main() { int T; scanf("%d",&T); ...

  10. Atom编辑神器

    最近喜欢上了Atom编辑神器,安装就不说了,重点讲配置. 一:软件配置 1.先将欢迎界面去掉,每次打开Atom的时候都会出现,实在是很烦人. 就在欢迎界面里面有个复选框,去掉选中就可以了. 2.让At ...