文章目录
contextConfigLocation一般配置在context-param标签中,下面首先了解context-param标签作用和Web初始化的简单过程。
context-param标签作用
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-basic.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
作用:该元素用来声明应用范围(整个WEB项目)内的上下文初始化参数。
- param-name:设定上下文的参数名称,必须是唯一名称。
- param-value:设定的参数名称的值。
Web项目初始化过程
- 启动一个WEB项目的时候,容器(比如:Tomcat)会读web.xml配置文件中的两个节点和。
- 接着容器会创建一个ServletContext(上下文),整个WEB项目所有部分都将共享这个上下文。
- 接着容器会将
<context-param></context-param>
转化为键值对,并交给ServletContext。 - 容器创建
<listener></listener>
中的类实例,即创建监听(备注:listener定义的类可以是自定义的类但必须需要继承ServletContextListener)。 - 在监听的类中会有一个
contextInitialized(ServletContextEvent event)
初始化方法,在这个方法中可以通过event.getServletContext().getInitParameter("contextConfigLocation")
来得到context-param设定的值。 - 得到这个context-param的值之后,你就可以做一些操作了。注意,这个时候你的WEB项目还没有完全启动完成,这个动作会比所有的Servlet都要早。换句话说,这个时候,你对
<context-param>
中的键值做的操作,将在你的WEB项目完全启动之前被执行。 - 举例,你可能想在项目启动之前就打开数据库,那么这里就可以在
<context-param>
中设置数据库的连接方式,在监听类中初始化数据库的连接。这个监听是自己写的一个类,除了初始化方法,它还有销毁方法。用于关闭应用前释放资源,比如说数据库连接的关闭。
由上面的初始化过程可知容器对于web.xml的加载过程是context-param >> listener >> fileter >> servlet
获取context-param定义的属性值
页面中:
${initParam.contextConfigLocation}
Servlet中:
String paramValue = getServletContext().getInitParameter(“contextConfigLocation”);
容器加载过程
- Root WebApplicationContext:这是对J2EE三层架构中的service层、dao层进行配置,如业务bean,数据源(DataSource)等。在web应用中,其一般通过ContextLoaderListener监听器来加载,通过contextConfigLocation指定配置文件。
- Servlet WebApplicationContext:这是对J2EE三层架构中的web层进行配置,如控制器(controller)、视图解析器(view resolvers)等相关的bean。通过spring-mvc中提供的DispatchServlet来加载配置,也是通过contextConfigLocation指定配置文件配置文件,默认名称为
<servlet-name>-servlet.xml
。
1、ContextLoaderListener会被优先加载,初始化时,其会根据context-param元素中contextConfigLocation参数指定的配置文件路径,来创建WebApplicationContext实例。 并调用ServletContext的setAttribute方法,将其设置到ServletContext中,属性的key为org.springframework.web.context.WebApplicationContext.ROOT
,最后的ROOT字样表明这是一个 Root WebApplicationContext。
2、DispatcherServlet在初始化时,会根据元素中contextConfigLocation参数指定的配置文件路径,来创建Servlet WebApplicationContext。同时,其会调用ServletContext的getAttribute方法来判断是否存在Root WebApplicationContext。如果存在,则将其设置为自己的parent。这就是父子上下文(父子容器)的概念。
父子容器的作用在于,当我们尝试从child context(即:Servlet WebApplicationContext)中获取一个bean时,如果找不到,则会委派给parent context (即Root WebApplicationContext)来查找。
3、如果我们没有通过ContextLoaderListener来创建Root WebApplicationContext,那么Servlet WebApplicationContext的parent就是null,也就是没有parent context。
Root WebApplicationContext创建过程源码分析
ContextLoaderListener用于创建ROOT WebApplicationContext,其实现了ServletContextListener接口的contextInitialized和contextDestroyed方法,在web应用启动和停止时,web容器(如tomcat)会负责回调这两个方法。而创建Root WebApplicationContext就是在contextInitialized中完成的,相关源码片段如下所示:
可以看到ContextLoaderListener继承了ContextLoader类,WebApplicationContext的创建是在ContextLoader的initWebApplicationContext方法中完成的,关键代码如下:
Servlet WebApplicationContext创建过程源码分析
其实就是SpringMVC初始化过程。
SpringMVC通过web.xml文件中servlet标签下的DispatcherServlet类完成自身的初始化。
每个servlet在初始化时,会先调用servlte的构造函数(为默认构造函数),接着调用init函数,而DispatcherServlet的init方法在其父类HttpServletBean中。
// DispatcherServlet第一次加载时调用init方法
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
/*加载web.xml文件中的servlet标签中的init-param,其中含有springMVC的配置文件的名字和路径
*若没有,则默认为(servlet-name)-servlet.xml,
*默认路径为WEF—INF下
*/
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
//创建BeanWrapper实例,为DispatcherServlet设置属性
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
//把init-param中的参数设置到DispatcherServlet里面去
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// Let subclasses do whatever initialization they like.
//该方法在FrameworkServlet中
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
FrameworkServlet中的initServletBean方法:
参考:
https://www.cnblogs.com/yaoyiyao/p/7198076.html
https://www.cnblogs.com/jiaguozhilian/p/5819032.html