zoukankan      html  css  js  c++  java
  • 《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对象交给特定的视图对象。通过视图对象完毕数据呈现工作。


  • 相关阅读:
    Laravel-RestfulAPI 资源控制器(全面详解)
    Laravel 支付宝SDK在Laravel5的封装
    Laravel 集成 阿里大于 短信接口
    Laravel --进阶篇 (单用户登录)
    Laravel LTS 分割 路由文件的最佳方式
    Laravel -- 实战篇 自制二维码 Simple QrCode
    Laravel 使用 Aliyun OSS 云存储
    Laravel 富文本插件-Ueditor
    Laravel 实战篇
    Laravel 微信开发组件-WeChat 微信支付
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6927216.html
Copyright © 2011-2022 走看看