《Spring技术内幕》笔记-第四章 Spring MVC与web环境

​上下文在web容器中的启动

1,IoC容器的启动过程

    IoC的启动过程就是建立上下文的过程。该上下文是与ServletContext相伴。在Spring中存在一个核心控制分发器,DispatcherServlet,这是Spring的核心。在web容器启动Spring应用程序时。首先建立根上下文,然后ContextLoader建立WebApplicationContext。

    Web容器中启动Spring步骤例如以下:


    在web.xml中。已经配置了ContextLoadListener,该类实现了ServletLoaderListener接口。

ServletLoaderListener是在Servlet API中定义的接口,接口提供Servlet生命周期。而详细的IoC加载过程是ContextLoadListener交由ContextLoader完毕,ContextLoader是ContextLoadListener的父类。三者关系例如以下:


    在ContextLoader中。完毕了两个IoC建立的基本过程,一是在Web容器中建立起双亲IoC容器,还有一个是生成对应的WebApplicationContext并将其初始化。

2。Web容器中的上下文设计。

    Spring为Web提供了上下文扩展接口WebApplicationContext来满足启动过程的须要。

该接口主要提供getServletContext方法,通过这种方法能够获取容器的Web上下文。


  1. /**

  2. * Return the standard Servlet API ServletContext for this application.

  3. * <p>Also available for a Portlet application, in addition to the PortletContext.

  4. */

  5. ServletContext getServletContext();

在启动过程中,Spring使用XmlWebApplicationContext作为默认的实现。

    XmlWebApplicationContext定义了一系列常量,定义了默认的配置文件,文件位置以及文件类型。

  1. /** Default config location for the root context */

  2. public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

  3. /** Default prefix for building a config location for a namespace */

  4. public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";

  5. /** Default suffix for building a config location for a namespace */

  6. public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";

通时。该类的方法中。定义了通过xml启动IoC的过程:

  1. @Override

  2. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {

  3. // Create a new XmlBeanDefinitionReader for the given BeanFactory.

  4. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

  5. // Configure the bean definition reader with this context's

  6. // resource loading environment.

  7. beanDefinitionReader.setEnvironment(getEnvironment());

  8. beanDefinitionReader.setResourceLoader(this);

  9. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

  10. // Allow a subclass to provide custom initialization of the reader,

  11. // then proceed with actually loading the bean definitions.

  12. initBeanDefinitionReader(beanDefinitionReader);

  13. loadBeanDefinitions(beanDefinitionReader);

  14. }

完毕对配置文件的读取:

  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {

  2. String[] configLocations = getConfigLocations();

  3. if (configLocations != null) {

  4. for (String configLocation : configLocations) {

  5. reader.loadBeanDefinitions(configLocation);

  6. }

  7. }

  8. }

获取配置文件位置。

  1. protected String[] getDefaultConfigLocations() {

  2. if (getNamespace() != null) {

  3. return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};

  4. }

  5. else {

  6. return new String[] {DEFAULT_CONFIG_LOCATION};

  7. }

  8. }


XmlWebApplicationContext类代码中能够看到,该类仅仅是实现了基础的基于XML文件的部分操作。详细的Servlet上下文,则默认取自其父类。

3。ContextLoader的设计与实现

    ContextLoaderListener通过ContextLoader完毕IoC的初始化。

    ContextLoaderListener的详细实现思路例如以下:ContextLoaderListener监听器启动根IoC容器并把它加载到Web容器的主要功能模块,这也是这个SpringWeb应用加载IoC的第一个地方。

从加载过程能够看到,首先从Servelt事件中得到ServletContext,然后能够读取配置在web.xml中的各个相关属性。记着ContextLoader会实例化WebApplicationContext,并完毕其加载和初始化过程。这个被初始化的第一个上下文作为根上下文存在,这个根上下文加载后。被绑定到Web应用程序的ServletCOntext上。怎样须要訪问根上下文的应用程序代码都能够通过WebApplicationContextUtils类的静态方法中得到。该根上下文存储至:ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE


 在ContextLoaderListener中,会监听ServletContext的详细变化,如创建和销毁,并再监听器中定义了对应的回调方法。

  1. public ContextLoaderListener(WebApplicationContext context) {

  2. super(context);

  3. }

  4. /**

  5. * Initialize the root web application context.

  6. */

  7. @Override

  8. public void contextInitialized(ServletContextEvent event) {

  9. initWebApplicationContext(event.getServletContext());

  10. }

  11. /**

  12. * Close the root web application context.

  13. */

  14. @Override

  15. public void contextDestroyed(ServletContextEvent event) {

  16. closeWebApplicationContext(event.getServletContext());

  17. ContextCleanupListener.cleanupAttributes(event.getServletContext());

  18. }


