最近在开发一个springboot项目,使用https接口与客户系统进行交互,我方提供服务端,但是客户想用通过HttpInvoker 的方式进行调用,此篇文章暂时不介绍HttpInvoker,在此介绍如何动态的吧相关的实现动态的发布成HTTPInvoker服务。
首先开发一个标记接口,所有的实现此接口的类都将被动态的注册成HTTPInvoker 服务
package com.baiwang.custom.web.base;
import java.io.Serializable;
/**
* @author wjh
* @Description HTTPInvoker 服务发布标记接口,默认实现序列化
* @date 20180605
*
*/
public interface HTTPInvokerService extends Serializable{
}
业务接口
package com.baiwang.custom.web.service;
public interface IUserTest {
public String getAll();
}
业务实现类
package com.baiwang.custom.web.service.impl;
import org.springframework.stereotype.Service;
import com.baiwang.custom.web.base.HTTPInvokerService;
import com.baiwang.custom.web.service.IUserTest;
@Service
public class UserTestImpl implements IUserTest, HTTPInvokerService {
private static final long serialVersionUID = 1L;
@Override
public String getAll() {
System.out.println("====>getAll Test 123");
// TODO Auto-generated method stub
return null;
}
}
注册类
package com.baiwang.custom.web.base;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
/**
* @author wjh
* @version 0.1
* @Description 本类是用来提供 容器上下文<link>ApplicationContext</link> 和容器注册器<link>BeanDefinitionRegistryPostProcessor</link>,
* 当前版本实现个性化的HTTPInvoker服务的动态发布注册。
* @param ContextRefreshedEvent
*/
@Component
public class BeanUtil implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
private static Logger logger = LoggerFactory.getLogger(BeanUtil.class);
//容器上下文
public static ApplicationContext applicationContext;
//容器注册器
private static BeanDefinitionRegistry registry;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanUtil.applicationContext = applicationContext;
}
/**
* @Description:将bean发布成HttpInvoker服务,同时添加地址实体映射<link>SimpleUrlHandlerMapping</link>
* 关于多接口实现HttpInvokerServiceExporter 实体命名规则 从0开始 依次递加
* @param String beanName bean名称
* @param T t 实体
*/
public static <T> void registAsInvokerBean(String beanName, T t) {
System.out.println("========> "+t.getClass().getName());
logger.info("==>registAsInvokerBean {} {} Begin params " +"["+beanName+"]," +"["+t+"]");
long beginTime = System.currentTimeMillis();
// 获取所有服务接口 不包括ApplicationContextAware,BeanDefinitionRegistryPostProcessor
Class[] classes = t.getClass().getInterfaces();
//接口过滤 过滤掉标记接口 HTTPInvokerService,接口需要在com.baiwang 包或子包内
// 发布成invoker接口默认需要面向接口编程 如果没有实现接口编程 抛出异常
if (classes == null || classes.length == 0) {
throw new RuntimeException("==>注册发布Invoker bean " + beanName + "未实现面向接口编程,不能进行注册,class name :"+t.getClass().getName());
} else {
// 如果是多接口实现,同样基于支持,但是不包括标记接口 HTTPInvokerService
for (int i = 0; i < classes.length; i++) {
//获取接口名称
String className = classes[i].getName();
// 不包括标记接口 HTTPInvokerService,接口需要在com.baiwang 包或子包内
if (classes[i].equals(HTTPInvokerService.class)|| !className.startsWith("com.baiwang")) {
continue;
}
// 关于多接口实现HttpInvokerServiceExporter 实体命名规则 如果只有一个实际实现接口 则名字为 beanName+"Exporter"
// 如果存在多个 则使用beanName + i+ "Exporter"
String exporterName = beanName + (i==0?"":i)+ "Exporter";
// 新注册 HttpInvokerServiceExporter 的bean用作分发代理
RootBeanDefinition beanExporter = new RootBeanDefinition(HttpInvokerServiceExporter.class);
// 添加注册bean的属性
MutablePropertyValues propertyValues = new MutablePropertyValues();
//设置服务接口
propertyValues.add("serviceInterface", classes[i]);
//设置服务bean 所有的接口用同一个bean
propertyValues.add("service", t);
beanExporter.setPropertyValues(propertyValues);
// 添加注册 HttpInvokerServiceExporter 名称是实际的实现类 首字母小写 加上Exporter
registry.registerBeanDefinition(exporterName, beanExporter);
HttpInvokerServiceExporter sh2 = (HttpInvokerServiceExporter) applicationContext.getBean(exporterName);
// 构建 实体地址映射
String path ="/" + exporterName + ".service";
SimpleUrlHandlerMapping sh = applicationContext.getBean(SimpleUrlHandlerMapping.class);
Properties propetis = new Properties();
propetis.setProperty(path, exporterName);
sh.setMappings(propetis);
//注册hander(设置完实体地址映射时需要初始化一下handerMapping)
sh.initApplicationContext();
logger.info("==>当前Invoker 服务注册信息:path:"+path +",处理接口 :"+className+"处理实现:"+t);
}
}
logger.info("==>registAsInvokerBean ["+beanName+"] end time "+ (System.currentTimeMillis()-beginTime));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
// TODO Auto-generated method stub
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
}
/**
* Description :将特定的请求映射到具体的hessianservice
*
*/
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
return new SimpleUrlHandlerMapping();
}
}
扫描实现HTTPInvokerService 接口的类并注册为 HttpInvokerServiceExcporter 代理类,同时 将代理类注册对应的地址
附上客户端调用的代码
package com.baiwang.custom.web.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import com.baiwang.custom.web.base.BeanUtil;
import com.baiwang.custom.web.base.MailSend;
import com.baiwang.custom.web.entity.Users;
import com.baiwang.custom.web.service.IUserTest;
import com.baiwang.custom.web.service.UserService;
import com.baiwang.platform.custom.common.result.CrRpcResult;
@RestController
@RequestMapping("/test")
public class CustomTestController {
private static final Log logger = LogFactory.getLog(CustomTestController.class);
@Autowired
IUserTest invokerUserAutoTest;
@Autowired
MailSend mailSend;
@RequestMapping(value = "/test", method = RequestMethod.GET)
public CrRpcResult findDownloadFileUrl() {
IUserTest us2 = (IUserTest) invokerUserAutoTest;
us2.getAll();
return result;
}