hessian集成spring注解实现

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对访问做了限制的。

Ø  在发布的时候如果先发客户端,客户端会报找不到服务的错误,但是只要发完服务端后调用就会正常。

关注微信公众号和今日头条,精彩文章持续更新中。。。。。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值