详细的调用。交由ContextLoader实现。例如以下。ContextLoader的初始化容器方法:

  1. /**

  2. * Initialize Spring's web application context for the given servlet context,

  3. * using the application context provided at construction time, or creating a new one

  4. * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and

  5. * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.

  6. * @param servletContext current servlet context

  7. * @return the new WebApplicationContext

  8. * @see #ContextLoader(WebApplicationContext)

  9. * @see #CONTEXT_CLASS_PARAM

  10. * @see #CONFIG_LOCATION_PARAM

  11. */

  12. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

  13. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {//是否已存在根上下文

  14. throw new IllegalStateException(

  15. "Cannot initialize context because there is already a root application context present - " +

  16. "check whether you have multiple ContextLoader* definitions in your web.xml!");

  17. }

  18. Log logger = LogFactory.getLog(ContextLoader.class);

  19. servletContext.log("Initializing Spring root WebApplicationContext");

  20. if (logger.isInfoEnabled()) {

  21. logger.info("Root WebApplicationContext: initialization started");

  22. }

  23. long startTime = System.currentTimeMillis();

  24. try {

  25. // Store context in local instance variable, to guarantee that

  26. // it is available on ServletContext shutdown.

  27. if (this.context == null) {//创建上下文

  28. this.context = createWebApplicationContext(servletContext);

  29. }

  30. if (this.context instanceof ConfigurableWebApplicationContext) {

  31. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;

  32. if (!cwac.isActive()) {

  33. // The context has not yet been refreshed -> provide services such as

  34. // setting the parent context, setting the application context id, etc

  35. if (cwac.getParent() == null) {

  36. // The context instance was injected without an explicit parent ->

  37. // determine parent for root web application context, if any.加载双亲上下文

  38. ApplicationContext parent = loadParentContext(servletContext);

  39. cwac.setParent(parent);

  40. }

  41. //配置各项參数。以及对应的初始化方法

  42. configureAndRefreshWebApplicationContext(cwac, servletContext);

  43. }

  44. }

  45. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

  46. ClassLoader ccl = Thread.currentThread().getContextClassLoader();

  47. if (ccl == ContextLoader.class.getClassLoader()) {

  48. currentContext = this.context;

  49. }

  50. else if (ccl != null) {

  51. currentContextPerThread.put(ccl, this.context);

  52. }

  53. if (logger.isDebugEnabled()) {

  54. logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +

  55. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");

  56. }

  57. if (logger.isInfoEnabled()) {

  58. long elapsedTime = System.currentTimeMillis() - startTime;

  59. logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");

  60. }

  61. return this.context;

  62. }

  63. catch (RuntimeException ex) {

  64. logger.error("Context initialization failed", ex);

  65. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);

  66. throw ex;

  67. }

  68. catch (Error err) {

  69. logger.error("Context initialization failed", err);

  70. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);

  71. throw err;

  72. }

  73. }

createWebApplicationContext是详细的创建根上下文的方法。

  1. protected WebApplicationContext createWebApplicationContext(ServletContext sc) {

  2. Class<?> contextClass = determineContextClass(sc);//Return the WebApplicationContext implementation class to use

  3. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {

  4. throw new ApplicationContextException("Custom context class [" + contextClass.getName() +

  5. "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");

  6. }

  7. return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

  8. }

创建好上下文的后。调用了configureAndRefreshWebApplicationContext方法:


  1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {

  2. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {

  3. // The application context id is still set to its original default value

  4. // -> assign a more useful id based on available information

  5. String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);

  6. if (idParam != null) {

  7. wac.setId(idParam);

  8. }

  9. else {

  10. // Generate default id...

  11. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +

  12. ObjectUtils.getDisplayString(sc.getContextPath()));

  13. }

  14. }

  15. wac.setServletContext(sc);

  16. String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);

  17. if (configLocationParam != null) {

  18. wac.setConfigLocation(configLocationParam);

  19. }

  20. // The wac environment's #initPropertySources will be called in any case when the context

  21. // is refreshed; do it eagerly here to ensure servlet property sources are in place for

  22. // use in any post-processing or initialization that occurs below prior to #refresh

  23. ConfigurableEnvironment env = wac.getEnvironment();

  24. if (env instanceof ConfigurableWebEnvironment) {

  25. ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);

  26. }

  27. customizeContext(sc, wac);

  28. wac.refresh();

  29. }


determineContextClass方法中。详细了详细实现什么上下文对象。默觉得XmlWebApplicationContext。

  1. protected Class<?> determineContextClass(ServletContext servletContext) {

  2. //配置參数

  3. String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);

  4. if (contextClassName != null) {

  5. try {

  6. return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());

  7. }

  8. catch (ClassNotFoundException ex) {

  9. throw new ApplicationContextException(

  10. "Failed to load custom context class [" + contextClassName + "]", ex);

  11. }

  12. }

  13. else {//没有额外配置则採用默认的配置

  14. contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

  15. try {

  16. return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());

  17. }

  18. catch (ClassNotFoundException ex) {

  19. throw new ApplicationContextException(

  20. "Failed to load default context class [" + contextClassName + "]", ex);

  21. }

  22. }

  23. }

以上就是IoC容器在Web容器中的启动过程,与应用中启动IoC基本同样,唯一不同的是须要考虑web容器特点。设置相应web容器须要的參数。

Spring MVC的设计与实现

    Spring MVC出了配置ContextListener之类,还需配置核心分发器DispatcherServlet。DispatcherServlet作为一个前端控制器,全部web请求都要经过他来处理,其对请求进行转发。匹配,数据处理后。再由页面处理。DispatcherServlet是Spring MVC的核心。

1,概览

    完毕ContextLoaderListener初始化之后,Web容器開始初始化DispatcherServlet。DispatcherServlet会建立自己的上下文来持有Spring MVC的Bean对象。在建立自己的上下文时,会从ServletContext中得到根上下文作为DispatcherServlet持有的上下文的双亲上下文。有了根上下文。在对自己持有上下文进行初始化,最后把自己的上下文保存到ServletContex,供以后使用。

    DispatcherServlet继承自FramworkServlet,FramworkServlet继承HttpServletBean。HttpServletBean继承HttpServlet,通过Servlet API对请求对应。

    DispatcherServlet的工作大致分为两个:-1,初始化部分。由initServletBean启动。通过initWebApplicationContext()方法终于抵用DispatcherServlet的initStraegies方法,在这种方法里,DispatcherServlet对MVC的其它模块进行了初始化。比方handlerMapping,ViewResolver。-2,对HTTP请求进行响应。作为一个Servlet,Web容器会调用Servlet的doGet和doPost方法,在经过FraeWorkServlet处理后。会调用DispatcherServlet的doService()方法,这种方法中封装了doDispatch()方法,doDispatch()是DIspatcher实现MVC的主要部分。

