MVC架构
MVC是模型(model),视图(view),控制器(controller)3个单词的首字母缩写。有些应用需要处理用户请求并操纵和显示数据,MVC模式可以简化其实现。该模式由3个组件组成:
- 模型表示用户期望看到的数据。通常情况下,模型由JavaBean组成。
- 视图负责显示模型。文本编辑器中的视图组件会以恰当的格式显示一段文本,视图在Web应用中会生成客户端浏览器可以解释显示的HTML。
- 控制器表示逻辑代码,负责处理请求和执行用户的意图。它会构建恰当的模型并将其传入视图进行显示。对Java Web应用来说,控制器多是一个servlet。当然,控制器可以使用任意语言实现,只要web容器支持就行。
- <web-appversion="2.5"xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <!-- Processes application requests -->
- <servlet>
- <servlet-name>appServlet</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>appServlet</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- <!-- Disables Servlet Container welcome file handling. Needed for compatibility with Servlet 3.0 and Tomcat 7.0 -->
- <welcome-file-list>
- <welcome-file></welcome-file>
- </welcome-file-list>
- </web-app>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- Processes application requests --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- Disables Servlet Container welcome file handling. Needed for compatibility with Servlet 3.0 and Tomcat 7.0 --> <welcome-file-list> <welcome-file></welcome-file> </welcome-file-list> </web-app>
- package org.springframework.samples.mvc.simple;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
- @Controller
- publicclass SimpleController {
- @RequestMapping("/simple")
- @ResponseBody
- public String simple() {
- return"Hello world!";
- }
- }
package org.springframework.samples.mvc.simple; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class SimpleController { @RequestMapping("/simple") @ResponseBody public String simple() { return "Hello world!"; } }
- INFO: Mapped URL path [/simple] onto handler 'simpleController'
- INFO: Mapped URL path [/simple.*] onto handler 'simpleController'
- INFO: Mapped URL path [/simple/] onto handler 'simpleController'
INFO: Mapped URL path [/simple] onto handler 'simpleController' INFO: Mapped URL path [/simple.*] onto handler 'simpleController' INFO: Mapped URL path [/simple/] onto handler 'simpleController'
- @ResponseBody注解和@RequestBody相似。这个注解可以放在方法上,表明返回的类型应该是可以直接写入到HTTP响应体里的(Model中不会放置该值,该值也不会被转成一个view名字)。
@ResponseBody注解和@RequestBody相似。这个注解可以放在方法上,表明返回的类型应该是可以直接写入到HTTP响应体里的(Model中不会放置该值,该值也不会被转成一个view名字)。
- <servlet>
- <servlet-name>appServlet</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
- publicclass DispatcherServlet extends FrameworkServlet
public class DispatcherServlet extends FrameworkServlet
- publicabstractclass FrameworkServlet extends HttpServletBean
public abstract class FrameworkServlet extends HttpServletBean
- publicabstractclass HttpServletBean extends HttpServlet implements EnvironmentAware
public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware
可见DispatcherServlet是继承了javax.servlet.http.HttpServlet的一个Servlet,既然是一个Servlet,则必然有
- @Override
- publicfinalvoid init() throws ServletException;
- @Override
- protectedfinalvoid doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException;
- @Override
- protectedfinalvoid doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException;
- @Override
- protectedvoid doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
@Override public final void init() throws ServletException; @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
这几个方法的实现,我们一个一个看:
init()方法在HttpServletBean中实现,doGet和doPost方法在FrameworkServlet中实现,doService()方法在DispatcherServlet中实现。
根据Servlet规范,一个Servlet启动的时候需要执行init()方法,一个请求来了首先判断是Get还Post方式提交,Get方式提交的执行doGet()方法,Post方式提交的执行doPost()方法。然后在doGet()和doPost()方法中可调用doService()方法,SpringMVC完全参照此规范。
在init()中,HttpServletBean执行
- protectedvoid initServletBean() throws ServletException {
- }
protected void initServletBean() throws ServletException { }
讲具体的实现交给FrameworkServlt重载的方法完成
- @Override
- protectedfinalvoid initServletBean() throws ServletException {
- System.out.println("使用servlet上下文记录日志");
- getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
- if (this.logger.isInfoEnabled()) {
- this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
- }
- long startTime = System.currentTimeMillis();
- System.out.println("启动时间:[" + startTime + "]");
- try {
- System.out.println("initWebApplicationContext()");
- this.webApplicationContext = initWebApplicationContext();
- System.out.println("initFrameworkServlet(),这是一个空实现");
- initFrameworkServlet();
- }
- catch (ServletException ex) {
- this.logger.error("Context initialization failed", ex);
- throw ex;
- }
- catch (RuntimeException ex) {
- this.logger.error("Context initialization failed", ex);
- throw ex;
- }
- if (this.logger.isInfoEnabled()) {
- long elapsedTime = System.currentTimeMillis() - startTime;
- System.out.println("结束时间:[" + elapsedTime + "]");
- this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
- elapsedTime + " ms");
- }
- }
@Override protected final void initServletBean() throws ServletException { System.out.println("使用servlet上下文记录日志"); getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); System.out.println("启动时间:[" + startTime + "]"); try { System.out.println("initWebApplicationContext()"); this.webApplicationContext = initWebApplicationContext(); System.out.println("initFrameworkServlet(),这是一个空实现"); initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; System.out.println("结束时间:[" + elapsedTime + "]"); this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
该方法会记录启动耗费的时间,也就时我们在启动项目时在控制台看到的时间,这里还解释了,为什么输出启动耗时的日志一般是红色的。
在该方法中执行了initWebApplicationContext()方法,进入该方法
- protected WebApplicationContext initWebApplicationContext() {
- //System.out.println("使用WebApplicationContextUtils工具类得到WebApplicatonContext");
- WebApplicationContext rootContext =
- WebApplicationContextUtils.getWebApplicationContext(getServletContext());
- WebApplicationContext wac = null;
- //System.out.println("判断webApplicationContext是否为null");
- if (this.webApplicationContext != null) {
- //System.out.println("webApplicationContext非空!");
- // A context instance was injected at construction time -> use it
- wac = this.webApplicationContext;
- if (wac instanceof ConfigurableWebApplicationContext) {
- ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
- 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 -> set
- // the root application context (if any; may be null) as the parent
- cwac.setParent(rootContext);
- }
- configureAndRefreshWebApplicationContext(cwac);
- }
- }
- }
- //System.out.println("判断wac是否为null");
- if (wac == null) {
- //System.out.println("was为空");
- // No context instance was injected at construction time -> see if one
- // has been registered in the servlet context. If one exists, it is assumed
- // that the parent context (if any) has already been set and that the
- // user has performed any initialization such as setting the context id
- wac = findWebApplicationContext();
- }
- //System.out.println("再次判断wac是否为null");
- if (wac == null) {
- System.out.println("wac还为空");
- System.out.println("No context instance is defined for this servlet -> create a local one");
- // No context instance is defined for this servlet -> create a local one
- wac = createWebApplicationContext(rootContext);
- }
- System.out.println("this.refreshEventReceived:[" + this.refreshEventReceived + "]");
- if (!this.refreshEventReceived) {
- System.out.println("onRefresh");
- // Either the context is not a ConfigurableApplicationContext with refresh
- // support or the context injected at construction time had already been
- // refreshed -> trigger initial onRefresh manually here.
- onRefresh(wac);
- }
- System.out.println("this.publishContext:[" + this.publishContext + "]");
- if (this.publishContext) {
- System.out.println("Publish the context as a servlet context attribute.");
- // Publish the context as a servlet context attribute.
- String attrName = getServletContextAttributeName();
- getServletContext().setAttribute(attrName, wac);
- if (this.logger.isDebugEnabled()) {
- this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
- "' as ServletContext attribute with name [" + attrName + "]");
- }
- }
- return wac;
- }
protected WebApplicationContext initWebApplicationContext() { //System.out.println("使用WebApplicationContextUtils工具类得到WebApplicatonContext"); WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; //System.out.println("判断webApplicationContext是否为null"); if (this.webApplicationContext != null) { //System.out.println("webApplicationContext非空!"); // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; 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 -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } //System.out.println("判断wac是否为null"); if (wac == null) { //System.out.println("was为空"); // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } //System.out.println("再次判断wac是否为null"); if (wac == null) { System.out.println("wac还为空"); System.out.println("No context instance is defined for this servlet -> create a local one"); // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } System.out.println("this.refreshEventReceived:[" + this.refreshEventReceived + "]"); if (!this.refreshEventReceived) { System.out.println("onRefresh"); // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac); } System.out.println("this.publishContext:[" + this.publishContext + "]"); if (this.publishContext) { System.out.println("Publish the context as a servlet context attribute."); // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
发现很长,但其实有用的只有wac = createWebApplicationContext(rootContext);在进入这个方法
- protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
- System.out.println("createWebApplicationContext");
- 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)) {
- thrownew ApplicationContextException(
- "Fatal initialization error in servlet with name '" + getServletName() +
- "': custom WebApplicationContext class [" + contextClass.getName() +
- "] is not of type ConfigurableWebApplicationContext");
- }
- System.out.println("初始化Bean开始");
- ConfigurableWebApplicationContext wac =
- (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
- System.out.println("初始化Bean结束");
- wac.setParent(parent);
- wac.setConfigLocation(getContextConfigLocation());
- System.out.println("configureAndRefreshWebApplicationContext开始");
- configureAndRefreshWebApplicationContext(wac);
- System.out.println("configureAndRefreshWebApplicationContext结束");
- return wac;
- }
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { System.out.println("createWebApplicationContext"); 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"); } System.out.println("初始化Bean开始"); ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); System.out.println("初始化Bean结束"); wac.setParent(parent); wac.setConfigLocation(getContextConfigLocation()); System.out.println("configureAndRefreshWebApplicationContext开始"); configureAndRefreshWebApplicationContext(wac); System.out.println("configureAndRefreshWebApplicationContext结束"); return wac; }
于是我们就看到了有点眼熟的
- ConfigurableWebApplicationContext wac =
- (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
这个方法就开始初始化我们servlet上下文即spring配置文件中的bean了。
我们的配置文件中servlet-context.xml只要一行
- <beans:importresource="controllers.xml"/>
<beans:import resource="controllers.xml" />
而controllers.xml中
- <context:component-scanbase-package="org.springframework.samples.mvc"/>
<context:component-scan base-package="org.springframework.samples.mvc" />
含义是扫描指定包下面所有带有注解的类,并将他们初始化。上面已经列出了带有@Controller注解的SimpleController类,于是Spring默认将这个bean的名字命名为simpleController(类名首字母小写),然后发现有一个方法带有@RequestMapping,Spring便通过org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping创建URL和控制器的映射(handlerMapping),具体过程可看如下日志:
- DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Looking for URL mappings in application context: WebApplicationContext for namespace 'appServlet-servlet': startup date [Fri Mar 1605:36:03 CST 2012]; root of context hierarchy
- DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'
- 将一个URL路径映射到一个handler上
- INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple] onto handler 'simpleController'
- DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'
- 将一个URL路径映射到一个handler上
- INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple.*] onto handler 'simpleController'
- DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'
- 将一个URL路径映射到一个handler上
- INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple/] onto handler 'simpleController'
DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Looking for URL mappings in application context: WebApplicationContext for namespace 'appServlet-servlet': startup date [Fri Mar 16 05:36:03 CST 2012]; root of context hierarchy DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController' 将一个URL路径映射到一个handler上 INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple] onto handler 'simpleController' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController' 将一个URL路径映射到一个handler上 INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple.*] onto handler 'simpleController' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController' 将一个URL路径映射到一个handler上 INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple/] onto handler 'simpleController'
URL到控制器的映射创建完了,剩下的便是处理器代理了(handlerAdapter),前文提到了,DispatcherServlet有一个List类型的handlerAdapters,如果为null,系统将使用默认策略。这个默认策略体现在DispatcherServlet的这个方法中
- /**
- * Initialize the HandlerAdapters used by this class.
- * <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
- * we default to SimpleControllerHandlerAdapter.
- */
- privatevoid initHandlerAdapters(ApplicationContext context) {
- this.handlerAdapters = null;
- if (this.detectAllHandlerAdapters) {
- // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
- Map<String, HandlerAdapter> matchingBeans =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
- if (!matchingBeans.isEmpty()) {
- this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
- // We keep HandlerAdapters in sorted order.
- OrderComparator.sort(this.handlerAdapters);
- }
- }
- else {
- try {
- HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
- this.handlerAdapters = Collections.singletonList(ha);
- }
- catch (NoSuchBeanDefinitionException ex) {
- // Ignore, we'll add a default HandlerAdapter later.
- }
- }
- // Ensure we have at least some HandlerAdapters, by registering
- // default HandlerAdapters if no other adapters are found.
- if (this.handlerAdapters == null) {
- System.out.println("确保我们有一些handlerAdapters");
- this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
- if (logger.isDebugEnabled()) {
- logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
- }
- }
- }
/** * Initialize the HandlerAdapters used by this class. * <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace, * we default to SimpleControllerHandlerAdapter. */ private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts. Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values()); // We keep HandlerAdapters in sorted order. OrderComparator.sort(this.handlerAdapters); } } else { try { HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerAdapter later. } } // Ensure we have at least some HandlerAdapters, by registering // default HandlerAdapters if no other adapters are found. if (this.handlerAdapters == null) { System.out.println("确保我们有一些handlerAdapters"); this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default"); } } }
这个默认策略会给我们三个HandlerAdapter,具体日志:
- DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter'
- DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter'
- DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter'
DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter'
至此,init()可以算是执行完了(当然为了简化,还有一些无关大局的细节略去没有讲),我们的Servlet,也就是项目就运行起来了。
下面讲解请求部分
先来看一段浏览器发出请求后,服务端的记录的日志
- FrameworkServlet doGet
- TRACE: org.springframework.web.servlet.DispatcherServlet - Bound request context to thread: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116
- doService
- DEBUG: org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'appServlet' processing GET request for [/web/simple]
- doDispatch
- Determine handler for the current request.
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet'
- TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple]
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet'
- DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor
- Determine handler adapter for the current request.
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb]
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298]
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7]
- Process last-modified header, if supported by the handler.
- DEBUG: org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/web/simple] is: -1
- Apply preHandle methods of registered interceptors.
- Actually invoke the handler.
- class org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
- invokeHandlerMethod
- 真正处理
- DEBUG: org.springframework.web.bind.annotation.support.HandlerMethodInvoker - Invoking request handler method: public java.lang.String org.springframework.samples.mvc.simple.SimpleController.simple()
- result:[Hello world!]
- getModelAndView[class java.lang.String]
- 原来进入了这个分支
- OK搞定
- 具体输出数据
- DEBUG: org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter - Written [Hello world!] as "text/html" using [org.springframework.http.converter.StringHttpMessageConverter@6b7de5ce]
- 返回mv为空,假设handler已经处理完了
- Do we need view name translation?
- Apply postHandle methods of registered interceptors.
- DEBUG: org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'appServlet': assuming HandlerAdapter completed request handling
- Trigger after-completion for successful outcome.
- [1]
- TRACE: org.springframework.web.servlet.DispatcherServlet - Cleared thread-bound request context: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116
- DEBUG: org.springframework.web.servlet.DispatcherServlet - Successfully completed request
- TRACE: org.springframework.web.context.support.XmlWebApplicationContext - Publishing event in WebApplicationContext for namespace 'appServlet-servlet': ServletRequestHandledEvent: url=[/web/simple]; client=[0:0:0:0:0:0:0:1%0]; method=[GET]; servlet=[appServlet]; session=[null]; user=[null]; time=[154ms]; status=[OK]
FrameworkServlet doGet TRACE: org.springframework.web.servlet.DispatcherServlet - Bound request context to thread: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116 doService DEBUG: org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'appServlet' processing GET request for [/web/simple] doDispatch Determine handler for the current request. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet' TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet' DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor Determine handler adapter for the current request. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7] Process last-modified header, if supported by the handler. DEBUG: org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/web/simple] is: -1 Apply preHandle methods of registered interceptors. Actually invoke the handler. class org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter invokeHandlerMethod 真正处理 DEBUG: org.springframework.web.bind.annotation.support.HandlerMethodInvoker - Invoking request handler method: public java.lang.String org.springframework.samples.mvc.simple.SimpleController.simple() result:[Hello world!] getModelAndView[class java.lang.String] 原来进入了这个分支 OK搞定 具体输出数据 DEBUG: org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter - Written [Hello world!] as "text/html" using [org.springframework.http.converter.StringHttpMessageConverter@6b7de5ce] 返回mv为空,假设handler已经处理完了 Do we need view name translation? Apply postHandle methods of registered interceptors. DEBUG: org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'appServlet': assuming HandlerAdapter completed request handling Trigger after-completion for successful outcome. [1] TRACE: org.springframework.web.servlet.DispatcherServlet - Cleared thread-bound request context: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116 DEBUG: org.springframework.web.servlet.DispatcherServlet - Successfully completed request TRACE: org.springframework.web.context.support.XmlWebApplicationContext - Publishing event in WebApplicationContext for namespace 'appServlet-servlet': ServletRequestHandledEvent: url=[/web/simple]; client=[0:0:0:0:0:0:0:1%0]; method=[GET]; servlet=[appServlet]; session=[null]; user=[null]; time=[154ms]; status=[OK]
我们来详细分析一下,请求上来时,首先进入FrameworkServlet的doGet方法,然后调用
- /**
- * Process this request, publishing an event regardless of the outcome.
- * <p>The actual event handling is performed by the abstract
- * {@link #doService} template method.
- */
- protectedfinalvoid processRequest(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- long startTime = System.currentTimeMillis();
- Throwable failureCause = null;
- // Expose current LocaleResolver and request as LocaleContext.
- LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
- LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
- // Expose current RequestAttributes to current thread.
- RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
- ServletRequestAttributes requestAttributes = null;
- if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
- requestAttributes = new ServletRequestAttributes(request);
- RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
- }
- if (logger.isTraceEnabled()) {
- logger.trace("Bound request context to thread: " + request);
- }
- try {
- doService(request, response);
- }
- catch (ServletException ex) {
- failureCause = ex;
- throw ex;
- }
- catch (IOException ex) {
- failureCause = ex;
- throw ex;
- }
- catch (Throwable ex) {
- failureCause = ex;
- thrownew NestedServletException("Request processing failed", ex);
- }
- finally {
- // Clear request attributes and reset thread-bound context.
- LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
- if (requestAttributes != null) {
- RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
- requestAttributes.requestCompleted();
- }
- if (logger.isTraceEnabled()) {
- logger.trace("Cleared thread-bound request context: " + request);
- }
- if (logger.isDebugEnabled()) {
- if (failureCause != null) {
- this.logger.debug("Could not complete request", failureCause);
- }
- else {
- this.logger.debug("Successfully completed request");
- }
- }
- if (this.publishEvents) {
- // Whether or not we succeeded, publish an event.
- long processingTime = System.currentTimeMillis() - startTime;
- this.webApplicationContext.publishEvent(
- new ServletRequestHandledEvent(this,
- request.getRequestURI(), request.getRemoteAddr(),
- request.getMethod(), getServletConfig().getServletName(),
- WebUtils.getSessionId(request), getUsernameForRequest(request),
- processingTime, failureCause));
- }
- }
- }
/** * Process this request, publishing an event regardless of the outcome. * <p>The actual event handling is performed by the abstract * {@link #doService} template method. */ protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; // Expose current LocaleResolver and request as LocaleContext. LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable); // Expose current RequestAttributes to current thread. RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = null; if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) { requestAttributes = new ServletRequestAttributes(request); RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } if (logger.isTraceEnabled()) { logger.trace("Bound request context to thread: " + request); } try { doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { // Clear request attributes and reset thread-bound context. LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable); if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable); requestAttributes.requestCompleted(); } if (logger.isTraceEnabled()) { logger.trace("Cleared thread-bound request context: " + request); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { this.logger.debug("Successfully completed request"); } } if (this.publishEvents) { // Whether or not we succeeded, publish an event. long processingTime = System.currentTimeMillis() - startTime; this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause)); } } }
该方法很杂,其他的不要看,就看调用了doService方法即可。此doService由DispatcherServlet重写
- @Override
- protectedvoid doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
- System.out.println("doService");
- if (logger.isDebugEnabled()) {
- String requestUri = urlPathHelper.getRequestUri(request);
- logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
- " request for [" + requestUri + "]");
- }
- // Keep a snapshot of the request attributes in case of an include,
- // to be able to restore the original attributes after the include.
- Map<String, Object> attributesSnapshot = null;
- if (WebUtils.isIncludeRequest(request)) {
- logger.debug("Taking snapshot of request attributes before include");
- attributesSnapshot = new HashMap<String, Object>();
- Enumeration<?> attrNames = request.getAttributeNames();
- while (attrNames.hasMoreElements()) {
- String attrName = (String) attrNames.nextElement();
- if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
- attributesSnapshot.put(attrName, request.getAttribute(attrName));
- }
- }
- }
- this.flashMapManager.requestStarted(request);
- // Make framework objects available to handlers and view objects.
- request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
- request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
- request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
- request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
- try {
- doDispatch(request, response);
- }
- finally {
- this.flashMapManager.requestCompleted(request);
- // Restore the original attribute snapshot, in case of an include.
- if (attributesSnapshot != null) {
- restoreAttributesAfterInclude(request, attributesSnapshot);
- }
- }
- }
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("doService"); if (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() + " request for [" + requestUri + "]"); } // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { logger.debug("Taking snapshot of request attributes before include"); attributesSnapshot = new HashMap<String, Object>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } this.flashMapManager.requestStarted(request); // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); try { doDispatch(request, response); } finally { this.flashMapManager.requestCompleted(request); // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }
其他不要看,就看调用了doDispatch方法
- protectedvoid doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- System.out.println("doDispatch");
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- int interceptorIndex = -1;
- try {
- ModelAndView mv;
- boolean errorView = false;
- try {
- processedRequest = checkMultipart(request);
- // Determine handler for the current request.
- System.out.println("Determine handler for the current request.");
- mappedHandler = getHandler(processedRequest, false);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- // Determine handler adapter for the current request.
- System.out.println("Determine handler adapter for the current request.");
- HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- // Process last-modified header, if supported by the handler.
- System.out.println("Process last-modified header, if supported by the handler.");
- String method = request.getMethod();
- boolean isGet = "GET".equals(method);
- if (isGet || "HEAD".equals(method)) {
- long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
- if (logger.isDebugEnabled()) {
- String requestUri = urlPathHelper.getRequestUri(request);
- logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- // Apply preHandle methods of registered interceptors.
- System.out.println("Apply preHandle methods of registered interceptors.");
- HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
- if (interceptors != null) {
- for (int i = 0; i < interceptors.length; i++) {
- HandlerInterceptor interceptor = interceptors[i];
- if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- return;
- }
- interceptorIndex = i;
- }
- }
- // Actually invoke the handler.
- System.out.println("Actually invoke the handler.");
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- System.out.println("返回mv为空,假设handler已经处理完了");
- // Do we need view name translation?
- System.out.println("Do we need view name translation?");
- if (mv != null && !mv.hasView()) {
- mv.setViewName(getDefaultViewName(request));
- }
- // Apply postHandle methods of registered interceptors.
- System.out.println("Apply postHandle methods of registered interceptors.");
- if (interceptors != null) {
- for (int i = interceptors.length - 1; i >= 0; i--) {
- HandlerInterceptor interceptor = interceptors[i];
- interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
- }
- }
- }
- catch (ModelAndViewDefiningException ex) {
- logger.debug("ModelAndViewDefiningException encountered", ex);
- mv = ex.getModelAndView();
- }
- catch (Exception ex) {
- Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
- mv = processHandlerException(processedRequest, response, handler, ex);
- errorView = (mv != null);
- }
- // Did the handler return a view to render?
- if (mv != null && !mv.wasCleared()) {
- System.out.println("执行render了");
- render(mv, processedRequest, response);
- if (errorView) {
- WebUtils.clearErrorRequestAttributes(request);
- }
- }
- else {
- if (logger.isDebugEnabled()) {
- logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
- "': assuming HandlerAdapter completed request handling");
- }
- }
- // Trigger after-completion for successful outcome.
- System.out.println("Trigger after-completion for successful outcome.");
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- }
- catch (Exception ex) {
- // Trigger after-completion for thrown exception.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
- throw ex;
- }
- catch (Error err) {
- ServletException ex = new NestedServletException("Handler processing failed", err);
- // Trigger after-completion for thrown exception.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
- throw ex;
- }
- finally {
- // Clean up any resources used by a multipart request.
- if (processedRequest != request) {
- cleanupMultipart(processedRequest);
- }
- }
- }
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("doDispatch"); HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; try { ModelAndView mv; boolean errorView = false; try { processedRequest = checkMultipart(request); // Determine handler for the current request. System.out.println("Determine handler for the current request."); mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. System.out.println("Determine handler adapter for the current request."); HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. System.out.println("Process last-modified header, if supported by the handler."); String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // Apply preHandle methods of registered interceptors. System.out.println("Apply preHandle methods of registered interceptors."); HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } // Actually invoke the handler. System.out.println("Actually invoke the handler."); mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); System.out.println("返回mv为空,假设handler已经处理完了"); // Do we need view name translation? System.out.println("Do we need view name translation?"); if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } // Apply postHandle methods of registered interceptors. System.out.println("Apply postHandle methods of registered interceptors."); if (interceptors != null) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); } } } catch (ModelAndViewDefiningException ex) { logger.debug("ModelAndViewDefiningException encountered", ex); mv = ex.getModelAndView(); } catch (Exception ex) { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(processedRequest, response, handler, ex); errorView = (mv != null); } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { System.out.println("执行render了"); render(mv, processedRequest, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } // Trigger after-completion for successful outcome. System.out.println("Trigger after-completion for successful outcome."); triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); } catch (Exception ex) { // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } catch (Error err) { ServletException ex = new NestedServletException("Handler processing failed", err); // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } finally { // Clean up any resources used by a multipart request. if (processedRequest != request) { cleanupMultipart(processedRequest); } } }
这里才是最精髓的,代码
- System.out.println("Determine handler for the current request.");
System.out.println("Determine handler for the current request.");
对应日志部分
- Determine handler for the current request.
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet'
- TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple]
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet'
- DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor
Determine handler for the current request. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet' TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet' DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor
代码
- Determine handler adapter for the current request.
Determine handler adapter for the current request.
对应日志部分
- Determine handler adapter for the current request.
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb]
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298]
- TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7]
Determine handler adapter for the current request. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7]
handlerMapping测试了BeanNameUrlHandlerMapping发现没有,接着找DefaultAmmotationHandlerMapping,有对应的映射,搞定。
handlerAdapter测试了HttpRequestHandlerAdapter不行,SimpleControllerHandlerAdapter不行,最后找到AnnotationMethodHandlerAdapter,有对应关系,搞定。
下面来到这段代码
- System.out.println("Actually invoke the handler.");
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- System.out.println("返回mv为空,假设handler已经处理完了");
System.out.println("Actually invoke the handler."); mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); System.out.println("返回mv为空,假设handler已经处理完了");
注意这里的mappedHandler.geteHandler()就是一个AnnotationMethodHandlerAdapter。
再调用
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- System.out.println(getClass());
- Class<?> clazz = ClassUtils.getUserClass(handler);
- Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
- if (annotatedWithSessionAttributes == null) {
- annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
- this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
- }
- if (annotatedWithSessionAttributes) {
- // Always prevent caching in case of session attribute management.
- checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
- // Prepare cached set of session attributes names.
- }
- else {
- // Uses configured default cacheSeconds setting.
- checkAndPrepare(request, response, true);
- }
- // Execute invokeHandlerMethod in synchronized block if required.
- if (this.synchronizeOnSession) {
- System.out.println("同步");
- HttpSession session = request.getSession(false);
- if (session != null) {
- Object mutex = WebUtils.getSessionMutex(session);
- synchronized (mutex) {
- return invokeHandlerMethod(request, response, handler);
- }
- }
- }
- return invokeHandlerMethod(request, response, handler);
- }
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(getClass()); Class<?> clazz = ClassUtils.getUserClass(handler); Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz); if (annotatedWithSessionAttributes == null) { annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null); this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes); } if (annotatedWithSessionAttributes) { // Always prevent caching in case of session attribute management. checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true); // Prepare cached set of session attributes names. } else { // Uses configured default cacheSeconds setting. checkAndPrepare(request, response, true); } // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { System.out.println("同步"); HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return invokeHandlerMethod(request, response, handler); } } } return invokeHandlerMethod(request, response, handler); }
其他的无视,看invokeHandlerMethod方法
- protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- System.out.println("invokeHandlerMethod");
- ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
- Method handlerMethod = methodResolver.resolveHandlerMethod(request);
- ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
- ServletWebRequest webRequest = new ServletWebRequest(request, response);
- ExtendedModelMap implicitModel = new BindingAwareModelMap();
- Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
- System.out.println("result:[" + result + "]");
- ModelAndView mav =
- methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
- methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
- return mav;
- }
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("invokeHandlerMethod"); ServletHandlerMethodResolver methodResolver = getMethodResolver(handler); Method handlerMethod = methodResolver.resolveHandlerMethod(request); ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver); ServletWebRequest webRequest = new ServletWebRequest(request, response); ExtendedModelMap implicitModel = new BindingAwareModelMap(); Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel); System.out.println("result:[" + result + "]"); ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest); methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest); return mav; }
进入 getModelAndView方法
- public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,
- ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
- System.out.println("getModelAndView[" + returnValue.getClass() + "]");
- ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
- if (responseStatusAnn != null) {
- HttpStatus responseStatus = responseStatusAnn.value();
- String reason = responseStatusAnn.reason();
- if (!StringUtils.hasText(reason)) {
- webRequest.getResponse().setStatus(responseStatus.value());
- }
- else {
- webRequest.getResponse().sendError(responseStatus.value(), reason);
- }
- // to be picked up by the RedirectView
- webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
- responseArgumentUsed = true;
- }
- // Invoke custom resolvers if present...
- if (customModelAndViewResolvers != null) {
- System.out.println("Invoke custom resolvers if present...");
- for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
- ModelAndView mav = mavResolver.resolveModelAndView(
- handlerMethod, handlerType, returnValue, implicitModel, webRequest);
- if (mav != ModelAndViewResolver.UNRESOLVED) {
- return mav;
- }
- }
- }
- if (returnValue instanceof HttpEntity) {
- handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
- returnnull;
- }
- elseif (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
- System.out.println("原来进入了这个分支");
- handleResponseBody(returnValue, webRequest);
- returnnull;
- }
- elseif (returnValue instanceof ModelAndView) {
- ModelAndView mav = (ModelAndView) returnValue;
- mav.getModelMap().mergeAttributes(implicitModel);
- return mav;
- }
- elseif (returnValue instanceof Model) {
- returnnew ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
- }
- elseif (returnValue instanceof View) {
- returnnew ModelAndView((View) returnValue).addAllObjects(implicitModel);
- }
- elseif (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
- addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
- returnnew ModelAndView().addAllObjects(implicitModel);
- }
- elseif (returnValue instanceof Map) {
- returnnew ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
- }
- elseif (returnValue instanceof String) {
- System.out.println("返回值为String");
- returnnew ModelAndView((String) returnValue).addAllObjects(implicitModel);
- }
- elseif (returnValue == null) {
- // Either returned null or was 'void' return.
- if (this.responseArgumentUsed || webRequest.isNotModified()) {
- returnnull;
- }
- else {
- // Assuming view name translation...
- returnnew ModelAndView().addAllObjects(implicitModel);
- }
- }
- elseif (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
- // Assume a single model attribute...
- addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
- returnnew ModelAndView().addAllObjects(implicitModel);
- }
- else {
- thrownew IllegalArgumentException("Invalid handler method return value: " + returnValue);
- }
- }
public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue, ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception { System.out.println("getModelAndView[" + returnValue.getClass() + "]"); ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class); if (responseStatusAnn != null) { HttpStatus responseStatus = responseStatusAnn.value(); String reason = responseStatusAnn.reason(); if (!StringUtils.hasText(reason)) { webRequest.getResponse().setStatus(responseStatus.value()); } else { webRequest.getResponse().sendError(responseStatus.value(), reason); } // to be picked up by the RedirectView webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus); responseArgumentUsed = true; } // Invoke custom resolvers if present... if (customModelAndViewResolvers != null) { System.out.println("Invoke custom resolvers if present..."); for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) { ModelAndView mav = mavResolver.resolveModelAndView( handlerMethod, handlerType, returnValue, implicitModel, webRequest); if (mav != ModelAndViewResolver.UNRESOLVED) { return mav; } } } if (returnValue instanceof HttpEntity) { handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest); return null; } else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) { System.out.println("原来进入了这个分支"); handleResponseBody(returnValue, webRequest); return null; } else if (returnValue instanceof ModelAndView) { ModelAndView mav = (ModelAndView) returnValue; mav.getModelMap().mergeAttributes(implicitModel); return mav; } else if (returnValue instanceof Model) { return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap()); } else if (returnValue instanceof View) { return new ModelAndView((View) returnValue).addAllObjects(implicitModel); } else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) { addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel); return new ModelAndView().addAllObjects(implicitModel); } else if (returnValue instanceof Map) { return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue); } else if (returnValue instanceof String) { System.out.println("返回值为String"); return new ModelAndView((String) returnValue).addAllObjects(implicitModel); } else if (returnValue == null) { // Either returned null or was 'void' return. if (this.responseArgumentUsed || webRequest.isNotModified()) { return null; } else { // Assuming view name translation... return new ModelAndView().addAllObjects(implicitModel); } } else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) { // Assume a single model attribute... addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel); return new ModelAndView().addAllObjects(implicitModel); } else { throw new IllegalArgumentException("Invalid handler method return value: " + returnValue); } }
于是我们看到了熟悉的ResponseBody.class的身影
然后handleResponseBody方法
- privatevoid handleResponseBody(Object returnValue, ServletWebRequest webRequest)
- throws Exception {
- System.out.println("OK搞定");
- if (returnValue == null) {
- return;
- }
- HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
- HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
- writeWithMessageConverters(returnValue, inputMessage, outputMessage);
- }
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest) throws Exception { System.out.println("OK搞定"); if (returnValue == null) { return; } HttpInputMessage inputMessage = createHttpInputMessage(webRequest); HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest); writeWithMessageConverters(returnValue, inputMessage, outputMessage); }
可以看到就是在这个方法中直接将字符串发送到浏览器的。writeWithMessageConverters方法无关紧要就不要列出来了。
呼,我是为了完整性,才将方法整个拷贝下来了,其实我们只需要关注主要的一行调用代码即可。
总结一下?
我觉得原理知道即可,关键还是会用,会搭建。下面讲一下如何搭建这个sample。
老办法,从GitHub上将代码clone下来加载到IDE中。
- git clone git://github.com/stephansun/samples.git
git clone git://github.com/stephansun/samples.git
最后补充一下pom.xml
- <dependencies>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
- <version>3.1.0.RELEASE</version>
- </dependency>
- </dependencies>
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>3.1.0.RELEASE</version> </dependency> </dependencies>
仅仅加载了spring-webmvc,打开m2e的Dependency Hierarchy栏,观察spring模块间的依赖关系:
spring-webmvc
|- spring-asm
|- spring-beans
|- spring-context
|- spring-context-support
|- spring-core
|- spring-expression
|- spring-web
spring-beans
|- spring-core
spring-context
|- spring-aop
|- spring-beans
|- spring-core
|- spring-expression
|- spring-asm
spring-context-support
|- spring-beans
|- spring-context
|- spring-core
spring-core
|- spring-asm
|- commons-logging
spring-expression
|- spring-core
spring-web
|- aoplliance
|- spring-beans
|- spring-context
|- spring-core
Spring发布包
- spring-aop.jar:此JAR文件包含了所有你在应用中使用Spring AOP特性时需要的类。如果应用中使用了其他涉及AOP的Spring功能时,例如声明式事务管理,你也需要将此JAR文件包含进来。
- spring-beans.jar:此文件包含了所有Spring依赖注入需要的代码。包括bean工厂和相关支持类。大部分情况下,你会需要加入spring-context.jar文件,它包含了建立应用环境上下文需要的代码
- spring-context.jar:此JAR文件包含了建立Spring应用环境上下文所需的代码,它将主要的ApplicationContext接口和实现、说明、JNDI、调度、主题和验证一起纳入其中
- spring-context-support.jar:这个包文件包括了Spring的工具代码,其中包括说明、电子邮件、调度支持以及脚本语言支持
- spring-core.jar:此文件包含了Spring框架的核心代码。它用来处理注解、枚举、任务执行、资源加载以及其他一些即便在Spring框架环境外也会有用的工具和异常类。
- spring-jdbc.jar:此文件包含了JDBC支持类的代码,例如JdbcTemplate类和JdbcDaoSupport类
- spring-jms.jar:此文件包含jms的代码
- spring-orm.jar:此文件包含了对象-关系映射(ORM)工具需要的文件。把这个包加入到classpath上将会给你提供针对Hibernate3,iBatis,JDO,JPA和TopLink的Spring支持
- spring-test.jar:此文件包含了使用Spring框架编写单元测试和集成测试的支持代码
- spring-tx.jar:此文件提供了核心的数据访问异常和事务技术支持。这两个概念彼此关系密切,因为一般情况下事务总同某些数据访问代码一起工作的
- spring-web.jar:此文件包含了Spring Web支持(工具类,绑定器,分段文件解析器)的代码
- spring-webmvc.jar:此文件包含了Spring MVC的代码
相关推荐
在 `spring-mvc-helloworld` 项目中,可能会包含一个 `web.xml` 文件来配置 DispatcherServlet。此外,还需要 Spring MVC 的配置文件(如 `servlet-context.xml`),其中定义了 Controller、视图解析器和其他组件的...
Spring3MVC-REST-HelloWorld 是一个基础的示例,用于展示如何在Spring框架的MVC模块中实现RESTful Web服务。这个实例是初学者理解Spring MVC与REST结合使用的理想起点。REST(Representational State Transfer)是一...
Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的一部分,主要用于构建 MVC(Model-View-Controller)模式的 Web 应用程序。在本"spring-mvc helloworld demo"中,我们可以看到如何设置一个...
理解并熟练运用这些基础概念,将为你进一步探索Spring MVC和更复杂的Web应用开发奠定坚实基础。在实际开发中,还需要考虑数据库集成、安全控制、异常处理等更多方面。因此,深入学习和实践Spring MVC是提升Java Web...
《Spring Web MVC初识——HelloWorld项目解析》 在IT领域,Spring框架是Java开发中的一个核心组件,尤其是它的Web MVC模块,为构建基于HTTP的Web应用提供了强大的支持。本篇文章将围绕“HelloWorld”项目,带你深入...
这个"HelloWorld"实例是初学者学习Spring MVC的入门项目,它展示了如何配置、创建和运行一个基本的Spring MVC应用。下面将详细介绍这个实例中的关键知识点。 1. **Spring MVC架构**: Spring MVC遵循模型-视图-...
在“spring-boot-helloworld”项目中,我们看到这是一个入门级的Spring Boot应用,虽然规模不大,但包含了Spring Boot的核心特性,为初学者提供了很好的学习模板。下面我们将深入探讨其中的关键知识点。 1. **...
Spring MVC 是一个强大的Java Web开发框架,用于构建可维护、高性能和灵活的Web应用程序。它作为Spring框架的一部分,提供了一种模型-视图-控制器(MVC)架构,简化了处理HTTP请求和响应的过程。本教程是针对初学者...
在IT领域,Spring MVC作为Spring框架的重要组成部分,是企业级Java应用开发中的主流MVC(Model-View-Controller)框架。本教程将通过逐步指导的方式,帮助开发者深入理解并掌握Spring MVC的核心概念和实践技巧。 一...
同时,Spring MVC还提供了异常处理、国际化、主题装饰等功能,使得Web应用开发更加灵活和强大。 总结来说,"helloworld spring mvc"项目是学习和理解Spring MVC框架的起点,它展示了Spring MVC如何处理HTTP请求,...
**Spring MVC HelloWorld Maven实例** Spring MVC是Spring框架的一部分,它是一个用于构建Web应用程序的模型-视图-控制器(MVC)架构。在这个实例中,我们将深入理解如何使用Maven构建一个基本的“Hello, World!”...
下面我们将深入探讨Spring Boot创建"Hello World"应用的关键知识点。 1. **起步依赖(Starter Dependencies)** Spring Boot的特性之一是其起步依赖,这些是Maven或Gradle的模块,可以自动配置相关的Spring功能。...
为了搭建基于Spring MVC框架和IntelliJ IDEA开发环境的Hello World项目,首先要掌握以下知识点: 1. **Spring MVC框架的理解**: - Spring MVC是Spring框架的一部分,用于构建Web应用程序。 - 它提供了一种MVC...
比如,"spring-boot-starter-web"提供了Web开发所需的所有依赖,包括Spring MVC、Tomcat等。 7. **健康检查与监控**:SpringBoot还内置了Actuator模块,提供了一组端点来监控和管理应用的健康状况,如"/health"端点...
在"SpringMVC-HelloWorld_helloworld_joint2p_"这个项目中,我们将会探索如何构建一个基础的Spring MVC应用,它能够通过网页展示出"HelloWorld"的字样。这个过程涉及到几个关键的知识点,包括Spring MVC的配置、控制...
Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建Web应用程序提供了模型-视图-控制器(MVC)架构。在这个“Spring MVC Helloworld实例”中,我们将会探讨如何利用Spring MVC 3.0.5版本创建一个简单的Hello ...
在这个基于XML配置的Spring MVC HelloWorld实例中,我们将深入理解如何设置并运行一个基本的Spring MVC项目。 首先,Spring MVC的核心在于DispatcherServlet,它是整个应用的入口点,负责处理所有的HTTP请求。在`...
在 "spring-mvc-hello-world-master" 中,你可能会看到一个名为 "spring-servlet.xml" 的配置文件,它包含了 DispatcherServlet 的配置。 **6. 注解驱动开发** Spring MVC 支持注解驱动开发,这使得代码更加简洁。...