您现在的位置是:首页 >技术交流 >Spring父子容器网站首页技术交流
Spring父子容器
简介Spring父子容器
一、痛点
当前开发工程以来的spring-boot-starter脚手架,配置了很多通用的bean,而部分无法满足自身需求,因此需要自定义bean,这时候就有可能出现自己定义bean和脚手架或者引入的第三方依赖中的某个bean冲突,导致出现bean重复的报错问题。
二、解决方案
方法1:自定义bean的名字
给自己定义的bean起个别名,避免和二、三方包里的bean重名
方法2:@ConditionalOnMissingBean
如果在 spring 上下⽂中找不到 GsonBuilder的 bean,这⾥才会配置。如果 上下⽂已经有相同的 bean 类型,那么这⾥就不会进⾏配置
给自己定义的bean起个别名,避免和二、三方包里的bean重名
方法3:父子容器
父子容器的主要用途之一便是是上下文隔离。
spring总的上下文容器有父子之分。父容器和子容器。父容器对子容器可见,子容器对父容器不可见。
三、Spring⽗⼦容器上下⽂隔离实战(方法3)
将公共组件包(如 通⽤log、通⽤缓存)等⾥⾯的 Spring 配置信息通通由 ⽗容器进⾏加载。
将当前⼯程上下⽂中的所有 Spring 配置由 ⼦容器进⾏加载。
⽗容器和⼦容器可以存在相同类型的 bean,并且如果⼦容器存在,则会优先使⽤⼦容器的 bean,我们可以将上⾯代码进⾏如下改造:
在⼯程⽬录下创建⼀个 parent 包,并编写 parent ⽗容器的配置类:
@Slf4j
@Configuration
//将 starter 中的 enable 注解放在⽗容器的配置中
@EnableZookeeper
public class ParentSpringConfiguration {
}
⾃定义实现 SpringApplicationBuilder 类
public class ChildSpringApplicationBuilder extends SpringApplicationBuilder {
public ChildSpringApplicationBuilder(Class<?>... sources) {
super(sources);
}
public ChildSpringApplicationBuilder functions() {
//初始化⽗容器,class类为刚写的⽗配置⽂件 ParentSpringConfiguration
GenericApplicationContext parent = new AnnotationConfigApplicationContext(ParentSpringConfiguration.class);
this.parent(parent);
return this;
}
}
主要作⽤是在启动 Springboot ⼦容器时,先根据⽗配置类 ParentSpringConfiguration 初始化⽗ 容器 GenericApplicationContext。
然后当前 SpringApplicationBuilder 上下⽂将 ⽗容器设置为初始化的⽗容器,这样就完成了⽗⼦容器配置。
starter 中的 GsonBuilder 会在⽗容器中进⾏初始化。
启动 Spring 容器:
@Slf4j
//@EnableZookeeper 此注解放到了 ParentConfiguration中。
@SpringBootApplication
public class ChildSpringServer {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new ChildSpringApplicationBuilder(ChildSpringServer.class)
.functions()
.run(args);
log.info("applicationContext: {}", applicationContext);
}
}
此时,可以正常启动 spring 容器,我们通过 applicationContext.getBean() 的形式获取 ZookeeperClinet。
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new ChildSpringApplicationBuilder(ChildSpringServer.class)
.functions()
.registerShutdownHook(false)
.run(args);
log.info("applicationContext: {}", applicationContext);
//当前上下⽂
log.info("zk name: {}", applicationContext.getBean(ZookeeperClient.class));
//当前上下⽂的⽗容器 get
log.info("parent zk name: {}", applicationContext.getParent().getBean(ZookeeperClient.class));
}
⽇志打印:
zk name: ZookeeperClient(name=From Current Project) //来⾃当前⼯程,⼦容器
parent zk name: ZookeeperClient(name=From Starter.) //来⾃⽗容器
可以看到当前上下⽂拿到的 bean 是当前⼯程配置的 bean,然⽽我们还可以获取到 ⽗容器中配置的 bean,通过先 getParent() (注意NPE),
然后再获取bean,则会获取到 ⽗容器中的 bean。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。