2。DispatcherServlet的启动和初始化

    DispatcherServlet的初始化方法的開始。是由HttpServletBean的init()方法開始的。该方法负责获取相应的各项配置參数:

  1. /**

  2. * Map config parameters onto bean properties of this servlet, and

  3. * invoke subclass initialization.

  4. * @throws ServletException if bean properties are invalid (or required

  5. * properties are missing), or if subclass initialization fails.

  6. */

  7. @Override

  8. public final void init() throws ServletException {

  9. if (logger.isDebugEnabled()) {

  10. logger.debug("Initializing servlet '" + getServletName() + "'");

  11. }

  12. // Set bean properties from init parameters.

  13. try {

  14. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

  15. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);

  16. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());

  17. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));

  18. initBeanWrapper(bw);

  19. bw.setPropertyValues(pvs, true);

  20. }

  21. catch (BeansException ex) {

  22. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);

  23. throw ex;

  24. }

  25. // Let subclasses do whatever initialization they like.

  26. initServletBean();

  27. if (logger.isDebugEnabled()) {

  28. logger.debug("Servlet '" + getServletName() + "' configured successfully");

  29. }

  30. }

在该方法中。调用了子类的initServletBean()方法,即FrameworkServlet中的initServletBean()方法:

    

  1. /**

  2. * Overridden method of {@link HttpServletBean}, invoked after any bean properties

  3. * have been set. Creates this servlet's WebApplicationContext.

  4. */

  5. @Override

  6. protected final void initServletBean() throws ServletException {

  7. getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");

  8. if (this.logger.isInfoEnabled()) {

  9. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");

  10. }

  11. long startTime = System.currentTimeMillis();

  12. try {//初始化上下文

  13. this.webApplicationContext = initWebApplicationContext();

  14. initFrameworkServlet();

  15. }

  16. catch (ServletException ex) {

  17. this.logger.error("Context initialization failed", ex);

  18. throw ex;

  19. }

  20. catch (RuntimeException ex) {

  21. this.logger.error("Context initialization failed", ex);

  22. throw ex;

  23. }

  24. if (this.logger.isInfoEnabled()) {

  25. long elapsedTime = System.currentTimeMillis() - startTime;

  26. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +

  27. elapsedTime + " ms");

  28. }

  29. }

该方法中,通过initWebApplicationContext()完毕初始化上下文操作:

  1. protected WebApplicationContext initWebApplicationContext() {

  2. WebApplicationContext rootContext =

  3. WebApplicationContextUtils.getWebApplicationContext(getServletContext());

  4. WebApplicationContext wac = null;

  5. if (this.webApplicationContext != null) {

  6. // A context instance was injected at construction time -> use it

  7. wac = this.webApplicationContext;

  8. if (wac instanceof ConfigurableWebApplicationContext) {

  9. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;

  10. if (!cwac.isActive()) {

  11. // The context has not yet been refreshed -> provide services such as

  12. // setting the parent context, setting the application context id, etc

  13. if (cwac.getParent() == null) {

  14. // The context instance was injected without an explicit parent -> set

  15. // the root application context (if any; may be null) as the parent

  16. cwac.setParent(rootContext);

  17. }

  18. configureAndRefreshWebApplicationContext(cwac);

  19. }

  20. }

  21. }

  22. if (wac == null) {

  23. // No context instance was injected at construction time -> see if one

  24. // has been registered in the servlet context. If one exists, it is assumed

  25. // that the parent context (if any) has already been set and that the

  26. // user has performed any initialization such as setting the context id

  27. wac = findWebApplicationContext();

  28. }

  29. if (wac == null) {

  30. // No context instance is defined for this servlet -> create a local one

  31. wac = createWebApplicationContext(rootContext);

  32. }

  33. if (!this.refreshEventReceived) {

  34. // Either the context is not a ConfigurableApplicationContext with refresh

  35. // support or the context injected at construction time had already been

  36. // refreshed -> trigger initial onRefresh manually here.

  37. onRefresh(wac);

  38. }

  39. if (this.publishContext) {

  40. // Publish the context as a servlet context attribute.

  41. String attrName = getServletContextAttributeName();

  42. getServletContext().setAttribute(attrName, wac);

  43. if (this.logger.isDebugEnabled()) {

  44. this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +

  45. "' as ServletContext attribute with name [" + attrName + "]");

  46. }

  47. }

  48. return wac;

  49. }

通过WebApplicationContextUtils.getWebApplicationContext获取根上下文。

  1. public static WebApplicationContext getWebApplicationContext(ServletContext sc) {

  2. return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

  3. }

  4. public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {

  5. Assert.notNull(sc, "ServletContext must not be null");

  6. Object attr = sc.getAttribute(attrName);

  7. if (attr == null) {

  8. return null;

  9. }

  10. if (attr instanceof RuntimeException) {

  11. throw (RuntimeException) attr;

  12. }

  13. if (attr instanceof Error) {

  14. throw (Error) attr;

  15. }

  16. if (attr instanceof Exception) {

  17. throw new IllegalStateException((Exception) attr);

  18. }

  19. if (!(attr instanceof WebApplicationContext)) {

  20. throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);

  21. }

  22. return (WebApplicationContext) attr;

  23. }

建立好根上下文后,在通过createWebApplicationContext建立DispatcherServlet的上下文。

  1. protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {

  2. Class<?> contextClass = getContextClass();

  3. if (this.logger.isDebugEnabled()) {

  4. this.logger.debug("Servlet with name '" + getServletName() +

  5. "' will try to create custom WebApplicationContext context of class '" +

  6. contextClass.getName() + "'" + ", using parent context [" + parent + "]");

  7. }

  8. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {

  9. throw new ApplicationContextException(

  10. "Fatal initialization error in servlet with name '" + getServletName() +

  11. "': custom WebApplicationContext class [" + contextClass.getName() +

  12. "] is not of type ConfigurableWebApplicationContext");

  13. }

  14. ConfigurableWebApplicationContext wac =

  15. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

  16. wac.setEnvironment(getEnvironment());

  17. wac.setParent(parent);

  18. wac.setConfigLocation(getContextConfigLocation());

  19. configureAndRefreshWebApplicationContext(wac);

  20. return wac;

  21. }

