目录
问题
系统中有2个模块:module1和module2,两个模块是独立开发的,module2会使用到module1中的一 些类,module1会将自己打包为jar提供给module2使用,我们来看一下这2个模块的代码。
模块一:
@Component
public class Service1 {
public String m1() {
return "我是module1中的Service1中的m1方法";
}
}
@Component
public class Service2 {
@Autowired
private Service1 service1;
public String m1() {
return this.service1.m1();
}
}
@ComponentScan
public class module1Config {
}
模块二:
@Component
public class Service1 {
public String m2() {
return "我是module2中的Service1中的m2方法";
}
}
@Component
public class Service3 {
//使用模块2中的Service1
@Autowired
private Service1 service1; //
//使用模块1中的Service2
@Autowired
private Service2 service2;
public String m1() {
return this.service2.m1();
}
public String m2() {
return this.service1.m2();
}
}
@ComponentScan
public class module2Config {
}
测试:
@Test
void contextLoads() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(module1Config.class,module2Config.class);
context.refresh();
}
运行后报错了:
Failed to parse configuration class [com.example.module2.module2Config]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'service1' for。。。
上面报错的原因是容器中的Service这个bean冲突了
那么我们如何解决?
对module1中的Service1进行修改?这个估计是行不通的,module1是别人以jar的方式提供给我们的, 源码我们是无法修改的。 而module2是我们自己的开发的,里面的东西我们可以随意调整,那么我们可以去修改一下module2中 的Service1,可以修改一下类名,或者修改一下这个bean的名称,此时是可以解决问题的。 不过大家有没有想过一个问题:如果我们的模块中有很多类都出现了这种问题,此时我们一个个去重 构,还是比较痛苦的,并且代码重构之后,还涉及到重新测试的问题,工作量也是蛮大的,这些都是风险。 而spring中的父子容器就可以很好的解决上面这种问题。
什么是父子容器
创建spring容器的时候,可以给当前容器指定一个父容器。
BeanFactory的方式
//创建父容器parentFactory
DefaultListableBeanFactory parentFactory = new DefaultListableBeanFactory();
//创建一个子容器childFactory
DefaultListableBeanFactory childFactory = new DefaultListableBeanFactory();
//调用setParentBeanFactory指定父容器
childFactory.setParentBeanFactory(parentFactory);
ApplicationContext的方式
//创建父容器
AnnotationConfigApplicationContext parentContext = new
AnnotationConfigApplicationContext();
//启动父容器
parentContext.refresh();
//创建子容器
AnnotationConfigApplicationContext childContext = new
AnnotationConfigApplicationContext();
//给子容器设置父容器
childContext.setParent(parentContext);
//启动子容器
childContext.refresh();
父子容器特点
- 父容器和子容器是相互隔离的,他们内部可以存在名称相同的bean
- 子容器可以访问父容器中的bean,而父容器不能访问子容器中的bean
- 调用子容器的getBean方法获取bean的时候,会沿着当前容器开始向上面的容器进行查找,直到 找到对应的bean为止
- 子容器中可以通过任何注入方式注入父容器中的bean,而父容器中是无法注入子容器中的bean, 原因就是第二条
使用父子容器解决开头的问题
关键代码:
@Test
public void test2() {
//创建父容器
AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext();
//向父容器中注册Module1Config配置类
parentContext.register(module1Config.class);
//启动父容器
parentContext.refresh();
//创建子容器
AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext();
//向子容器中注册Module2Config配置类
childContext.register(module2Config.class);
//给子容器设置父容器
childContext.setParent(parentContext);
//启动子容器
childContext.refresh();
//从子容器中获取Service3
Service3 service3 = childContext.getBean(Service3.class);
System.out.println(service3.m1());
System.out.println(service3.m2());
}
运行就不报异常了,输出:
我是module1中的Servce1中的m1方法
我是module2中的Servce1中的m2方法