Spring HTTP Invoker远程代理详解及实例

一、概念

Spring HTTP Invoker一种JAVA远程方法调用框架实现,原理与JDK的RMI基本一致,所以我们先跟其它JAVA远程方法调用实现做下简单比较。

  • RMI:使用JRMP协议(基于TCP/IP),不允许穿透防火墙,使用JAVA序列化方式,使用于任何JAVA应用之间相互调用。

  • Hessian:使用HTTP协议,允许穿透防火墙,使用自己的序列化方式,支持JAVA、C++、.Net等跨语言使用。

  • Burlap: 与Hessian相同,只是Hessian使用二进制传输,而Burlap使用XML格式传输(两个产品均属于caucho公司的开源产品)。

  • Spring HTTP Invoker: 使用HTTP协议,允许穿透防火墙,使用JAVA序列化方式,但仅限于Spring应用之间使用,即调用者与被调用者都必须是使用Spring框架的应用。

既然是通过HTTP请求调用,那么客户端肯定需要一个代理用于帮忙发送HTTP请求,帮忙做对象系列化和反系列化等,Spring框架中的HttpInvokerProxyFactoryBean类处理这些杂事;

而服务器端需要一个HTTP请求处理器,帮忙处理HTTP请求已经对象系列化和反系列化工作,Spring框架中的HttpInvokerServiceExporter类就是干这活的,对于Sun JRE 6 的HTTP Server,Spring还提供了SimpleHttpInvokerServiceExporter类供选择。

二、服务端(服务提供者)

项目都是创建spring mvc项目
具体spring mvc创建流程
可浏览博主之前发布的文章
spring MVC项目的搭建

2.1 创建对外暴露的服务接口及对象

对外暴露的服务接口及对象都是需要打包成jar给客户端调用
所以一般来说,这种接口跟对象都是单独创建成一个项目
服务端跟客户端都直接导入jar进行使用

如果不想打包成jar使用
则客户端及服务端的接口及对象的包名、类名、需实现序列化都一样

person对象类

package com.service;

import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = 6139752933642464599L;
    private String name;
    private String age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
}

IPersonService服务接口类

package com.service;

public interface IPersonService {
    Person findByName(String name);
}

2.2 服务实现类

PersonService实现接口,相关业务处理

这里不进行具体的数据库操作
只返回模拟数据

package com.service;

public class PersonService implements IPersonService {
    @Override
    public Person findByName(String name) {
        Person person = new Person();
        person.setAge("18");
        person.setName(name);
        return person;
    }
}

2.3 服务代理设置

服务端需要实例化HttpInvokerServiceExporter服务注册的一个类;
并且需要设置两个属性

service:服务实现类,也就是接口的实现类,一般都是spring的bean;
serviceInterface:服务类型,也就是服务实现类的一个接口类;

代码如下:

package com.service.util;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import com.service.IPersonService;

public class RemotingService extends HttpServlet {

    private static final long serialVersionUID = -1017031460828010508L;
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//实例化代理服务
        HttpInvokerServiceExporterserviceExporter = new HttpInvokerServiceExporter();
      	//获取服务的实现bean
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        Object service = wac.getBean("personService");
      	//设置属性
        serviceExporter.setService(service);
        serviceExporter.setServiceInterface(IPersonService.class);
        serviceExporter.afterPropertiesSet();
		
        serviceExporter.handleRequest(req, resp);
    }
}

web.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>com.service.util.remoting</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/remote/*</url-pattern>
    </servlet-mapping>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/springContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

springContext.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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 id="personService" class="com.service.PersonService" />  
</beans>

项目结构:
Spring HTTP Invoker远程代理详解及实例

三、客户端(服务消费者)

3.1 导入服务端的jar

<dependency>
	<groupId>com.xiao</groupId>
	<artifactId>springMVC</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>

这里我服务端名称是springMVC
服务端跟客户端项目都是Spring MVC项目,所以都直接使用maven管理jar包
使用下列命令可导入jar进maven仓库

mvn install:install-file -Dfile=jar绝对路径全称 -DgroupId=项目包名 -DartifactId=项目名称 -Dversion=版本号 -Dpackaging=jar

3.2 客户端配置

因为使用的是spring自带的远程代理,需要项目是spring项目
所以客户端直接就使用默认的一个MVC配置

web.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>WEB-INF/springContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

springContext.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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">
    
    <!-- 搜索spring -->
    <context:component-scan base-package="com.xiao"></context:component-scan>
    <!-- 视图页面配置 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
</beans>

3.3 远程代理测试

客户端也需要实现一个代理工厂类HttpInvokerProxyFactoryBean实现代理。
也有两个属性需要设置

serviceInterface:需要消费的服务接口类

serviceUrl:服务提供者的url

这里url可自定义,可在路径后加上调用的接口名
服务端也就能根据接口名实现不同的代理

测试类代码:

package com.xiao.controller;

import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
import org.springframework.stereotype.Controller;
import com.service.IPersonService;
import com.service.Person;

public class controller {

    public static void main(String[] args) {
        String name = "小小";
        Person person = getService(IPersonService.class).findByName(name);
        assert person.getName().equals(name) : name + "与代理返回数据name不匹配";
        System.out.println(person.getName());
    }

    public static <T> T getService(Class<T> clazz) {
        HttpInvokerProxyFactoryBean bean = new HttpInvokerProxyFactoryBean();

        bean.setServiceInterface(clazz);
        String url = "http://192.168.0.161:8081/springMVC/remote";
        bean.setServiceUrl(url);
        bean.afterPropertiesSet();
        Object result = bean.getObject();

        return (T) result;
    }
}

运行后控制台可以输出person的name
测试通过

客户端项目结构
Spring HTTP Invoker远程代理详解及实例

结语

对您有帮助请帮忙点个赞
有疑问可留言评论,博主看到会进行回复
相互学习

上一篇:[LOJ#530]「LibreOJ β Round #5」最小倍数


下一篇:Java设计模式——命令模式