再通过configureAndRefreshWebApplicationContext完毕各项配置以及初始化:

  1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {

  2. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {

  3. // The application context id is still set to its original default value

  4. // -> assign a more useful id based on available information

  5. if (this.contextId != null) {

  6. wac.setId(this.contextId);

  7. }

  8. else {

  9. // Generate default id...

  10. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +

  11. ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());

  12. }

  13. }

  14. wac.setServletContext(getServletContext());

  15. wac.setServletConfig(getServletConfig());

  16. wac.setNamespace(getNamespace());

  17. wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

  18. // The wac environment's #initPropertySources will be called in any case when the context

  19. // is refreshed; do it eagerly here to ensure servlet property sources are in place for

  20. // use in any post-processing or initialization that occurs below prior to #refresh

  21. ConfigurableEnvironment env = wac.getEnvironment();

  22. if (env instanceof ConfigurableWebEnvironment) {

  23. ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());

  24. }

  25. postProcessWebApplicationContext(wac);

  26. applyInitializers(wac);

  27. wac.refresh();//初始化

  28. }

以上完毕了DispatcherServlet的IoC容器的建立。

在Spring MVC DispatcherServlet的初始化过程中。以对HandlerMapping的初始化作为開始。该初始化过程。以HttpServletbean的init方法開始,到FrameWorkServlet的initServletBean,再到DispatcherServlet的onRefresh方法。最后到initStrategies(),启动整个MVC的初始化。

  1. /**

  2. * This implementation calls {@link #initStrategies}.

  3. */

  4. @Override

  5. protected void onRefresh(ApplicationContext context) {

  6. initStrategies(context);

  7. }

  8. /**

  9. * Initialize the strategy objects that this servlet uses.

  10. * <p>May be overridden in subclasses in order to initialize further strategy objects.

  11. */

  12. protected void initStrategies(ApplicationContext context) {

  13. initMultipartResolver(context);

  14. initLocaleResolver(context);

  15. initThemeResolver(context);

  16. initHandlerMappings(context);

  17. initHandlerAdapters(context);

  18. initHandlerExceptionResolvers(context);

  19. initRequestToViewNameTranslator(context);

  20. initViewResolvers(context);

  21. initFlashMapManager(context);

  22. }

HandlerMappings的初始化例如以下:

  1. private void initHandlerMappings(ApplicationContext context) {

  2. this.handlerMappings = null;

  3. if (this.detectAllHandlerMappings) {

  4. // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.

  5. Map<String, HandlerMapping> matchingBeans =

  6. BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

  7. if (!matchingBeans.isEmpty()) {

  8. this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());

  9. // We keep HandlerMappings in sorted order.

  10. OrderComparator.sort(this.handlerMappings);

  11. }

  12. }

  13. else {

  14. try {

  15. HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);

  16. this.handlerMappings = Collections.singletonList(hm);

  17. }

  18. catch (NoSuchBeanDefinitionException ex) {

  19. // Ignore, we'll add a default HandlerMapping later.

  20. }

  21. }

  22. // Ensure we have at least one HandlerMapping, by registering

  23. // a default HandlerMapping if no other mappings are found.

  24. if (this.handlerMappings == null) {

  25. this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);

  26. if (logger.isDebugEnabled()) {

  27. logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");

  28. }

  29. }

  30. }


以上即为HandlerMappings的读取过程。

MVC处理HTTP分发请求

1。HandlerMapping的配置和设计原理    

    在初始化完毕时。在上下文环境中已经定义的全部HandlerMapping都已经被载入了,这些HandlerMapping被放入List并排序。存储着HTTP请求的相应映射数据。Spring提供了一系列HandlerMapping实现。SimpleUrlHandlerMapping为例。在SimpleUrlHandlerMapping中定义了一个Map持有一系列的映射关系,这些映射关系使SpringMVC能够相应到Controller中。

    这些映射关系通过HandlerMapping接口封装。通过getHandler获取相应的HandlerExcutionChain,在HandlerExcutionChain中封装详细的Controller。

    HandlerMapping接口的方法例如以下:

  1. HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

此处是典型的命令模式。

    HandlerExecutionChain中持有一个Inteceptor链和一个handler对象。该handler实际就是Controller对象。


  1. public class HandlerExecutionChain {

  2. private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

  3. private final Object handler;

  4. private HandlerInterceptor[] interceptors;

  5. private List<HandlerInterceptor> interceptorList;

  6. private int interceptorIndex = -1;

构造函数:

  1. /**

  2. * Create a new HandlerExecutionChain.

  3. * @param handler the handler object to execute

  4. * @param interceptors the array of interceptors to apply

  5. * (in the given order) before the handler itself executes

  6. */

  7. public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {

  8. if (handler instanceof HandlerExecutionChain) {

  9. HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;

  10. this.handler = originalChain.getHandler();

  11. this.interceptorList = new ArrayList<HandlerInterceptor>();

  12. CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);

  13. CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);

  14. }

  15. else {

  16. this.handler = handler;

  17. this.interceptors = interceptors;

  18. }

  19. }


HandlerExecutionChain中定义的Handler和Interceptor须要在定义HandlerMapping的时候配置好。

    以SimpleUrlHandlerMapping为例。

