title: 把大象放进冰箱--spring自动扫描并发布rmi tags:
- Spring
- rmi categories: spring date: 2016-09-25 18:18:54
对于需要暴露大量rmi的应用来说,每次新增加一个service都需要手写一段xml也是蛮累的……
偷懒的程序员想到了如下的办法
利用component-scan发布service同时发布rmi呢?
说做就做
把大象放进冰箱三步骤
-
打开冰箱门
首先需要把所有的@service注解扫描,设置BeanDefinition的相关属性包括service,serviceName等 -
把大象塞进冰箱
将BeanDefinition注册到spring容器中 -
关上冰箱门
初始化对应的BeanDefinition具体代码如下
/**
* Created by qixiaobo on 2016/10/8.
*/
public class PublishRmiBean implements ApplicationContextAware {
private Logger logger = Logger.getLogger(PublishRmiBean.class);
private String basePkg;
private ApplicationContext context;
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
@Value("${erp.rmi.base.port}")
private int port;
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
private static final List<TypeFilter> includeFilters = new LinkedList<>();
private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
public String getBasePkg() {
return basePkg;
}
public void setBasePkg(String basePkg) {
this.basePkg = basePkg;
}
static {
includeFilters.add(new AnnotationTypeFilter(Service.class));
}
@PostConstruct
public void init() throws IOException {
assert (basePkg != null);
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePkg) + "/" + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
for (Resource resource : resources) {
{
try {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
String clazzName = sbd.getBeanClassName();
Class clazz = null;
if (sbd.hasBeanClass()) {
clazz = sbd.getBeanClass();
} else {
clazz = Class.forName(clazzName);
}
String beanName = RmiNameGenerator.getBeanName(clazzName.substring(clazzName.lastIndexOf(".") + 1));
registerRmi(clazz, beanName);
}
}
} catch (Throwable e) {
logger.error(e);
}
}
}
try {
context.getBeansOfType(RmiServiceExporter.class);
} catch (Exception ex) {
logger.error(ex);
}
}
private void registerRmi(Class clazz, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RmiServiceExporter.class);
if (clazz.getInterfaces().length == 0) {
logger.warn("skip publish rmi:" + beanName + " because no interface found!");
return;
}
builder.addPropertyValue("serviceInterface", clazz.getInterfaces()[0]);
builder.addPropertyValue("serviceName", beanName);
builder.addPropertyValue("registryPort", port);
builder.addPropertyReference("remoteInvocationExecutor", "clientInfoRemoteInvocationExecutor");
builder.addPropertyValue("service", context.getBean(clazz));
builder.setLazyInit(false);
getBeanDefinitionRegistry().registerBeanDefinition(beanName + "Exporter", builder.getRawBeanDefinition());
logger.info("publish rmi:" + beanName);
}
private BeanDefinitionRegistry getBeanDefinitionRegistry() {
return (BeanDefinitionRegistry) ((XmlWebApplicationContext) context).getBeanFactory();
}
private boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return true;
}
}
return false;
}
private boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
private String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(basePackage);
}
}
复制代码
在spring的xml中注册如下
复制代码
<bean class="com.xxx.PublishRmiBean" >
<property name="basePkg" value="com.xxx.service.*.impl"/>
</bean>
复制代码
复制代码