初始入口:
org.springframework.web.context.ContextLoaderListener org.springframework.web.context.ContextLoader
类的继承关系图如下:
可以看到这个Listener实现了Servlet标准提供的接口,这个listener只有两个方法是 用来监听web容器启动和关闭动作的:
/**
* Receives notification that the web application initialization
* process is starting.
*
* <p>All ServletContextListeners are notified of context // 这里已经说了,使用的观察者模式,容器启动会调用
* initialization before any filters or servlets in the web
* application are initialized.
*
* @param sce the ServletContextEvent containing the ServletContext
* that is being initialized
*/
public void contextInitialized(ServletContextEvent sce); /**
* Receives notification that the ServletContext is about to be
* shut down.
*
* <p>All servlets and filters will have been destroyed before any
* ServletContextListeners are notified of context
* destruction.
*
* @param sce the ServletContextEvent containing the ServletContext //这里也说了,容器关闭会调用
* that is being destroyed
*/
public void contextDestroyed(ServletContextEvent sce);
当然ContextLoaderListener实现了这两个方法:
/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext()); //这个方法在父类ContextLoader中实现 ,这里只是把ServletContext当参数传过去了,因为在执行逻辑中需要用到很多与web容器相关的参数
} /**
* Close the root web application context.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext()); //同上
}
切换到父类ContextLoader 中initWebApplicationContext方法中:
/**
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @see #ContextLoader(WebApplicationContext)
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
} Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis(); try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext); //这里先创建Web环境的 ApplicationContext ,这个Context的地位就类似于ServletContext,是获取Bean的入口
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext); //这才是最核心的方法,看名字就知道 配置并刷新 ApplicationContext,这个动作完成了类示例的创建,初始化和 注册事件的发布,各种监听的执行, 为
} //DispatcherServlet这个核心类执行Handler的注册做好了万全准备
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context); //这里可以看到把当前Context绑定到当前线程,是为了做隔离使用,保证线程安全,稍后再说这个问题
} if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
} return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
先来看看ApplicationContext的创建,进入createWebApplicationContext,毕竟它是获取Bean的入口。
/**
* Instantiate the root WebApplicationContext for this loader, either the
* default context class or a custom context class if specified.
* <p>This implementation expects custom contexts to implement the
* {@link ConfigurableWebApplicationContext} interface.
* Can be overridden in subclasses.
* <p>In addition, {@link #customizeContext} gets called prior to refreshing the
* context, allowing subclasses to perform custom modifications to the context.
* @param sc current servlet context
* @return the root WebApplicationContext
* @see ConfigurableWebApplicationContext
*/
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);
} /**
* Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified.
* @param servletContext current servlet context
* @return the WebApplicationContext implementation class to use
* @see #CONTEXT_CLASS_PARAM
* @see org.springframework.web.context.support.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);
}
}
}
这里先从ServletContext中获取参数是contextClass的值,也就是从web.xml读取contextParam或者硬编码的方式(servlet3.0以上的版本)中获取配置指定的ApplicationContext。如果没有,使用默认的策略 XmlWebApplicationContext,这个默认的策略 defaultStrategies是在ContextLoader开头的静态代码块中初始的:
/**
* Name of the class path resource (relative to the ContextLoader class)
* that defines ContextLoader's default strategy names.
*/
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties"; private static final Properties defaultStrategies; static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); //这里从ContextLoader所在的包下加载一个叫ContextLoader.properties的文件,
//这个文件在spring-web包的context子包中,内容只有一行,配置了默认的WebApplicationContext
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
有了ApplicationContext了,再回到上面initWebApplicationContext方法,下面就开始判断这个context是不是ConfigurableWebApplicationContext实例,如果是,并且没有执行过刷新,就执行配置刷新的方法,也就是上面提到的最核心的方法,用于bean的初始化,
这里在判断后,还获取了parent context并将它设置成当前context的parent.
当然最核心的入口,一切源头:
org.springframework.web.servlet.DispatcherServlet
该类的继承关系(使用Idea分析工具)如下:
DefaultListableBeanFactory类的继承关系:
与Bean创建时相关的Aware接口的三个直接子接口:
再看SpringMVC通过一个DispatcherServlet处理Servlet的更多相关文章
-
1.SpringMVC设计理念与DispatcherServlet
SpringMVC作为Struts2之后异军突起的一个表现层框架,正越来越流行,相信javaee的开发者们就算没使用过SpringMVC,也应该对其略有耳闻.我试图通过对SpringMVC的设计思想和 ...
-
springmvc源码分析----入门看springmvc的加载过程
接上一篇我们写的入门---http://www.cnblogs.com/duanxiaojun/p/6591448.html 今天从这个门里进去我们看springmvc是如何在容器启动的时候将各个模块 ...
-
SpringMVC源码分析2:SpringMVC设计理念与DispatcherServlet
转自:https://my.oschina.net/lichhao/blog SpringMVC简介 SpringMVC作为Struts2之后异军突起的一个表现层框架,正越来越流行,相信javaee的 ...
-
Spring Mvc中DispatcherServlet和Servlet的区别小结
在web开发过程中开始接触的是servlet,用来处理用户请求.这几年随着spring 框架越来越成熟,几乎成了java web开发界的主流框架.既然这么受欢迎肯定有它的优点,spring框架在原来的 ...
-
探秘Tomcat——一个简易的Servlet容器
即便再简陋的服务器也是服务器,今天就来循着书本的第二章来看看如何实现一个servlet容器. 背景知识 既然说到servlet容器这个名词,我们首先要了解它到底是什么. servlet 相比你或多或少 ...
-
SpringMVC基础——一个简单的例子
一.导入 jar 包 二.配置 web.xml 文件 <servlet> <servlet-name>dispatcherServlet</servlet-name> ...
-
how tomcat works 札记(两)----------一个简单的servlet集装箱
app1 (看着眼前这章建议读者,看how tomcat works 札记(一个)----------一个简单的webserver http://blog.csdn.net/dlf123321/art ...
-
再看Ajax
再回顾Ajax相关的内容,再次梳理学习还是很有必要的,尤其是实际的开发中,ajax更是必不可少,仔细学习以便避免不必要的错误. 文章导读: --1.使用XMLHttpRequest---------- ...
-
再看ftp上传文件
前言 去年在项目中用到ftp上传文件,用FtpWebRequest和FtpWebResponse封装一个帮助类,这个在网上能找到很多,前台使用Uploadify控件,然后在服务器上搭建Ftp服务器,在 ...
随机推荐
-
yum简单安装apache
yum install httpd -y chkconfig httpd on service httpd start 启动软件
-
HDU 5820 (可持久化线段树)
Problem Lights (HDU 5820) 题目大意 在一个大小为50000*50000的矩形中,有n个路灯.(n<=500000) 询问是否每一对路灯之间存在一条道路,使得长度为|x1 ...
-
【风马一族_Android】Button 按钮之记录
Button button = new Button(); Button button = (Button)findViewById(R.id.bt_button);//让按钮显示灰色,失效 butt ...
-
SVN 提交必填备注Commit
操作方法:在SVN的Repositories下,找到要配置的项目,在项目目录下找到hooks文件夹,在其下创建pre-commit.bat文件,把下面复制进去就可以了(无需重启,如果改动,保存bat文 ...
-
android: ListView历次优化
第一版: ListView一屏显示多少对象其内部就创建多少View对象.滑动时退出的缓存对象留给滑进去时调用getView传的convertView.因为如果每次都findViewById查找创建视图 ...
-
SE 2014年5月23日
两站点 A 和 B,由于业务往来需要,所以工程师提出vpn技术,同时需要保证业务流在internet上的安全性,同时在这里站点均为固定ip地址. 通过分析以上信息,确定这里使用 IPSec VPN的主 ...
-
Printk 标志优先级别
#define KERN_EMERG "<0>" /* 致命级:紧急事件消息,系统崩溃之前提示,表示系统不可用 */# ...
-
centos配置单网卡为Trunk模式
单网卡配置多IP(trunk模式)操作标准 1.linux的单网卡配置多IP的操作 下面为linux系统单网卡配置多IP(trunk模式)的操作步骤,系统平台为centos5.5.全部操作完成后,将实 ...
-
Linux命令:mapfile
mapfile [-n 计数] [-O 起始序号] [-s 计数] [-t] [-u fd] [-C 回调] [-c 量子] [数组] 两个作用: 一是给index数组读取标准输入来赋值:二是文件描述 ...
-
mybatis @Select注解中如何拼写动态sql
@Mapper public interface DemandCommentMapper extends BaseMapper<DemandComment>{ @Select(" ...