在初始化时。会调用。

  1. /**

  2. * Calls the {@link #registerHandlers} method in addition to the

  3. * superclass's initialization.

  4. */

  5. @Override

  6. public void initApplicationContext() throws BeansException {

  7. super.initApplicationContext();

  8. registerHandlers(this.urlMap);

  9. }

  10. /**

  11. * Register all handlers specified in the URL map for the corresponding paths.

  12. * @param urlMap Map with URL paths as keys and handler beans or bean names as values

  13. * @throws BeansException if a handler couldn't be registered

  14. * @throws IllegalStateException if there is a conflicting handler registered

  15. */

  16. protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {

  17. if (urlMap.isEmpty()) {

  18. logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");

  19. }

  20. else {

  21. for (Map.Entry<String, Object> entry : urlMap.entrySet()) {

  22. String url = entry.getKey();

  23. Object handler = entry.getValue();

  24. // Prepend with slash if not already present.

  25. if (!url.startsWith("/")) {

  26. url = "/" + url;

  27. }

  28. // Remove whitespace from handler bean name.

  29. if (handler instanceof String) {

  30. handler = ((String) handler).trim();

  31. }

  32. registerHandler(url, handler);

  33. }

  34. }

  35. }


部分发放基于其父类,AbstractUrlhandlerMapping。

  1. /**

  2. * Register the specified handler for the given URL path.

  3. * @param urlPath the URL the bean should be mapped to

  4. * @param handler the handler instance or handler bean name String

  5. * (a bean name will automatically be resolved into the corresponding handler bean)

  6. * @throws BeansException if the handler couldn't be registered

  7. * @throws IllegalStateException if there is a conflicting handler registered

  8. */

  9. protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {

  10. Assert.notNull(urlPath, "URL path must not be null");

  11. Assert.notNull(handler, "Handler object must not be null");

  12. Object resolvedHandler = handler;

  13. // Eagerly resolve handler if referencing singleton via name.

  14. if (!this.lazyInitHandlers && handler instanceof String) {

  15. String handlerName = (String) handler;

  16. if (getApplicationContext().isSingleton(handlerName)) {

  17. resolvedHandler = getApplicationContext().getBean(handlerName);

  18. }

  19. }

  20. Object mappedHandler = this.handlerMap.get(urlPath);

  21. if (mappedHandler != null) {

  22. if (mappedHandler != resolvedHandler) {

  23. throw new IllegalStateException(

  24. "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +

  25. "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");

  26. }

  27. }

  28. else {

  29. if (urlPath.equals("/")) {

  30. if (logger.isInfoEnabled()) {

  31. logger.info("Root mapping to " + getHandlerDescription(handler));

  32. }

  33. setRootHandler(resolvedHandler);

  34. }

  35. else if (urlPath.equals("/*")) {

  36. if (logger.isInfoEnabled()) {

  37. logger.info("Default mapping to " + getHandlerDescription(handler));

  38. }

  39. setDefaultHandler(resolvedHandler);

  40. }

  41. else {

  42. this.handlerMap.put(urlPath, resolvedHandler);

  43. if (logger.isInfoEnabled()) {

  44. logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));

  45. }

  46. }

  47. }

  48. }

hadnlerMap保存了URL和controller的映射关系。

  1. private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();

2,使用HandlerMapping完毕请求的处理。

    在HandlerExecutionChain的启动过程中,调用了getHandler方法,该方法就是完毕请求映射处理的地方。AbstractHadnlerMapping的getHandler方法例如以下:

  1. /**

  2. * Look up a handler for the given request, falling back to the default

  3. * handler if no specific one is found.

  4. * @param request current HTTP request

  5. * @return the corresponding handler instance, or the default handler

  6. * @see #getHandlerInternal

  7. */

  8. @Override

  9. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

  10. Object handler = getHandlerInternal(request);

  11. if (handler == null) {

  12. handler = getDefaultHandler();

  13. }

  14. if (handler == null) {

  15. return null;

  16. }

  17. // Bean name or resolved handler?

  18. if (handler instanceof String) {

  19. String handlerName = (String) handler;

  20. handler = getApplicationContext().getBean(handlerName);

  21. }

  22. return getHandlerExecutionChain(handler, request);

  23. }

  1. /**

  2. * Build a {@link HandlerExecutionChain} for the given handler, including

  3. * applicable interceptors.

  4. * <p>The default implementation builds a standard {@link HandlerExecutionChain}

  5. * with the given handler, the handler mapping's common interceptors, and any

  6. * {@link MappedInterceptor}s matching to the current request URL. Subclasses

  7. * may override this in order to extend/rearrange the list of interceptors.

  8. * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a

  9. * pre-built {@link HandlerExecutionChain}. This method should handle those

  10. * two cases explicitly, either building a new {@link HandlerExecutionChain}

  11. * or extending the existing chain.

  12. * <p>For simply adding an interceptor in a custom subclass, consider calling

  13. * {@code super.getHandlerExecutionChain(handler, request)} and invoking

  14. * {@link HandlerExecutionChain#addInterceptor} on the returned chain object.

  15. * @param handler the resolved handler instance (never {@code null})

  16. * @param request current HTTP request

  17. * @return the HandlerExecutionChain (never {@code null})

  18. * @see #getAdaptedInterceptors()

  19. */

  20. protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {

  21. HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?

  22. (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

  23. chain.addInterceptors(getAdaptedInterceptors());

  24. String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);

  25. for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {

  26. if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {

  27. chain.addInterceptor(mappedInterceptor.getInterceptor());

  28. }

  29. }

  30. return chain;

  31. }


