本文中的使用Hessian和Burlap发布远程服务以及使用Spring的HTTPInvoker发不远程服务测试失败。最常用的应该是使用JAX-WS,也就是WebService。
调用远程服务使用的比较多的应该是WebService吧。但是远程调用技术并不仅仅只有WebService。还包括:
1、远程调用是客户端和服务端的会话。有时在客户端所需要的一些功能并不在客户端是实现范围之内,此时需要寻求其他系统的帮助,而远程应用就需要通过远程服务暴露这些功能接口。
2、客户端和服务端的会话开始于客户端的一个远程过程调用(remote procedure call,RPC)。在客户端调用时,从表面上看就像调用本地方法一样。
3、RPC调用就是执行流从一个应用传递到另一个应用,理论上另一个应用部署在跨网络的一台远程机器上。
RPC模型 | 适用场景 |
远程方法调用(RMI) | 不考虑网络限制时(例如防火墙),访问/发布基于java的服务 |
Hessian和Burlap | 考虑网络限制时,通过HTTP访问/发布基于Java的服务,Hessian是二进制协议,Burlap是基于XML的 |
HTTP invoker | 考虑网络限制,并希望使用基于XML或专有的序列化机制实现Java序列化时,访问/发布基于Spring的服务 |
JAX-RPC和JAX-WS | 访问/发布平台独立的、基于SOAP的Web服务 |
在Spring中使用RMI很简单,注入RmiServiceExporter即可,RmiServiceExporter可以把任意Spring管理的bean发布为RMI服务。
服务端发布服务(使用XML配置,使用Java配置这里就不做记录了,照着XML就可以实现):
服务端要发布的服务接口:
public interface TuserDao {
@CachePut(value = "tUserCache", key = "#result.userId")
public Tuser add(Tuser tuser);
public void delete(Tuser tuser);
public Tuser update(Tuser tuser);
/**
* 查询User,并将查询结果放进tUserCache缓存中
* @param tuser
* @return
*/
@Cacheable("tUserCache")
public List find(Tuser tuser);
/**
* 根据ID查询用户,并将查询结果放进tUserCache缓存中
* @param userId
* @return
*/
@Cacheable(value = "tUserCache", unless = "#result.message.contains('NoCache')", condition = "#userId == '10'")
public Tuser findByPrimary(String userId);
}
发布服务接口的实现类:
package com.mfc.dao;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.mfc.entity.Tuser;
@Repository(value = "tuserDao")
public class TuserDaoImpl implements TuserDao {
@Autowired
private SessionFactory sessionFactory;
private Session getSession(){
return sessionFactory.getCurrentSession();
}
@Override
public Tuser add(Tuser tuser) {
getSession().save(tuser);
return tuser;
}
@Override
public void delete(Tuser tuser) {
getSession().delete(tuser);
}
@Override
public Tuser update(Tuser tuser) {
getSession().update(tuser);
return tuser;
}
@Override
public List find(Tuser tuser) {
return getSession().createCriteria(Tuser.class).list();
}
@Override
public Tuser findByPrimary(String userId) {
return (Tuser) getSession().get(Tuser.class, userId);
}
}
客户端装配RMI服务(使用XML配置,使用Java配置这里就不做记录了,照着XML就可以实现):
客户端也需要服务的接口:
public interface TuserDao {
@CachePut(value = "tUserCache", key = "#result.userId")
public Tuser add(Tuser tuser);
public void delete(Tuser tuser);
public Tuser update(Tuser tuser);
/**
* 查询User,并将查询结果放进tUserCache缓存中
* @param tuser
* @return
*/
@Cacheable("tUserCache")
public List find(Tuser tuser);
/**
* 根据ID查询用户,并将查询结果放进tUserCache缓存中
* @param userId
* @return
*/
@Cacheable(value = "tUserCache", unless = "#result.message.contains('NoCache')", condition = "#userId == '10'")
public Tuser findByPrimary(String userId);
}
客户端的Controller中调用服务:
@Controller
@RequestMapping("testServiceCtrl")
public class TestServiceCtrl {
@Autowired
private TuserDao tuserDao;
@RequestMapping("findAll")
public String findAll(Model model){
List list = tuserDao.find(null);
model.addAttribute("userList", list);
return "userList";
}
}
完成以上配置,就已经实现了一个简单的远程服务调用。但是RMI存在某些限制:
①RMI很难穿透防火墙,因为RMI可以使用任意端口来交互,这是防火墙不允许的。所以在企业内部应用中可以使用RMI,但是在互联网应用中不要使用。
②RMI是基于Java的,这意味着客户端和服务端都必须是用Java开发的。
Hessian和Burlap的区别:Hessian基于二进制协议,Burlap基于XML协议。
关于使用远程的service或者dao时还是和RMI一样,看起来像本地服务一样使用。
遗留问题:服务端使用hibernate时,从客户端访问服务端的服务,总是在hibernate的dao层获取Session时报空指针。此问题未解决,还望有大佬可以解释一下这是为什么。
Hessian发布远程服务:
服务端要发布服务的接口和实现类同使用RMI一样,不一样的配置有:
web.xml:
hessian
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-service.xml
2
hessian
/api/service/*
spring-service.xml:
客户端spring-service.xml:
Burlap发布远程服务:
关于web.xml的配置和Hessian是一样的,只是在服务端的spring-service.xml中将HessianServiceExporter改为了BurlapServiceExporter,客户端的spring-service.xml中将HessianProxyFactoryBean改为BurlapProxyFactoryBean。
服务端spring-service.xml:
客户端spring-service.xml:
因为Hessian和Burlap都是基于Http的,都解决了RMI所头疼的防火墙渗透问题。但是当传递过来的RPC消息中包含序列化对象时,RMI就完胜Hessian和Burlap了。因为Hessian和Burlap采用了私有的序列化机制,而RMI使用的是Java自身的序列化机制。如果数据模型给长复杂,Hessian和Burlap就不能胜任了。所以就有了Spring的HttpInvoker,使用了java自身的序列化机制,并基于HTTP进行远程调用。
Spring的HttpInvoker跟Hessian和Burlap是一样使用的,只不过将服务端的HessianServiceExporter和BurlapServiceExporter换成了HttpInvokerServiceExporter;客户端的HessianProxyFactoryBean和BurlapProxyFactoryBean换成了HttpInvokerProxyFactoryBean。
注意,关于web.xml还是要和Hessian和Burlap的一样配置。
服务端配置:
客户端配置:
近几年比较流行的一个TLA(三个字母缩写)就是SOA(面向服务的架构)。SOA的核心理念是应用程序可以并且应该被设计成依赖于一组公共的核心服务,而不是为每个应用都重新实现相同的功能。Spring为了使用Java API for XML Web Service(JAX-WS)来发布和使用SOAP Web服务提供了支持。
1、在Spring中自动装配JAX-WS端点
使用@WebService注解所标注的类被认为是Web服务的端点,使用@WebMethod注解所标注的方法被认为是操作。
装配方式一:
继承SpringBeanAutowiringSupport。
package com.mfc.service;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import com.mfc.dao.TuserDao;
import com.mfc.entity.Tuser;
@Service
@WebService(serviceName = "tuserService")
public class TuserService extends SpringBeanAutowiringSupport {
@Autowired
private TuserDao tuserDao;
@WebMethod
public List find(){
return tuserDao.find(null);
}
@WebMethod
public Tuser add(Tuser tuser){
return tuserDao.add(tuser);
}
@WebMethod
public void delete(Tuser tuser){
tuserDao.delete(tuser);
}
@WebMethod
public Tuser update(Tuser tuser){
return tuserDao.update(tuser);
}
@WebMethod
public Tuser findByPrimary(String userId){
return tuserDao.findByPrimary(userId);
}
}
装配方式二:
将方式一种的TuserService的@Service换成@Component并且不再继承SpringBeanAutowiringSupport。其他配置不变。
package com.mfc.service;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.mfc.dao.TuserDao;
import com.mfc.entity.Tuser;
@Component
@WebService(serviceName = "tuserService")
public class TuserService {
@Autowired
private TuserDao tuserDao;
@WebMethod
public List find(){
return tuserDao.find(null);
}
@WebMethod
public Tuser add(Tuser tuser){
return tuserDao.add(tuser);
}
@WebMethod
public void delete(Tuser tuser){
tuserDao.delete(tuser);
}
@WebMethod
public Tuser update(Tuser tuser){
return tuserDao.update(tuser);
}
@WebMethod
public Tuser findByPrimary(String userId){
return tuserDao.findByPrimary(userId);
}
}
2、在客户端代理JAX-WS服务
服务端配置好之后,可以访问试试,如上面所配置的,可以访问路径http://localhost:8082/services/tuserService?wsdl试试。其中http://localhost:8082/services/是注入到SimpleJaxWsServiceExporter中的baseAddress属性值。后面的tuserService是@WebService注解的serviceName。
在客户端代理的方式:
配置:
客户端的Service接口:
package com.mfc.service;
import java.util.List;
import javax.jws.WebService;
import com.mfc.entity.Tuser;
@WebService(name = "tuserService")
public interface TuserService {
public Tuser add(Tuser tuser);
public void delete(Tuser tuser);
public Tuser update(Tuser tuser);
public List find(Tuser tuser);
public Tuser findByPrimary(String userId);
}
注意:客户端的Service接口和服务端的Service包名要一致。方法名也要一致。