spring mvc里的root/child WebApplicationContext的关系

本文介绍了Spring MVC应用程序中root和child WebApplicationContext的继承关系。root Context由applicationContext.xml加载,child Context由servlet-context.xml加载。在代码示例中,展示了如何通过不同方式获取这两个上下文,并通过containsLocalBean()方法检查bean的存在,强调了区分不同上下文可能带来的问题。在applicationContext.xml中,通常扫描非Controller类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前的博客搭建spring mvc应用的时候,我们使用spring中的ContextLoaderListener和DispatcherServlet分别加载了不同的spring配置文件。spring mvc程序里会有两个WebApplicationContext,一个是parent,从applicationContext.xml里加载的,一个是child,从servlet-context.xml里加载的。 两者是继承关系,child WebApplicationContext 可以通过getParent()函数获取到root WebApplicationContext。


还是之前的代码和配置,我们修改下FirstController.java来验证下父子关系:

package net.aty.springmvc;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import net.aty.service.MyService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.DispatcherServlet;

@Controller
public class FirstController {

	@Autowired
	private MyService service;

	@Autowired
	private HttpServletRequest request;

	public FirstController() {
		System.out.println("I am a controller.");
	}

	@RequestMapping("/mvc/first/hello.do")
	@ResponseBody
	public String hello(@RequestParam("userName") String userName) {

		testContext();

		return service.process(userName);
	}

	private void testContext() {

		WebApplicationContext root = ContextLoader
				.getCurrentWebApplicationContext();

		ServletContext servletContext = root.getServletContext();

		// true
		System.out
				.println(root == servletContext
						.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));

		// true
		System.out.println(root == WebApplicationContextUtils
				.getWebApplicationContext(servletContext));

		WebApplicationContext child = (WebApplicationContext) request
				.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);

		// false
		System.out.println("root:" + root.containsLocalBean("myService"));

		// true
		System.out
				.println("root:" + root.containsLocalBean("firstController"));

		// false
		System.out.println("child:" + child.containsLocalBean("myService"));

		// true
		System.out.println("child:"
				+ child.containsLocalBean("firstController"));

		// true
		System.out.println("is parent==" + (child.getParent() == root));

	}

}


当我们再浏览器访问http://localhost:8080/SpringMvcDemo/mvc/first/hello.do?userName=11的时候,后台输出:



代码有几点需要说明一下:

1.我们可以通过ContextLoader.getCurrentWebApplicationContext()获取rootWebApplicationContext。

2.也可以通过WebApplicationContextUtils.getWebApplicationContext(servletContext)获取root。

3.可以通过request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE)获取child。

4.使用containsLocalBean()可以检查自己的是否包含这个bean,不会从父context中查找这个bean。


区分不同的spring context也会带来一些问题,摘自这篇文章

1.不少开发者不知道Spring mvc里分有两个WebApplicationContext,导致各种重复构造bean,各种bean无法注入的问题。 
2.有一些bean,比如全局的aop处理的类,如果先root WebApplicationContext里初始化了,那么child 
WebApplicationContext里的初始化的bean就没有处理到。如果在child WebApplicationContext里初始化,
在root WebApplicationContext里的类就没有办法注入了。 
3.区分哪些bean放在root/child很麻烦,不小心容易搞错,而且费心思。


解决这些问题,其实只使用一个ApplicationContext即可,按照下面这样配置,可以让spring只创建一个spring上下文。
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>

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

<servlet>
	<servlet-name>dispatcher</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value></param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>dispatcher</servlet-name>
	<url-pattern>/*</url-pattern>
</servlet-mapping>

如果确实需要2个spring配置文件,root WebApplicationContext是通过listener初始化的,child WebApplicationContext是通过servlet初始化的。


而在applicationContext.xml里通常只component-scan非Controller的类,如:

<context:component-scan base-package="io.github.test">
	<context:exclude-filter expression="org.springframework.stereotype.Controller"
		type="annotation" />
	<context:exclude-filter type="annotation"
		expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>


在servlet-context.xml里通常只component-scan Controller类,如:
<context:component-scan base-package="io.github.test.web" use-default-filters="false">
	<context:include-filter expression="org.springframework.stereotype.Controller"
		type="annotation" />
	<context:include-filter type="annotation"
		expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>


参考文章: 扯谈spring mvc之WebApplicationContext的继承关系


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值