取得Handler的详细方法在getHandlerInternal()。该方法的详细实如今AbstractUrlHandlerMapping中:

  1. /**

  2. * Look up a handler for the URL path of the given request.

  3. * @param request current HTTP request

  4. * @return the handler instance, or {@code null} if none found

  5. */

  6. @Override

  7. protected Object getHandlerInternal(HttpServletRequest request) throws Exception {

  8. //从request获取请求路径

  9. String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);

  10. //请求路径与Handler匹配

  11. Object handler = lookupHandler(lookupPath, request);

  12. if (handler == null) {

  13. // We need to care for the default handler directly, since we need to

  14. // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.

  15. Object rawHandler = null;

  16. if ("/".equals(lookupPath)) {

  17. rawHandler = getRootHandler();

  18. }

  19. if (rawHandler == null) {

  20. rawHandler = getDefaultHandler();

  21. }

  22. if (rawHandler != null) {

  23. // Bean name or resolved handler?

  24. if (rawHandler instanceof String) {

  25. String handlerName = (String) rawHandler;

  26. rawHandler = getApplicationContext().getBean(handlerName);

  27. }

  28. validateHandler(rawHandler, request);

  29. handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);

  30. }

  31. }

  32. if (handler != null && logger.isDebugEnabled()) {

  33. logger.debug("Mapping [" + lookupPath + "] to " + handler);

  34. }

  35. else if (handler == null && logger.isTraceEnabled()) {

  36. logger.trace("No handler mapping found for [" + lookupPath + "]");

  37. }

  38. return handler;

  39. }



  1. /**

  2. * Look up a handler instance for the given URL path.

  3. * <p>Supports direct matches, e.g. a registered "/test" matches "/test",

  4. * and various Ant-style pattern matches, e.g. a registered "/t*" matches

  5. * both "/test" and "/team". For details, see the AntPathMatcher class.

  6. * <p>Looks for the most exact pattern, where most exact is defined as

  7. * the longest path pattern.

  8. * @param urlPath URL the bean is mapped to

  9. * @param request current HTTP request (to expose the path within the mapping to)

  10. * @return the associated handler instance, or {@code null} if not found

  11. * @see #exposePathWithinMapping

  12. * @see org.springframework.util.AntPathMatcher

  13. */

  14. protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {

  15. // Direct match?

  16. Object handler = this.handlerMap.get(urlPath);

  17. if (handler != null) {

  18. // Bean name or resolved handler?

  19. if (handler instanceof String) {

  20. String handlerName = (String) handler;

  21. handler = getApplicationContext().getBean(handlerName);

  22. }

  23. validateHandler(handler, request);

  24. return buildPathExposingHandler(handler, urlPath, urlPath, null);

  25. }

  26. // Pattern match?

  27. List<String> matchingPatterns = new ArrayList<String>();

  28. for (String registeredPattern : this.handlerMap.keySet()) {

  29. if (getPathMatcher().match(registeredPattern, urlPath)) {

  30. matchingPatterns.add(registeredPattern);

  31. }

  32. }

  33. String bestPatternMatch = null;

  34. Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);

  35. if (!matchingPatterns.isEmpty()) {

  36. Collections.sort(matchingPatterns, patternComparator);

  37. if (logger.isDebugEnabled()) {

  38. logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);

  39. }

  40. bestPatternMatch = matchingPatterns.get(0);

  41. }

  42. if (bestPatternMatch != null) {

  43. handler = this.handlerMap.get(bestPatternMatch);

  44. // Bean name or resolved handler?

  45. if (handler instanceof String) {

  46. String handlerName = (String) handler;

  47. handler = getApplicationContext().getBean(handlerName);

  48. }

  49. validateHandler(handler, request);

  50. String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);

  51. // There might be multiple 'best patterns', let's make sure we have the correct URI template variables

  52. // for all of them

  53. Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();

  54. for (String matchingPattern : matchingPatterns) {

  55. if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {

  56. Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);

  57. Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);

  58. uriTemplateVariables.putAll(decodedVars);

  59. }

  60. }

  61. if (logger.isDebugEnabled()) {

  62. logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);

  63. }

  64. return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);

  65. }

  66. // No handler found...

  67. return null;

  68. }

经过一系列对HTTP请求进行解析和匹配handler的过程,得到了与请求对一个的handler处理器。在返回的handler中。已经完毕了在HandlerExecutionChain中的封装工作,为handler对HTTP请求的响应做好了准备。


3,Spring对HTTP请求的分发处理。

    DispatcherServlet是Servlet的子类,通过doService来响应HTTP请求。


  1. /**

  2. * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}

  3. * for the actual dispatching.

  4. */

  5. @Override

  6. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

  7. if (logger.isDebugEnabled()) {

  8. String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ?

    " resumed" : "";

  9. logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +

  10. " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");

  11. }

  12. // Keep a snapshot of the request attributes in case of an include,

  13. // to be able to restore the original attributes after the include.

  14. Map<String, Object> attributesSnapshot = null;

  15. if (WebUtils.isIncludeRequest(request)) {

  16. attributesSnapshot = new HashMap<String, Object>();

  17. Enumeration<?

    > attrNames = request.getAttributeNames();

  18. while (attrNames.hasMoreElements()) {

  19. String attrName = (String) attrNames.nextElement();

  20. if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {

  21. attributesSnapshot.put(attrName, request.getAttribute(attrName));

  22. }

  23. }

  24. }

  25. // Make framework objects available to handlers and view objects.

  26. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());

  27. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);

  28. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);

  29. request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

  30. FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);

  31. if (inputFlashMap != null) {

  32. request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));

  33. }

  34. request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());

  35. request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

  36. try {

  37. doDispatch(request, response);

  38. }

  39. finally {

  40. if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {

  41. return;

  42. }

  43. // Restore the original attribute snapshot, in case of an include.

  44. if (attributesSnapshot != null) {

  45. restoreAttributesAfterInclude(request, attributesSnapshot);

  46. }

  47. }

  48. }

