1、Hessian简单介绍:
Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。可跨语言使用。官网地址:http://hessian.caucho.com/
Hessian包下载:
从图上可以看出hessian很久没更新了,但实际上很多公司使用的框架基础却都是基于hessian改造来的。比如dubbo及其扩展框架。
2、Hessian集成Spring注解改造:
Hessian集成Spring,采用配置文件的例子网上很多,可以百度。本文要介绍的是Hessian采用注解的配置。
2.1、Hessian服务端改造思路:
两种方式,一种是在接口上加注解,一种是在服务端的实现类上添加注解,我们采用第二种方式,在实现类上添加注解,两者的实现思路基本上一样。
Ø 实现HttpServlet接口,实现分发访问HessianServlet功能;
Ø 采用ServletContext的createServlet对外暴露分发Hessian的Servlet;
Ø 实现spring的InitializingBean接口在Bean加载完后扫描添加了注解的Bean,将Bean和实现的接口注入HessianServiceExporter,生成新的Bean;
Ø 将生成的Bean放入Map,以便分发Hessian的Servlet使用;
2.2、代码实现:
新建注解类HessianService
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface HessianService {
//hessian服务名称
public String name() default "";
}
创建HttpHessianServlet,暴露Hessian,分发访问Hessian的Servlet
package com.mining.stock.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.HttpRequestHandler;
public class HttpHessianServlet extends HttpServlet{
private Map<String, HttpRequestHandler> requestHandlerMap = new HashMap<String, HttpRequestHandler>();
private static Logger logger = LoggerFactory.getLogger(HttpHessianServlet.class);
private static final long serialVersionUID = -2409880638944395829L;
public void put(String key,HttpRequestHandler value){
requestHandlerMap.put(key, value);
}
public void init(ServletConfig config)throws ServletException{
logger.info("创建 hessian 服务对外的servlet");
super.init(config);
super.destroy();
}
public void setRequestHandlerMap(Map<String, HttpRequestHandler> requestHandlerMap) {
this.requestHandlerMap = requestHandlerMap;
}
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{
logger.info("接收 hessian 服务对外的servlet");
HttpRequestHandler httpRequestHandler = requestHandlerMap.get(req.getPathInfo());
if(httpRequestHandler==null){
logger.info("访问的hessian对象不存在pathInfo:"+req.getPathInfo());
((ServletRequest) resp).getRequestDispatcher("404.html").forward(req, resp);
return;
}
httpRequestHandler.handleRequest(req, resp);
}
}
集成Spring,实现InitializingBean和ApplicationContextAware接口,扫描注解Bean,并创建新的Bean,具体代码参看hessian-common的HessianBeanExploerConfig类。
package com.mining.stock.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.remoting.caucho.HessianServiceExporter;
import org.springframework.util.ClassUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.WebApplicationContext;
public class HessianBeanExploerConfig implements InitializingBean,ApplicationContextAware{
private static final Log log = LogFactory.getLog(HessianBeanExploerConfig.class);
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
resourcePatternResolver);
private String basePackage ;
ApplicationContext applicationcontext;
public void afterPropertiesSet() throws Exception {
//手动注册servlet
ServletContext sc = ((WebApplicationContext)applicationcontext).getServletContext();
HttpHessianServlet httpHessianServlet = sc.createServlet(HttpHessianServlet.class);
Map<String, HttpRequestHandler> requestHandlerMap = new HashMap<String, HttpRequestHandler>();
httpHessianServlet.setRequestHandlerMap(requestHandlerMap);
ServletRegistration sr = sc.addServlet("httpHessianServlet", httpHessianServlet);
//sr.setInitParameter("servletInitName", "servletInitValue");
sr.addMapping("/hessian/*");
//查找所有添加了HessianService的注解
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) this.applicationcontext.getAutowireCapableBeanFactory();
// 扫描获取bean定义,这里得到的是包含 @hessianService 注解的service接口 的bean定义
Set<BeanDefinition> candidateComponents = findCandidateComponents(this.basePackage);
if(candidateComponents != null && candidateComponents.size() > 0){
for (BeanDefinition beanDefinition : candidateComponents){
try {
//获取到注解的Bean
GenericBeanDefinition definition = (GenericBeanDefinition) beanDefinition;
Class<?> serviceClazz = Class.forName(definition.getBeanClassName());
Object bean = null;
HessianService annotation = (HessianService) serviceClazz.getAnnotation(HessianService.class);
try {
bean = beanFactory.getBean(serviceClazz);
} catch (Exception e) {
log.info("处理的hessian service "+serviceClazz.getName()+" 在本地没有定义,未找到实例化bean!");
}
if (bean != null) {
String serviceName = annotation.name();
if (StringUtils.isBlank(serviceName)) {
serviceName = serviceClazz.getSimpleName();
serviceName = serviceName.substring(0, 1).toLowerCase() + serviceName.substring(1);
}
if (!beanFactory.containsBean(serviceName)){
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(HessianServiceExporter.class);
AbstractBeanDefinition beanProxyDefinition = beanDefinitionBuilder.getBeanDefinition();
// 设置value就相当于在xml中设置 <property name="service"// ref="beanServiceName" />
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("service", bean);
propertyValues.addPropertyValue("serviceInterface", bean.getClass().getInterfaces()[0]);
beanProxyDefinition.setPropertyValues(propertyValues);
// 这一步是很关键的,如果beanFactory不是注册bean的 class 而是注册
// bean的定义definition
beanFactory.registerBeanDefinition("/" + serviceName, beanProxyDefinition);
} else {
//可以通过serviceName可以找到Bean,则判断是否加是HessianServiceExporter,没有则注册
Object serviceBean = beanFactory.getBean(serviceName);
if(serviceBean.getClass().getName().indexOf("HessianServiceExporter") < 0){
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(HessianServiceExporter.class);
AbstractBeanDefinition beanProxyDefinition = beanDefinitionBuilder.getBeanDefinition();
// 设置value就相当于在xml中设置 <property name="service"// ref="beanServiceName" />
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("service", bean);
propertyValues.addPropertyValue("serviceInterface", bean.getClass().getInterfaces()[0]);
beanProxyDefinition.setPropertyValues(propertyValues);
// 这一步是很关键的,如果beanFactory不是注册bean的 class 而是注册
// bean的定义definition
beanFactory.registerBeanDefinition("/" + serviceName, beanProxyDefinition);
}else{
log.debug("beanName:" + serviceName + "对应的bean已经存在!");
}
}
log.info("创建hessian server bean:" + serviceName);
try {
// 这里就实例化生成了bean,如果不调用这个方法,那么如果bean在其他地方使用了,同样也是会被实例化的
HessianServiceExporter hessianServiceExporter = beanFactory.getBean("/" + serviceName,HessianServiceExporter.class);
requestHandlerMap.put("/" + serviceName, hessianServiceExporter);
} catch (Exception e) {
log.info("创建hessian server bean:" + "/" + serviceName + " 时发生异常!");
e.printStackTrace();
}
}
}catch(Exception e){
log.error(e.getMessage());
}
}
}
}
private String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(this.applicationcontext.getEnvironment().resolveRequiredPlaceholders(basePackage));
}
//查找添加了HessianService注解的类
private Set<BeanDefinition> findCandidateComponents(String basePackage){
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
String packageSearchPath = "classpath*:" + resolveBasePackage(basePackage) + "/" + "**/*.class";
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (Resource resource : resources) {
if (resource.isReadable())
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
GenericBeanDefinition definition = sbd;
Class<?> entityClazz = Class.forName(definition.getBeanClassName());
HessianService annotation = (HessianService)entityClazz.getAnnotation(HessianService.class);
if (annotation != null)
candidates.add(sbd);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
}
}
}
catch (IOException ex)
{
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
public void setApplicationContext(ApplicationContext applicationcontext)
throws BeansException {
this.applicationcontext = applicationcontext;
}
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
}
客户端调用方基本上不用怎么改动,直接使用Spring的@Configuration注解生成Bean的类,用@Bean注解创建Hessian链接的方法即可:
@Configuration
public class HessianClientConfig {
@Bean
public static HessianInterface getHessian() throws Exception{
HessianProxyFactory hessianProxy = new HessianProxyFactory();
//是否支持重载方法
hessianProxy.setOverloadEnabled(true);
HessianInterface hessianService = (HessianInterface)hessianProxy.create(Class.forName("com.mining.sotck.busi.hessian.HessianInterface"),
"http://localhost:8080/hessianserver/hessian/hessianServlet");
return hessianService;
}
}
3、Hessian集成Spring注解方法的使用:
改造完成后开始Hessian的使用。Hessian面相接口编程,包括服务端和调用端,但是在开发和应用过程中又自然的生成一个api应用,方便服务端和客户端的维护。加上改造工程最终hessian影响的会有4个应用hessian-server,hessian-client,hessian-api,hessian-common,如下图:
3.1、Hessian-server配置:
Pom.xml中引入hessian,hessian-api,hessian-common包,以及spring相关的包,要注意Servlet-api包的引用,要3.0.1版本及其以上,2.0.5版本的不支持代码注册Servlet的一些方法。
Spring中添加配置,加载注解Bean。basePackage的值为实现服务接口,对外提供服务的Bean所在包,可以直接写根目录。
接口的实现类添加注解,正常的Bean注解还要添加,HessianService注解可以添加name,如果添加了name,那么客户端调用的时候就访问name所指定的值,如果没有使用name,那么访问的Servlet名称就是类名称首字母小写。
至此服务端配置完成,剩下的就是业务代码的开发。
3.2、hessian-client的配置:
同hessian-server一样,首先需要引入相关的jar包,但是如果不提供服务的话不需要引入hessian-common包。
调用hessian可以在业务代码中创建调用,也可以提供公共方法调用,推荐公共方法调用。
服务方的URL地址可以写入配置文件。
@Configuration
public class HessianClientConfig {
@Bean
public static HessianInterface getHessian() throws Exception{
HessianProxyFactory hessianProxy = new HessianProxyFactory();
//是否支持重载方法
hessianProxy.setOverloadEnabled(true);
HessianInterface hessianService = (HessianInterface)hessianProxy.create(Class.forName("com.mining.sotck.busi.hessian.HessianInterface"),
"http://localhost:8080/hessianserver/hessian/hessianServlet");
return hessianService;
}
}
至此服务端和客户端创建完毕,进行发布调用测试。
注意点:
Ø 默认情况下Hessian是不支持方法的重载,这个时候我们可以在客户端使用的时候新增属性overloadEnabled,值为true。
Ø 浏览器可以直接访问服务端的Hessian服务,但是会提示错误,只支持Post,这是Spring对访问做了限制的。
Ø 在发布的时候如果先发客户端,客户端会报找不到服务的错误,但是只要发完服务端后调用就会正常。
关注微信公众号和今日头条,精彩文章持续更新中。。。。。