本文要点:
- 什么是父子容器
- 为什么需要用父子容器
- 父子容器如何使用
先来一个案例
系统中有2个模块:module1和module2,两个模块是独立开发的,module2会使用到module1中的一些类,module1会将自己打包为jar提供给module2使用。
模块1
module1.service1
@Component
public class Service1 {
public String m1() {
return "我是module1中的Servce1中的m1方法";
}
}
module1.service2
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Service2 {
@Autowired
private Service1 service1;
public String m1() {
return this.service1.m1();
}
}
module1.Module1Config
import org.springframework.context.annotation.ComponentScan;
//会自动扫描当前类所在的包中的所有类,将标注有@Compontent注解的类注册到spring容器,即Service1和Service2会被注册到spring容器。
@ComponentScan
public class Module1Config {
}
模块2
module2.Service1
import org.springframework.stereotype.Component;
@Component
public class Service1 {
public String m2() {
return "我是module2中的Servce1中的m2方法";
}
}
module2.Service3
import com.javacode2018.lesson002.demo17.module1.Service2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Service3 {
//使用模块2中的Service1
@Autowired
private module2.Service1 service1;
//使用模块1中的Service2
@Autowired
private module1.Service2 service2;
public String m1() {
return this.service2.m1();
}
public String m2() {
return this.service1.m2();
}
}
module2.Module2Config
@ComponentScan
public class Module2Config {
}
上面的这些使用spring来操作是会有问题的
会报错,因为两个模块中都有Service1,被注册到spring容器的时候,bean名称会冲突,导致注册失败。
运行看一下
public class ParentFactoryTest {
@Test
public void test1() {
//定义容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//注册bean
context.register(Module1Config.class, Module2Config.class);
//启动容器
context.refresh();
}
}
Caused by:
org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name ‘service1’ for bean class
[module2.Service1] conflicts with existing, non-compatible bean
definition of same name and class [module1.Service1]
我们可以使用spring中的父子容器就可以很好的解决上面这种问题。
什么是父子容器
创建spring容器的时候,可以给当前容器指定一个父容器。
方式1:BeanFactory的方式
//创建父容器parentFactory
DefaultListableBeanFactory parentFactory = new DefaultListableBeanFactory();
//创建一个子容器childFactory
DefaultListableBeanFactory childFactory = new DefaultListableBeanFactory();
//调用setParentBeanFactory指定父容器
childFactory.setParentBeanFactory(parentFactory);
方式2:ApplicationContext的方式
//创建父容器
AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext();
//启动父容器
parentContext.refresh();
//创建子容器
AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext();
//给子容器设置父容器
childContext.setParent(parentContext);
//启动子容器
childContext.refresh();
父子容器特点
- 父容器和子容器是相互隔离的,他们内部可以存在名称相同的bean
- 子容器可以访问父容器中的bean,而父容器不能访问子容器中的bean
- 调用子容器的getBean方法获取bean的时候,会沿着当前容器开始向上面的容器进行查找,直到找到对应的bean为止
- 子容器中可以通过任何注入方式注入父容器中的bean,而父容器中是无法注入子容器中的bean,原因是第2点
使用父子容器解决上面的问题
@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方法
springmvc的父子容器
springmvc父子容器就是controller层交给一个spring容器加载,其他的service和dao层交给另外一个spring容器加载,这两个容器组成了父子容器的关系
- springmvc中只使用一个容器是可以正常运行的。
- 通常我们使用springmvc的时候,采用3层结构,controller层,service层,dao层;父容器中会包含dao层和service层,而子容器中包含的只有controller层;这2个容器组成了父子容器的关系,controller层通常会注入service层的bean。
- 采用父子容器可以避免有些人在service层去注入controller层的bean,导致整个依赖层次是比较混乱的。
父容器和子容器的需求也是不一样的,比如父容器中需要有事务的支持,会注入一些支持事务的扩展组件,而子容器中controller完全用不到这些,对这些并不关心,子容器中需要注入一下springmvc相关的bean,而这些bean父容器中同样是不会用到的,也是不关心一些东西,将这些相互不关心的东西隔开,可以有效的避免一些不必要的错误,而父子容器加载的速度也会快一些。