doDispatch方法:

  1. /**
  2. * Process the actual dispatching to the handler.
  3. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
  4. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
  5. * to find the first that supports the handler class.
  6. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
  7. * themselves to decide which methods are acceptable.
  8. * @param request current HTTP request
  9. * @param response current HTTP response
  10. * @throws Exception in case of any kind of processing failure
  11. */
  12. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  13. HttpServletRequest processedRequest = request;
  14. HandlerExecutionChain mappedHandler = null;
  15. boolean multipartRequestParsed = false;
  16. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  17. try {
  18. ModelAndView mv = null;
  19. Exception dispatchException = null;
  20. try {
  21. processedRequest = checkMultipart(request);
  22. multipartRequestParsed = (processedRequest != request);
  23. // Determine handler for the current request.
  24. mappedHandler = getHandler(processedRequest);
  25. if (mappedHandler == null || mappedHandler.getHandler() == null) {
  26. noHandlerFound(processedRequest, response);
  27. return;
  28. }
  29. // Determine handler adapter for the current request.
  30. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  31. // Process last-modified header, if supported by the handler.
  32. String method = request.getMethod();
  33. boolean isGet = "GET".equals(method);
  34. if (isGet || "HEAD".equals(method)) {
  35. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  36. if (logger.isDebugEnabled()) {
  37. logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
  38. }
  39. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  40. return;
  41. }
  42. }
  43. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  44. return;
  45. }
  46. try {
  47. // Actually invoke the handler.
  48. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  49. }
  50. finally {
  51. if (asyncManager.isConcurrentHandlingStarted()) {
  52. return;
  53. }
  54. }
  55. applyDefaultViewName(request, mv);
  56. mappedHandler.applyPostHandle(processedRequest, response, mv);
  57. }
  58. catch (Exception ex) {
  59. dispatchException = ex;
  60. }
  61. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  62. }
  63. catch (Exception ex) {
  64. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  65. }
  66. catch (Error err) {
  67. triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
  68. }
  69. finally {
  70. if (asyncManager.isConcurrentHandlingStarted()) {
  71. // Instead of postHandle and afterCompletion
  72. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  73. return;
  74. }
  75. // Clean up any resources used by a multipart request.
  76. if (multipartRequestParsed) {
  77. cleanupMultipart(processedRequest);
  78. }
  79. }
  80. }
  1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  2. for (HandlerMapping hm : this.handlerMappings) {
  3. if (logger.isTraceEnabled()) {
  4. logger.trace(
  5. "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
  6. }
  7. HandlerExecutionChain handler = hm.getHandler(request);
  8. if (handler != null) {
  9. return handler;
  10. }
  11. }
  12. return null;
  13. }

  1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  2. for (HandlerAdapter ha : this.handlerAdapters) {
  3. if (logger.isTraceEnabled()) {
  4. logger.trace("Testing handler adapter [" + ha + "]");
  5. }
  6. if (ha.supports(handler)) {
  7. return ha;
  8. }
  9. }
  10. throw new ServletException("No adapter for handler [" + handler +
  11. "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
  12. }

HandlerAdapter的基本实现。SimpleControllerHandlerAddapter。
  1. public class SimpleControllerHandlerAdapter implements HandlerAdapter {
  2. @Override
  3. public boolean supports(Object handler) {
  4. return (handler instanceof Controller);
  5. }
  6. @Override
  7. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  8. throws Exception {
  9. return ((Controller) handler).handleRequest(request, response);
  10. }
  11. @Override
  12. public long getLastModified(HttpServletRequest request, Object handler) {
  13. if (handler instanceof LastModified) {
  14. return ((LastModified) handler).getLastModified(request);
  15. }
  16. return -1L;
  17. }
  18. }

经过上面的处理。获取到Controller对象,開始调用Handler对象的HTTP响应动作。

运行完获取到视图,并将视图返回。

Spring MVC视图的呈现


1,DispatcherServlet视图呈现设计

    在DIspatchServlet的doDispatch()方法中,获取到视图后调用了processDispatchResult()方法处理结果。视图的处理採用render方法。

  1. /**
  2. * Render the given ModelAndView.
  3. * <p>This is the last stage in handling a request. It may involve resolving the view by name.
  4. * @param mv the ModelAndView to render
  5. * @param request current HTTP servlet request
  6. * @param response current HTTP servlet response
  7. * @throws ServletException if view is missing or cannot be resolved
  8. * @throws Exception if there's a problem rendering the view
  9. */
  10. protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
  11. // Determine locale for request and apply it to the response.
  12. Locale locale = this.localeResolver.resolveLocale(request);
  13. response.setLocale(locale);
  14. View view;
  15. if (mv.isReference()) {
  16. // We need to resolve the view name.
  17. view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
  18. if (view == null) {
  19. throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
  20. "' in servlet with name '" + getServletName() + "'");
  21. }
  22. }
  23. else {
  24. // No need to lookup: the ModelAndView object contains the actual View object.
  25. view = mv.getView();
  26. if (view == null) {
  27. throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
  28. "View object in servlet with name '" + getServletName() + "'");
  29. }
  30. }
  31. // Delegate to the View object for rendering.
  32. if (logger.isDebugEnabled()) {
  33. logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
  34. }
  35. try {
  36. view.render(mv.getModelInternal(), request, response);
  37. }
  38. catch (Exception ex) {
  39. if (logger.isDebugEnabled()) {
  40. logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
  41. getServletName() + "'", ex);
  42. }
  43. throw ex;
  44. }
  45. }

resolveViewName通过对视图名称对象解析,获取视图。

  1. protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
  2. HttpServletRequest request) throws Exception {
  3. for (ViewResolver viewResolver : this.viewResolvers) {
  4. View view = viewResolver.resolveViewName(viewName, locale);
  5. if (view != null) {
  6. return view;
  7. }
  8. }
  9. return null;
  10. }

ViewResolver的resolveViewName方法,BeanNameViewresolver是其经常使用实现。


  1. @Override
  2. public View resolveViewName(String viewName, Locale locale) throws BeansException {
  3. ApplicationContext context = getApplicationContext();
  4. if (!context.containsBean(viewName)) {
  5. // Allow for ViewResolver chaining.
  6. return null;
  7. }
  8. return context.getBean(viewName, View.class);
  9. }

Spring为了实现视图的灵活性,方便应用使用各种视图。在View接口下实现了不同View对象。各View对象依据其详细的使用,氛围不同的视图。

