前段时间写了个小组件 async-excel 为了异步导入导出公共组件化,由于整个流程需要做到将错误文件或者导出文件异步写到oss存储平台,并且需要让用户可查,所以这里还要设计一张表作为异步任务记录使用,但是又不想既有项目进行多数据源配置,那样对开发太不友好了。所以就想到了spring的父子容器。
父子容器的特性:
- 子容器可以获取到父容器的bean
- 父容器无法获取子容器的bean
利用这样的特性,我可以把数据源声明在子容器内。也就不影响原项目的配置了。
如何构建一个子容器呢?且看代码
protected AnnotationConfigApplicationContext createContext() {
//我们可以直接new一个出来
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//然后将配置类注册给它以及环境变量设置给他即可
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.<String, Object>singletonMap(this.propertyName, this.contextName)));
if (this.parent != null) {
ExcelHandleBasePackages ehbp = this.parent.getBean(ExcelHandleBasePackages.class);
basePackages = ehbp.getBasePackages();
//将它设置为ApplicationContext的子容器
context.setParent(this.parent);
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(generateDisplayName(this.contextName));
//最后执行refresh启动容器
context.refresh();
return context;
}
一个子容器构建完成
然后要还需要提供个接口给外部获取bean,因为父容器无法获取子容器的bean
defaultConfigType 是作为配置的入口函数,就跟SpringbootApplication一样。作为配置入口就好了。
public <T> T getInstance(Class<T> type) {
AnnotationConfigApplicationContext context = getContext();
//使用spring提供的工具类,可以从当前容器与祖先容器的所有bean中进行筛选。然父容器通过该方法获取Bean就好了
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
type).length > 0) {
return context.getBean(type);
}
return null;
}
至此,又想到一个点,其实这也是之前微服务架构下特别头疼的一个问题,就是一些所谓的公共特性的服务,比如一些死的缓存类的东西,比如字典。以及一些配置,每次都要去调用公共服务,这太损耗性能以及开发成本太高了,如果可以直接使用一个子容器,将它包到各个服务去,并且提供一个公共数据源,将这部分高频,低效的内容高效化。有人也许会说,这样高耦合违背了微服务的设计原则。我也不反驳,这个就跟违背数据库范式一个原则,采取适当冗余,不要死读书。