阅读前提:
1、理解IOC的一些概念,以及在Spring中的实现(上下文,BeanFactory,BeanDefinition等等)
2、理解Web的基础知识(Servlet)
3、理解MVC的基础知识(Model/View/Controller)
spring mvc的配置解析:
一、需要在web.xml中配置DispatchServlet
<!-- Spring MVC 配置 -->
<servlet>
<servlet-name>spring-sample</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-sample</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Spring bean配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 指定Spring Bean的配置文件所在目录。默认配置在WEB-INF目录下 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
1.servlet节点描述的是servlet对象,这里是spring mvc的DispatcherServlet,servlet的名称为spring-sample
2.servlet-mapping节点描述的是url映射,这里定义了对于任何/*的请求都走名称为spring-sample的servlet去处理
3.listener节点描述的spring mvc的启动类,ContextLoaderListener被定义为一个监听器,负责完成IoC容器在web环境中的启动
4.context-param节点描述的是spring的bean配置文件
5.servlet的load-on-startup作用:
关于load-on-startup的官方描述
Servlet specification: The load-on-startup element indicates that this servlet should be loaded (instantiated and have its init() called) on the startup of the web application. The optional contents of these element must be an integer indicating the order in which the servlet should be loaded. If the value is a negative integer, or the element is not present, the container is free to load the servlet whenever it chooses. If the value is a positive integer or 0, the container must load and initialize the servlet as the application is deployed. The container must guarantee that servlets marked with lower integers are loaded before servlets marked with higher integers. The container may choose the order of loading of servlets with the same load-on-start-up value
load-on-startup指明当web应用启动的时候,初始化servlet(调用init()方法),
1)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
2)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载;
3)值越小,该servlet的优先级越高,应用启动时就越先加载;
二、需要在bean定义中配置web请求和Controller的对应关系,例如常用的通过RequestMapping注解来定义web请求和Controller的对应关系
@RequestMapping("/action")
public ModelAndView action(
......
)
spring mvc源码解析:
spring mvc主要涉及2个模块
- spring-web
- spring-webmvc
mvc核心概念
- view:视图
- model:模型
- controller:控制器
spring mvc核心实体类/接口
- View:视图
- Handler:对应mvc中的控制器
- HandlerAdapter:handler的调度器
- DispatcherServlet:http的请求分发处理器
- ModelAndView:handler返回的model和view对象
spring mvc核心类源码解读:
- ContextLoaderListener:
源码位置:
决定第一个讲ContextLoaderListener,是因为ContextLoaderListener负责在项目启动时扫描spring的Bean配置(比如applicationContext.xml),加载Bean的定义到IoC容器中去,是spring mvc应用的基础
ContextLoaderListener的源码如下:
/**
* Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
* Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
*
* <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
* application context via the {@link #ContextLoaderListener(WebApplicationContext)}
* constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
* See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 17.02.2003
* @see #setContextInitializers
* @see org.springframework.web.WebApplicationInitializer
*/
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
/**
* Close the root web application context.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
ContextLoaderListener的核心:
1.读类注释可以知道ContextLoaderListener的作用是负责启动和关闭spring的root webapplicationContext 2.ContextLoaderListener继承了ServletContextListener接口,重写了contextInitialized和contextDestroyed方法,这2个方法是和servlet生命周期相结合的回调,所以ServletContextListener的功能之一是对servlet的生命周期做监听 3.继承了ContextLoader类,通过调用ContextLoader类中的initWebApplicationContext()/closeWebApplicationContext()2个方法实现对root webapplicationContext的初始化和销毁
- ContextLoader
initWebApplicationContext()/closeWebApplicationContext()的实现:
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);
}
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);
}
}
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);
}
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;
}
}
public void closeWebApplicationContext(ServletContext servletContext) {
servletContext.log("Closing Spring root WebApplicationContext");
try {
if (this.context instanceof ConfigurableWebApplicationContext) {
((ConfigurableWebApplicationContext) this.context).close();
}
}
finally {
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = null;
}
else if (ccl != null) {
currentContextPerThread.remove(ccl);
}
servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
}