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框架的应用。HttpInvoke实现远程访问。Spring httpInvoker使用标准java序列化机制,通过Http暴露业务服务。客户端可以很轻松的像调用本地对象一样调用远程服务器上的对象。如果你的参数和返回值比较复杂的,通过httpInvoker有巨大的优势。
注意事项:由于是通过网络传输,所以服务端、客户端的POJO类,都要实现Serializable接口,进行序列化、反序列化。
服务端环境:Tomcat服务, Spring环境
因为服务端需要提供HTTP请求服务,而且是基于Servlet的,所以服务端需要跑在如Tomcat这样的Servlet Web容器上;
服务类只要是普通的POJO即可,没有特殊要求。
客户端环境:Spring环境
一 远程访问流程
1. 服务端定义服务接口
2. 服务端实现服务接口
3. 服务端暴露服务对象
4. 客户端定义暴露的服务端接口
5. 客户端配置服务参数
6. 测试客户端调用服务接口
Spring HTTP Invoker有两种实现方式
1.基于Url映射方式,远程系统处理请求的方式同SpringMVC的controller类似,所有的请求通过在web.xml中的 org.springframework.web.servlet.DispatcherServlet统一处理,根据url映射,去对应的 【servlet名称-servlet.xml】文件中,查询跟请求的url匹配的bean配置
2.基于Servlet方式,由org.springframework.web.context.support.HttpRequestHandlerServlet去拦截url- pattern匹配的请求,如果匹配成功,去ApplicationContext中查找name与servlet-name一致的bean,完成远程方法调用。
下面我们使用第一种方式实现测试。
二 Spring HttpInvoker具体实现
1. 服务端定义服务接口
接口1:获取当前学生对象的List集合
import java.util.List;
import com.entity.Student;
public interface StudentService {
public List<Student> getStudent();
}
接口2:根据名字获取用户
import com.entity.User;
public interface UserService {
public User getUserbyName(String name);
}
2. 服务端实现服务接口
实现上述接口1
import java.util.ArrayList;
import java.util.List;
import com.entity.Student;
import com.entity.User;
import com.service.StudentService;
public class StudentServiceImpl implements StudentService{
@Override
public List<Student> getStudent() {
List<Student> slist = new ArrayList<>();
Student student1 = new Student();
Student student2 = new Student();
User user1 = new User("zhangsan", 24, "perfectwwh@163.com");
User user2 = new User("lisi", 25, "perfectwwh@163.com");
student1.setUser(user1);
student2.setUser(user2);
slist.add(student1);
slist.add(student2);
return slist;
}
}
实现上述接口2
import com.entity.User;
import com.service.UserService;
public class UserServiceImpl implements UserService{
@Override
public User getUserbyName(String name) {
User user = new User();
user.setName(name);
user.setAge(26);
user.setEmail("perfectwwh@163.com");
return user;
}
}
3. 服务端暴露服务对象
服务端的XML配置文件
<?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>
<!-- 这个Bean映射了当URL是/studentService时,处理器为studentServiceInvoker -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/studentService">studentServiceInvoker</prop>
</props>
</property>
</bean>
<!-- Announce that this interface is a HTTP invoker service. -->
<!-- 服务端暴露UserService服务接口 -->
<bean id="userServiceInvoker" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="userServiceImpl" />
<property name="serviceInterface" value="com.service.UserService" />
</bean>
<bean id="userServiceImpl" class="com.service.impl.UserServiceImpl" />
<!-- 服务端暴露UserService服务接口 -->
<bean id="studentServiceInvoker" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="studentServiceImpl" />
<property name="serviceInterface" value="com.service.StudentService" />
</bean>
<bean id="studentServiceImpl" class="com.service.impl.StudentServiceImpl" />
</beans>
4. 客户端定义暴露的服务端接口(和服务端接口一模一样,保持包结构一致)
接口1:获取当前学生对象的List集合
import java.util.List;
import com.entity.Student;
public interface StudentService {
public List<Student> getStudent();
}
接口2:根据名字获取用户
import com.entity.User;
public interface UserService {
public User getUserbyName(String name);
}
5. 客户端配置服务参数(名称为:client-application-context.xml)
<?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 的服务,
ip地址为服务器地址 -->
<bean id="userService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean" >
<property name="serviceUrl" value="http://192.168.1.96:8080/SpringHttpInvokerDemo/service/userService"/>
<property name="serviceInterface" value="com.service.UserService" />
</bean>
<!-- 客户端使用 HttpInvokerProxyFactoryBean 代理客户端向服务器端发送请求,请求接口为StudentService的服务,
ip地址为服务器地址 -->
<bean id="studentService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean" >
<property name="serviceUrl" value="http://192.168.1.96:8080/SpringHttpInvokerDemo/service/studentService"/>
<property name="serviceInterface" value="com.service.StudentService" />
</bean>
</beans>
6. 客户端调用服务接口
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.entity.Student;
import com.entity.User;
import com.service.StudentService;
import com.service.UserService;
public class Test {
public static void main(String[] args) {
ApplicationContext ac =
new ClassPathXmlApplicationContext("classpath:client-application-context.xml");
UserService service = (UserService)ac.getBean("userService");
User u = service.getUserbyName("wwh");
System.out.println(u);
StudentService studentService = ac.getBean("studentService", StudentService.class);
List<Student> slist = studentService.getStudent();
for(Student s: slist) {
System.out.println(s);
}
}
}
7.客户端输出结果
POJO
注意事项:由于是通过网络传输,所以服务端、客户端的POJO类,都要实现Serializable接口,进行序列化、反序列化。
import java.io.Serializable;
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Student [user=" + user + "]";
}
}
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 User() {}
public User(String name, int age, String email) {
super();
this.name = name;
this.age = age;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
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 + "]";
}
}
#####配置web.xml
<?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/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>SpringHttpInvokerDemo</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>
依赖Spring
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>SpringHttpInvokerDemo</groupId>
<artifactId>SpringHttpInvokerDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringHttpInvokerDemo</name>
<description/>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<version>3.0</version>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>