当前位置: 首页 > 工具软件 > Hessian > 使用案例 >

Spring-boot集成Hessian服务

钮边浩
2023-12-01

hessian 协议

Hessian 是 Caucho 开源的一个 RPC 框架,其通讯效率高于 WebService 和 Java 自带的序列化。
Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。
Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:

  • 提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用
  • 或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。

特性

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:Hessian二进制序列化
  • 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。
  • 适用场景:页面传输,文件传输,或与原生hessian服务互操作

约束

  • 参数及返回值需实现 Serializable 接口
  • 参数及返回值不能自定义实现 List, Map, Number, Date, Calendar 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。

Spring集成Hessian

Hessian分服务端和客户端,都要导入依赖

<!-- 集成hessian-->
<dependency>
	<groupId>com.caucho</groupId>
	<artifactId>hessian</artifactId>
	<version>4.0.38</version>
</dependency>

服务端

声明服务

接口服务类HelloHessianService.java

public interface HelloHessianService {
    String hello(String name);
}

接口服务实现类HelloHessianServiceImpl.java

@Service
public class HelloHessianServiceImpl implements HelloHessianService {
    @Override
    public String hello(String name) {
        return "Hello Hessian " + name;
    }
}

发布服务

发布服务的方法有很多,这里只介绍主流的三种

方法一(手动单独发布)

此方法存在弊端:当服务量很多时,需要写大量这样的代码块

@Configuration
public class HessianConfig {
    @Autowired
    private HelloHessianService helloHessianService;

    //发布服务
    @Bean(name = "/helloHessianService")
    public HessianServiceExporter hessianServer() {
        HessianServiceExporter exporter = new HessianServiceExporter();
        exporter.setService(helloHessianService);
        exporter.setServiceInterface(HelloHessianService.class);
        return exporter;
    }
}
方法二(配置文件发布)

此方法还没来得及测试,不过应该是可行的

首先,编写hessian-server.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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="helloHessianService" class="com.hessian.service.impl.HelloHessianServiceImpl" />
    <bean name="/helloHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter">
        <property name="service" ref="helloHessianService" />
        <property name="serviceInterface" value="com.hessian.service.HelloHessianService " />
    </bean>
</beans>

Spring-boot启动类加载配置文件

/**
 * 启动程序
 * 
 * @author xxx
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@ImportResource(locations = {"classpath:hessian-service.xml"})
public class AdminApplication {
	public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class, args);
    }
}
方法三(注解发布服务:推荐)

先声明HessianService.java注解类(此注解类包含了Spring的@Service注解)

import org.springframework.stereotype.Service;

import java.lang.annotation.*;


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface HessianService {

    String value() default "";
}

把原先用@Service注解的接口服务实现类的@Service注解换成我们重新定义的@HessianService注解

@HessianService
public class HelloHessianServiceImpl implements HelloHessianService {
    ...
    ...
}

使用spring的BeanFactoryPostProcessor机制,添加扫描类HessianServiceScanner.java,扫描@HessianService注解并暴露hessian服务

import cn.datahh.common.annotation.HessianService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.remoting.caucho.HessianServiceExporter;
import org.springframework.stereotype.Component;

import java.util.LinkedHashSet;
import java.util.Set;

/**
 * Hessian服务扫描器
 *
 * @author xxx
 * @create 2022-08-30 17:53
 */
@Slf4j
@Component
public class HessianServiceScanner implements BeanFactoryPostProcessor {

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Set<String> beanDefinitions = new LinkedHashSet<>();
        String[] beanNames = beanFactory.getBeanNamesForAnnotation(HessianService.class);
        for (String beanName : beanNames) {
            String className = beanFactory.getBeanDefinition(beanName).getBeanClassName();
            Class<?> clazz;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new BeanInitializationException(e.getMessage(), e);
            }
            String hessianServiceBeanName = "/" + beanName.replace("Impl", "");

            BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(HessianServiceExporter.class);
            builder.addPropertyReference("service", beanName);
            builder.addPropertyValue("serviceInterface", clazz.getInterfaces()[0].getName());
            ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(hessianServiceBeanName, builder.getBeanDefinition());

            beanDefinitions.add(hessianServiceBeanName);
        }

        log.info("--------------加载Hessian服务---------------------->");
        if (beanDefinitions.isEmpty()) {
            System.out.println(this.getClass().getName()+": not service be scanned");
        } else {
            for (String beanName : beanDefinitions) {
                System.out.println(this.getClass().getName() + ": " + beanName);
            }
        }
        log.info("==============初始化Hessian服务:" + (beanDefinitions.isEmpty() ? 0 : beanDefinitions.size()) + " 个==============>");
    }
}

至此,服务端发布服务完成。

客户端

客户端注入bean就可以使用,可以更加灵活的封装,这里只是作简单的使用
方法也有很多,介绍三种

方法一(单独注入bean)

Spring-boot启动类添加bean,业务代码直接使用注入的bean即可

/**
 * 启动程序
 * 
 * @author xxx
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class AdminApplication {
	public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class, args);
    }

	@Bean
	public HessianProxyFactoryBean hessianClient() {
		HessianProxyFactoryBean hessianProxyFactoryBean = new HessianProxyFactoryBean();
		hessianProxyFactoryBean.setServiceUrl("http://localhost:8080/helloHessianService");
		hessianProxyFactoryBean.setServiceInterface(HelloHessianService.class);
		return hessianProxyFactoryBean;
	}
}

配置好Bean后,编写TestController.java,自动装载刚刚配置的Bean,直接引入使用

@RestController
public class TestController {

	@Autowired
	private HelloHessianService helloHessianService;
	
	@RequestMapping("/test")
	public String test() {
		return helloHessianService.hello("小明");
	}
}
方法二(从代理获取bean)

直接用代理请求服务,跟方法一大同小异,写一个测试类TestHessian.java

/**
 * description
 *
 * @author xxx
 * @create 2022-09-02 13:45
 */
public class TestHessian {

	/**
     *  获取服务端对象
     * @param clazz 实体对象泛型
     * @param url 服务端url地址
     * @param <T>
     * @return 服务对象
     */
    public static <T> T getHessianService(Class<T> clazz,String url) throws Exception {
        // 代理工厂
        HessianProxyFactory factory = new HessianProxyFactory();
        return (T)factory.create(clazz,url);
    }
    
    @Test
    //创建测试类方法
    public void test() throws Exception {
        String url = "http://localhost:8080/helloHessianService";
        HelloHessianService helloHessianService = getHessianService(HelloHessianService.class, url);
        String result = helloHessianService.hello("小明");
        System.out.println(result);
    }
方法三(配置文件,服务量多时:推荐)

首先,编写hessian-client.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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="helloHessianService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
        <property name="serviceInterface" value="com.hessian.service.HelloHessianService" />
        <property name="serviceUrl" value="http://localhost:8080/helloHessianService" />
        <property name="overloadEnabled" value="true" />
    </bean>

</beans>

Spring-boot启动类加载配置文件

/**
 * 启动程序
 * 
 * @author xxx
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@ImportResource(locations = {"classpath:hessian-client.xml"})
public class AdminApplication {
	public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class, args);
    }
}

使用与方法一同样,TestController.java,自动装载刚刚配置的Bean,直接引入使用

@RestController
public class TestController {

	@Autowired
	private HelloHessianService helloHessianService;
	
	@RequestMapping("/test")
	public String test() {
		return helloHessianService.hello("小明");
	}
}

结束。

 类似资料: