介绍分为两个部分,第一部分介绍HttpInvoker相关配置以及实现原理,第二部分介绍系统架构内部的使用,主要是为了实现远程模块无法使用hibernate懒加载拖拽功能的解决方案
HttpInvoker是Spring提供的一种远程对象调用的机制,主要依赖于spring内置的Bean,分别包括一下几个类
前者是HttpInvoker客户端的代理工厂类,后者是HttpInvoker服务端的类似控制器的角色的类
两个类都需要绑定制定的接口,通过接口的一致来保证客户端对服务端的调用能够正常进行
服务端配置
<bean name="userService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="userManager"/>
<property name="serviceInterface" value="com.efeiyi.ec.website.organization.service.UserManager"/>
bean>
服务端web.xml中配置
<servlet>
<servlet-name>userServiceservlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>userServiceservlet-name>
<url-pattern>/service/userServiceurl-pattern>
servlet-mapping>
客户端配置
<bean id="userServiceProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="http://192.168.227.130:8080/service/userService"/>
<property name="serviceInterface" value="com.efeiyi.ec.website.organization.service.UserManager"/>
bean>
在服务端我们直接配置一个servlet指向到HttpRequestHandlerServlet,servlet的名字(servlet-name)要与HttpInvokerServiceExporter的beanName相同,在客户端配置代理的时候需要将远程服务的地址指定到服务端配置的Servlet上(http://hostName:port/service/userService)。
在客户端使用远程服务的时候需要先获取代理工厂类(从spring容器中)将之转型为指定接口的类型,这样就可以像使用本地Service一样使用远程Service了。
...
UserManager userManager = (UserManager) ContextUtils.getBean("userServiceProxy");
User myUser = userManager.getUserByUserId(AuthorizationUtil.getMyUser().getId());
...
model类之间会有一些相互依赖的关系,例如 订单(PurchaseOrder) 会和用户(User)关联,之前使用hibernate的懒加载,再需要用到用户信息的时候我们可以直接通过hibernate的session将关联的user拖拽出来,如今由于多个产品需要使用同一套用户体系,所以需要将原有的用户模块从电商当中分离开来,随着模块的分离,数据库也要分离,这样其实是开始了分布式服务的系统架构。由于用户模块与电商系统处于不同的数据库,那么hibernate的懒加载拖拽功能就无法再使用,为了对现有代码的支持,造了一套中间件来代替hibernate来实现拖拽功能。
以电商系统为例:
所有的数据都是通过BaseManager.getObject()或者BaseManager.listObject(),所以中间件就是加载BaseManager这一层。
@Override
public Object getObject(String model, String id) {
Object object = xdoDao.getObject(model, id);
try {
WebServiceHandlerManagerImpl.dealObject(object);
} catch (Exception e) {
e.printStackTrace();
}
return object;
}
对数据进行处理是通过得到的数据类型来判断,如何判断? 需要我们事先配置好哪些类型需要用到远程服务
在/system/webService.xml中配置如下:
<web-services>
<service name="com.efeiyi.ec.purchase.model.PurchaseOrder" handler="user:com.efeiyi.ec.website.base.model.UserServiceHandler"/>
web-services>
以上这段配置文件的字面意思就是:
类型为com.efeiyi.ec.purchase.model.PurchaseOrder的model类中的user属性需要用到com.efeiyi.ec.website.base.model.UserServiceHandler的远程服务的Handler。
UserServiceHandler实现了WebServiceHandler接口,实现如下:
public class UserServiceHandler implements WebServiceHandler {
@Override
public void setValue(Object object, String propertyName) {
try {
UserManager userManager = (UserManager) ContextUtils.getBean("userServiceProxy");
Object objTemp = ReflectUtil.invokeGetterMethod(object, propertyName);
String id = ReflectUtil.invokeGetterMethod(objTemp, "id").toString();
User user = userManager.getUserByUserId(id);
ReflectUtil.invokeSetterMethod(object, propertyName, user, User.class);
} catch (Exception e) {
e.printStackTrace();
}
}
}
每个远程服务在相应的项目当中都需要实现对应的Handler才可以。
以下是该系统简单的类图: