spring的父子上下文容器及配置

本文深入解析Spring框架中父子容器的概念及应用,阐述如何通过配置父子容器实现上下文隔离,解决项目中因多个相同bean导致的注入冲突问题。

本文由作者张远道授权网易云社区发布。

spring父子容器

spring总的上下文容器有父子之分。父容器和子容器。父容器对子容器可见,子容器对父容器不可见。

对于传统的spring mvc来说,spring mvc容器为子容器,也就是说ServletDispatcher对应的容器为子容器,而web.xml中通过ConextLoaderListener的contextConfigLocation属性配置的为父容器。

使用场景

父子容器的主要用途之一便是是上下文隔离。考虑以下一种场景。

project-service.jar为服务层模块。包含一些数据库service方法。起对应的spring配置文件为project-service.xml。 project-api为api服务器代码。它依赖于project-service.jar。 但是,project-api需要对project-service里的某些方法进行decorate,进行装饰,比如给CustomerService进行装饰。装饰后的类为CachedCustomerService。于是,现在project-api里面包含两个CustomerService,一个是来自project-service的CustomerService,另一个是CachedCustomerService。这个时候,如果project-api工程所有的配置文件都通过一个上下文进行加载,势必出现问题。因为,project里的PayService里通过@Resource标准注入了CustomerService,类似如下

@Serivcepublic class PayService{@Resourceprivate CustomerService cusService;

}

这时,由于上下文在注入customerService属性的时候,遇到了两个CustomService。它无法判读注入哪个Service。 当然了,有人会说,改一下PayService的Resource属性,指定下具体注入哪个。但是,project-service.jar是第三方库的话,改动代码变得不可行,除非拿到源码。

配置父子容器

这个时候,就可以通过父子容器的方式解决这个问题。 将project-service放在父容器中,project-api所有的bean用子容器加载。

假设project-api的上下文配置文件为project-api.xml,实现方法如下。

1、定义project-total.xml

    <bean id = "serviceContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
        <constructor-arg>
        <value>
            classpath:project-service.xml        </value>
        </constructor-arg>
    </bean>


    <bean id = "apiContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
        <constructor-arg>
            <value>
                classpath:project-api.xml            </value>
        </constructor-arg>

        <constructor-arg>
            <ref bean="serviceContext"/>
        </constructor-arg>
    </bean>

2、在web.xml的上下文配置中如下。

    <context-param> 
        <param-name>contextConfigLocation</param-name>
        <param-value> classpath*:project-total.xml</param-value>  
    </context-param>


    <listener>   
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>   
      </listener>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

说明,其中,serviceContext为父容器,apiContext为子容器。在apiContext扫描路径里的任务bean都对serviceContext不可见。从而达到隔离的目的

参考列表


更多网易技术、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 #3.14 Piday#我的圆周率日
【推荐】 玩转可视化--来聊聊地图投影的学问

转载于:https://www.cnblogs.com/163yun/p/10102266.html

<think>我们正在讨论Spring框架中多上下文并行加载的实现方式和注意事项。根据用户提供的引用和之前的问题,我们需要关注以下几点:引用[1]提到了在Spring框架下利用管道模式、上下文和路由表实现业务流程串联。这暗示了Spring上下文的灵活使用。引用[2]展示了一个bean的配置,它依赖于线程池和配置信息,这涉及到bean的加载顺序和依赖关系。引用[3]提到了在SpringCloud中每个服务都有自己的ApplicationContext,这直接关联到多上下文的概念。用户的问题:Spring框架中多上下文并行加载的实现方式和注意事项。我们需要解释什么是多上下文并行加载,如何实现,以及注意事项。实现方式:1.使用多个ApplicationContext实例:在同一个JVM中,可以创建多个SpringApplicationContext,每个上下文可以独立加载配置。2.上下文(父容器)与子上下文(子容器):Spring支持上下文层次结构,子上下文可以访问父上下文中的bean,但父上下文不能访问子上下文的bean。3.并行加载:可以利用多线程同时加载多个ApplicationContext,以加速启动过程。注意事项:1.上下文隔离:每个上下文中的bean默认是相互隔离的,注意bean的作用域和可见性。2.资源竞争:并行加载时,如果多个上下文依赖相同的资源(如数据库连接池、线程池等),需要确保资源的线程安全或合理配置。3.依赖关系:如果上下文之间存在依赖(如子上下文需要父上下文中的某些bean),则加载顺序很重要,不能随意并行。4.类加载器:多个上下文可能使用不同的类加载器,需要注意类加载冲突。5.事件传播:Spring上下文事件(如ContextRefreshedEvent)在多个上下文之间不会自动传播,需要手动处理。配置示例:我们可以通过编程方式创建多个ApplicationContext,并指定它们之间的关系。示例代码:```java//创建父上下文AnnotationConfigApplicationContextparentContext=newAnnotationConfigApplicationContext();parentContext.register(ParentConfig.class);parentContext.refresh();//创建子上下文1AnnotationConfigApplicationContextchildContext1=newAnnotationConfigApplicationContext();childContext1.setParent(parentContext);childContext1.register(ChildConfig1.class);//使用线程并行刷新子上下文Threadthread1=newThread(()->childContext1.refresh());thread1.start();//创建子上下文2AnnotationConfigApplicationContextchildContext2=newAnnotationConfigApplicationContext();childContext2.setParent(parentContext);childContext2.register(ChildConfig2.class);Threadthread2=newThread(()->childContext2.refresh());thread2.start();//等待两个子上下文加载完成thread1.join();thread2.join();```在这个示例中,父上下文首先加载,然后两个子上下文并行加载。由于它们都依赖于父上下文,所以父上下文必须先加载完成。注意事项补充:-并行加载可能不会带来显著的性能提升,因为I/O(如读取配置)和类加载可能是瓶颈。-确保并行加载的上下文之间没有循环依赖。引用说明:引用[3]中提到了每个服务有自己的ApplicationContext,在微服务架构中,每个微服务通常是一个独立的SpringBoot应用,拥有自己的上下文。但是,如果在一个JVM内运行多个Spring上下文(例如,在一个应用中模块化隔离),则可以使用上述方法。另外,引用[2]中提到的bean配置,如果多个上下文需要共享同一个线程池,可以将该线程池定义在父上下文中,这样所有子上下文都可以使用。相关问题:1.Spring中父上下文和子上下文之间的bean是如何继承的?2.如何避免多个Spring上下文之间的配置冲突?3.SpringBoot中如何实现多个上下文的并行加载?</think>### Spring上下文并行加载实现与配置 #### 一、实现方式 1. **显式创建多个上下文** 通过编程方式创建多个`ApplicationContext`实例,每个上下文独立加载配置: ```java // 创建父上下文 AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext(ParentConfig.class); // 创建并行子上下文 AnnotationConfigApplicationContext childContext1 = new AnnotationConfigApplicationContext(ChildConfig1.class); AnnotationConfigApplicationContext childContext2 = new AnnotationConfigApplicationContext(ChildConfig2.class); // 设置父子关系(可选) childContext1.setParent(parentContext); ``` 2. **模块化上下文划分** ```java // 服务A的上下文 @Configuration @ComponentScan("com.example.serviceA") public class ServiceAConfig { /* 服务A专用配置 */ } // 服务B的上下文 @Configuration @ComponentScan("com.example.serviceB") public class ServiceBConfig { /* 服务B专用配置 */ } ``` 3. **并行加载优化** 利用线程池实现上下文并行初始化: ```java ExecutorService executor = Executors.newFixedThreadPool(2); Future<ApplicationContext> future1 = executor.submit(() -> new AnnotationConfigApplicationContext(Config1.class)); Future<ApplicationContext> future2 = executor.submit(() -> new AnnotationConfigApplicationContext(Config2.class)); // 获取上下文结果 ApplicationContext ctx1 = future1.get(); ApplicationContext ctx2 = future2.get(); ``` #### 二、配置要点 1. **上下文层次结构** ```mermaid graph LR RootContext[根上下文] --> ServiceA[服务A上下文] RootContext --> ServiceB[服务B上下文] ServiceA --> ModuleX[模块X] ServiceB --> ModuleY[模块Y] ``` 2. **资源隔离配置** ```xml <!-- 不同上下文的独立配置 --> <context:property-placeholder location="classpath:serviceA.properties" ignore-unresolvable="true"/> ``` 3. **Bean可见性规则** - 子上下文可访问父上下文Bean - 同级上下文默认不可见(需通过`@Autowired`显式注入) - 根上下文提供公共基础设施(如数据源、线程池)[^2] #### 三、注意事项 1. **依赖管理** - 避免跨上下文循环依赖 - 使用`@DependsOn`明确跨上下文初始化顺序 - 公共服务(如线程池)应在根上下文定义[^2] 2. **资源冲突预防** ```properties # 不同上下文使用独立配置 serviceA.datasource.url=jdbc:mysql://hostA/db serviceB.datasource.url=jdbc:mysql://hostB/db ``` 3. **生命周期协调** ```java // 统一关闭所有上下文 ConfigurableApplicationContext root = ...; root.registerShutdownHook(); // 子上下文继承关闭逻辑 ((ConfigurableApplicationContext)childCtx).setParent(root); ``` 4. **类加载器隔离** 当使用并行加载时,建议为关键模块配置独立类加载器,防止类冲突: ```java ClassLoader customLoader = new URLClassLoader(...); Thread.currentThread().setContextClassLoader(customLoader); ``` #### 四、微服务场景实践 在Spring Cloud中天然支持多上下文: ```java // 订单服务上下文 @SpringBootApplication(scanBasePackages="com.example.order") public class OrderApp { ... } // 用户服务上下文 @SpringBootApplication(scanBasePackages="com.example.user") public class UserApp { ... } ``` 通过`@FeignClient`实现跨上下文调用[^3],每个服务维护独立上下文但可通过接口通信。 > **性能提示**:并行加载可缩短启动时间$T_{total} = \max(T_{ctx1}, T_{ctx2})$,但需权衡线程开销。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值