2,JSP视图的实现

    使用jsp作为视图,Sprinng採用JstlView来作为View对象,而其render方法继承自父类AbstractView。

  1. /**
  2. * Prepares the view given the specified model, merging it with static
  3. * attributes and a RequestContext attribute, if necessary.
  4. * Delegates to renderMergedOutputModel for the actual rendering.
  5. * @see #renderMergedOutputModel
  6. */
  7. @Override
  8. public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
  9. if (logger.isTraceEnabled()) {
  10. logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
  11. " and static attributes " + this.staticAttributes);
  12. }
  13. Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
  14. prepareResponse(request, response);
  15. renderMergedOutputModel(mergedModel, request, response);
  16. }

createMergedOutputModel方法:

  1. /**
  2. * Creates a combined output Map (never {@code null}) that includes dynamic values and static attributes.
  3. * Dynamic values take precedence over static attributes.
  4. */
  5. protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest request,
  6. HttpServletResponse response) {
  7. @SuppressWarnings("unchecked")
  8. Map<String, Object> pathVars = (this.exposePathVariables ?
  9. (Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);
  10. // Consolidate static and dynamic model attributes.
  11. int size = this.staticAttributes.size();
  12. size += (model != null) ? model.size() : 0;
  13. size += (pathVars != null) ? pathVars.size() : 0;
  14. Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size);
  15. mergedModel.putAll(this.staticAttributes);
  16. if (pathVars != null) {
  17. mergedModel.putAll(pathVars);
  18. }
  19. if (model != null) {
  20. mergedModel.putAll(model);
  21. }
  22. // Expose RequestContext?
  23. if (this.requestContextAttribute != null) {
  24. mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));
  25. }
  26. return mergedModel;
  27. }

renderMergedOutputModel,在InternalResourceView中完毕,InternalResourceView也属于JstlView的基类。

  1. /**
  2. * Render the internal resource given the specified model.
  3. * This includes setting the model as request attributes.
  4. */
  5. @Override
  6. protected void renderMergedOutputModel(
  7. Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
  8. // Determine which request handle to expose to the RequestDispatcher.
  9. HttpServletRequest requestToExpose = getRequestToExpose(request);
  10. // Expose the model object as request attributes.
  11. exposeModelAsRequestAttributes(model, requestToExpose);
  12. // Expose helpers as request attributes, if any.
  13. exposeHelpers(requestToExpose);
  14. // Determine the path for the request dispatcher.
  15. String dispatcherPath = prepareForRendering(requestToExpose, response);
  16. // Obtain a RequestDispatcher for the target resource (typically a JSP).
  17. RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
  18. if (rd == null) {
  19. throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
  20. "]: Check that the corresponding file exists within your web application archive!");
  21. }
  22. // If already included or response already committed, perform include, else forward.
  23. if (useInclude(requestToExpose, response)) {
  24. response.setContentType(getContentType());
  25. if (logger.isDebugEnabled()) {
  26. logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
  27. }
  28. rd.include(requestToExpose, response);
  29. }
  30. else {
  31. // Note: The forwarded resource is supposed to determine the content type itself.
  32. if (logger.isDebugEnabled()) {
  33. logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
  34. }
  35. rd.forward(requestToExpose, response);
  36. }
  37. }

AbstractView的exposeModelAsRequestAttributes()方法:

  1. /**
  2. * Expose the model objects in the given map as request attributes.
  3. * Names will be taken from the model Map.
  4. * This method is suitable for all resources reachable by {@link javax.servlet.RequestDispatcher}.
  5. * @param model Map of model objects to expose
  6. * @param request current HTTP request
  7. */
  8. protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
  9. for (Map.Entry<String, Object> entry : model.entrySet()) {
  10. String modelName = entry.getKey();
  11. Object modelValue = entry.getValue();
  12. if (modelValue != null) {
  13. request.setAttribute(modelName, modelValue);
  14. if (logger.isDebugEnabled()) {
  15. logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
  16. "] to request in view with name '" + getBeanName() + "'");
  17. }
  18. }
  19. else {
  20. request.removeAttribute(modelName);
  21. if (logger.isDebugEnabled()) {
  22. logger.debug("Removed model object '" + modelName +
  23. "' from request in view with name '" + getBeanName() + "'");
  24. }
  25. }
  26. }
  27. }

  1. /**
  2. * Prepare for rendering, and determine the request dispatcher path
  3. * to forward to (or to include).
  4. * <p>This implementation simply returns the configured URL.
  5. * Subclasses can override this to determine a resource to render,
  6. * typically interpreting the URL in a different manner.
  7. * @param request current HTTP request
  8. * @param response current HTTP response
  9. * @return the request dispatcher path to use
  10. * @throws Exception if preparations failed
  11. * @see #getUrl()
  12. */
  13. protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
  14. throws Exception {
  15. String path = getUrl();
  16. if (this.preventDispatchLoop) {
  17. String uri = request.getRequestURI();
  18. if (path.startsWith("/") ?

    uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) {

  19. throw new ServletException("Circular view path [" + path + "]: would dispatch back " +
  20. "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " +
  21. "(Hint: This may be the result of an unspecified view, due to default view name generation.)");
  22. }
  23. }
  24. return path;
  25. }

视图的解析过程大概就例如以下代码。



总而言之。Spring MVC的建立有下面几个过程:

    -1。须要建立Controller和HTTP请求之间的映射关系。在HandlerMapping中封装HandlerExecutionChain对象完毕。而对Controller和HTTP请求关系的描写叙述是在Bean定义的描写叙述。并在IoC初始化时。通过初始化HandlerMapping来完毕,这些映射关系存储在handlerMap中。

    -2,在MVC接收请求的时候,DispatcherServlet会依据详细的URL请求信息。在HandlerMapping中查询,从而得到HandlerExecutionChain。在HandlerExecutionChain中封装了Controller,这个请求的COntroller会完毕请求对应,以及生成须要的ModelAndView对象。


    -3。得到ModelAndView对象后,DispatcherServlet将ModelAndView对象交给特定的视图对象。通过视图对象完毕数据呈现工作。


原文地址:https://www.cnblogs.com/claireyuancy/p/6927216.html