SpringMVC源码解析(一)

加载spring上下文

ContextLoaderListener加载ApplicationContext

我们在web.xml中分别配置了ContextLoaderListener和DispatcherServlet:

<!-- spring -->
<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/applicationContext.xml
	</param-value>
</context-param>
<!-- springmvc begin -->
<servlet>
	<servlet-name>spring</servlet-name>
	<servlet-class>
		org.springframework.web.servlet.DispatcherServlet
	</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/springmvc-servlet.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>spring</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping> 

  ContextLoaderListener实现了ServletContextListener接口,在servlet容器启动后会执行contextInitialized,调用其父类ContextLoader的initWebApplicationContext,创建ApplicationContext并执行上下文刷新refresh。现在我们来看下具体是如何执行的。

//创建ApplicationContext
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	Class<?> contextClass = determineContextClass(sc);
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
				"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
	}
	return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

//创建XmlWebApplicationContext
protected Class<?> determineContextClass(ServletContext servletContext) {
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
	if (contextClassName != null) {
        try {
            return ClassUtils.forName(contextClassName,ClassUtils.getDefaultClassLoader());
		}
		catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
					"Failed to load custom context class [" + contextClassName + "]", ex);
		}
	}
	else {
		contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
		try {
            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
		}
		catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
					"Failed to load default context class [" + contextClassName + "]", ex);
		}
	}
}

//defaultStrategies初始化
//加载jar下的ContextLoader.properties配置项:org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
static {
	try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
	}
	catch (IOException ex) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
	}
}

  XmlWebApplicationContext从applicationContext.xml中加载bean的配置,刷新到自己的容器中:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
		if (idParam != null) {
			wac.setId(idParam);
		}
		else {
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(sc.getContextPath()));
		}
	}

	wac.setServletContext(sc);
	//加载配置资源,对应web.xml的配置
	/**
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/applicationContext.xml
		</param-value>
	</context-param>
	**/
	String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
	if (configLocationParam != null) {
		wac.setConfigLocation(configLocationParam);
	}

	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
	}

	customizeContext(sc, wac);
	wac.refresh();
}

DispatchServlet加载ApplicationContext

 

  DispatchServlet在初始化过程中,会调用父类FrameworkServlet的initServletBean()方法,创建spring-mvc的容器并加载bean。我们来看下代码的具体如何实现的:

//创建ApplicationContext并设置父容器
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
	Class<?> contextClass = getContextClass();
	if (this.logger.isDebugEnabled()) {
		this.logger.debug("Servlet with name '" + getServletName() +
				"' will try to create custom WebApplicationContext context of class '" +
				contextClass.getName() + "'" + ", using parent context [" + parent + "]");
	}
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException(
				"Fatal initialization error in servlet with name '" + getServletName() +
				"': custom WebApplicationContext class [" + contextClass.getName() +
				"] is not of type ConfigurableWebApplicationContext");
	}
	ConfigurableWebApplicationContext wac =
			(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

	wac.setEnvironment(getEnvironment());
	//设置父容器为ContextLoadListener加载的ApplicationContext
	wac.setParent(parent);
	wac.setConfigLocation(getContextConfigLocation());

	configureAndRefreshWebApplicationContext(wac);

	return wac;
}
//刷新上下文,加载配置的bean
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		if (this.contextId != null) {
			wac.setId(this.contextId);
		}
		else {
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
		}
	}

	wac.setServletContext(getServletContext());
	//servletConfig为web.xml中指定的springmvc-servlet.xml
	wac.setServletConfig(getServletConfig());
	wac.setNamespace(getNamespace());
	wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
	}

	postProcessWebApplicationContext(wac);
	applyInitializers(wac);
	wac.refresh();
}

  由于Web容器(如Tomcat)先加载ContextLoaderListener,然后生成一个IoC容器。然后再实例化DispatchServlet时候会加载对应的配置文件,再次生成Controller相关的IoC容器。为解决同一个bean被两次加载一般采取两种方式:
  方法1: 通过在dispatcher-servlet.xml(applicationContext.xml)中配置exclude-filter来避免重复加载的问题
  方法2:分开扫描,养成良好习惯:
    applicationContext.xml   <context:component-scan base-package="xx.xxx.xx.dao,xx.xx.xxx.service"/>
    dispatcher-servlet.xml    <context:component-scan base-package="xx.xx.xx.controller" />

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值