当前位置:   article > 正文

Spring MVC源码学习一之初始化

spring 源码controller如何初始化地

一、简单提下使用入门(依赖于注解方式)

1、通过在web.xml中添加配置,引入spring mvc框架,类似于Struts2引入需要在web.xml中配置过滤器StrutsPrepareAndExecuteFilter一样,但是Spring MVC是通过servlet实现的,需要配置一个Servlet。

  1. <servlet>
  2. <servlet-name>dispatcher</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <init-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>classpath:springConfig/springmvx-servlet.xml</param-value>
  7. </init-param>
  8. <load-on-startup>1</load-on-startup>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>dispatcher</servlet-name>
  12. <url-pattern>/</url-pattern>
  13. </servlet-mapping>

2、在springmvc中主要是配置Controller,静态文件的映射策略,视图的配置等(即后面原理要讲的几个组件)。

  1. <!-- 自动扫描的包名 -->
  2. <context:component-scan base-package="com.test"/>
  3. <!-- 默认的注解映射的支持,自动注册RequestMappingHandlerMapping和RequestMappingHandlerAdapter
  4. (spring 3.2以上版本已经不再是DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter)
  5. -->
  6. <mvc:annotation-driven />
  7. <!-- 视图解释类 -->
  8. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  9. <property name="prefix" value="/WEB-INF/jsp/"/>
  10. <property name="suffix" value=".jsp"/>
  11. <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
  12. </bean>

3、编写controller

  1. @Controller
  2. @RequestMapping("/test")
  3. public class IndexController {
  4. @RequestMapping("/index")
  5. public ModelAndView index() {
  6. ModelAndView view = new ModelAndView("index");
  7. view.addObject("welcome", "hello");
  8. return view;
  9. }
  10. }

4、访问地址:xxx/contexpath/test/index。

说明:关于mvc:annotation-driven配置的详细说明可以参考:https://my.oschina.net/HeliosFly/blog/205343

二、先尝试理解原理

Spring的MVC框架主要由DispatcherServlet、处理器映射(HandlerMapping)、处理器(Controller)、视图解析器(ViewResolver)、视图(View)组成。DispatcherServlet是整个Spring MVC的核心。它负责接收HTTP请求组织协调Spring MVC的各个组成部分。

  • 原理图

  • 原理图说明

1、客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),web容器将请求转交给DispatcherServlet.

2、DipatcherServlet接收到这个请求之后将根据请求的信息(包括URL、Http方法、请求报文头和请求参数Cookie等)以及HandlerMapping的配置找到处理请求的处理器(Handler)。

3-4、DispatcherServlet根据HandlerMapping找到对应的Handler,将处理权交给Handler(Handler将具体的处理进行封装),再由具体的HandlerAdapter对Handler进行具体的调用。

5、Handler对数据处理完成以后将返回一个ModelAndView()对象给DispatcherServlet。

6、Handler返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet通过ViewResolver将逻辑视图转化为真正的视图View。

7、Dispatcher通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端。

  • 重要的类说明

DispatcherServlet

Spring提供的前端控制器,所有的请求都有经过它来统一分发。在DispatcherServlet将请求分发给Spring Controller之前,需要借助于Spring提供的HandlerMapping定位到具体的Controller。

HandlerMapping 

Spring mvc 使用HandlerMapping来找到并保存url请求和处理函数间的mapping关系。   
以RequestMappingHandlerMapping为例来具体看HandlerMapping的作用 
DefaultAnnotationHandlerMapping将扫描当前所有已经注册的spring beans中的@requestmapping标注以找出url 和 handler method处理函数的关系并予以关联。 

Handleradapter 

Spring MVC通过HandlerAdapter来实际调用处理函数。 
以RequestMappingHandlerAdapter为例 
DispatcherServlet中根据handlermapping找到对应的handler method后,首先检查当前工程中注册的所有可用的handlerAdapter,根据handlerAdapter中的supports方法找到可以使用的handlerAdapter。通过调用handlerAdapter中的handle方法来处理及准备handler method中的参数及annotation(这就是spring mvc如何将reqeust中的参数变成handle method中的输入参数的地方),最终调用实际的handle method。 

 

ViewResolver

Spring提供的视图解析器(ViewResolver)在Web应用中查找View对象,从而将相应结果渲染给客户。

 

三、初始化过程

DispatcherServlet的继承体系如:

看到它们继承自HttpServlet,你就知道初始化过程应该是从init方法开始了,整个初始化的流程为:

1、服务器启动的时候,tomcat容器执行init()方法,主要做两个事情:加载web.xml配置的初始化参数、让子类回调同时覆写方法initServletBean()。

  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. // Set bean properties from init parameters.
  10. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  11. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  12. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
  13. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
  14. initBeanWrapper(bw);
  15. bw.setPropertyValues(pvs, true);
  16. // Let subclasses do whatever initialization they like.
  17. initServletBean();
  18. }

2、对于第一点的initServletBean(),由子类FrameworkServlet继承,覆写该方法,因此接下来执行该方法。

  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. this.webApplicationContext = initWebApplicationContext();
  8. //此方法本类没有代码逻辑,由子类继承覆写该方法,
  9. //但是DispatcherServlet并没有覆写,一次此方法没有做任何事。
  10. initFrameworkServlet();
  11. }

第一行代码的作用是:初始化FrameworkServlet的属性webApplicationContext,这个属性代表SpringMVC上下文,它有个父类上下文,既web.xml中配置的ContextLoaderListener监听器初始化的容器上下文,然后提供onRefresh方法给DispatcherServlet覆写。

进入initWebApplicationContext()方法,我们可以发现代码分成两块,一块是获取WebApplicationContext ,另一块是拿到这个类的实例传递给onfresh()方法。对于onRefresh方法本类并没有任何的逻辑,只是提供了一个模版供子类覆写。

  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. }

3、对于第二点提到的onfresh方法,由子类DispatcherServlet继承覆写,因此接下来执行DispatcherServlet类中的onRefresh()方法。

  1. /**
  2. * This implementation calls {@link #initStrategies}.
  3. */
  4. @Override
  5. protected void onRefresh(ApplicationContext context) {
  6. initStrategies(context);
  7. }
  1. protected void initStrategies(ApplicationContext context) {
  2. initMultipartResolver(context);
  3. initLocaleResolver(context);
  4. initThemeResolver(context);
  5. initHandlerMappings(context);
  6. initHandlerAdapters(context);
  7. initHandlerExceptionResolvers(context);
  8. initRequestToViewNameTranslator(context);
  9. initViewResolvers(context);
  10. initFlashMapManager(context);
  11. }

从代码可以清晰地知道,这个方法就是根据拿到的上下文初始化一些列的策略,即初始化什么样的handlerMappings、handlerAdapters等等,这里我们看initHandlerMappings这个方法,简化后的代码如下:

  1. /**
  2. * Initialize the HandlerMappings used by this class.
  3. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
  4. * we default to BeanNameUrlHandlerMapping.
  5. */
  6. private void initHandlerMappings(ApplicationContext context) {
  7. this.handlerMappings = null;
  8. // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
  9. Map<String, HandlerMapping> matchingBeans =
  10. BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
  11. if (!matchingBeans.isEmpty()) {
  12. this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
  13. // We keep HandlerMappings in sorted order.
  14. OrderComparator.sort(this.handlerMappings);
  15. }
  16. // Ensure we have at least one HandlerMapping, by registering
  17. // a default HandlerMapping if no other mappings are found.
  18. if (this.handlerMappings == null) {
  19. this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
  20. }
  21. }

先根据前面获取好的ApplicationContext获取到所有的handlerMappings,然后排序,这里调试后的结果是这里获取到了两个handlerMapping:RequestMappingHandlerMapping、=和BeanNameUrlHandlerMapping,如果获取到的 handlerMappings 集合为空,则按照默认去加载,这个默认的规则,这个默认的规则是配置在DispatcherServlet.properties中,看下图代码结构即可知道。

145255_PmEO_3161152.png

 

四、总结

回顾整个SpringMVC的初始化流程,我们看到,通过HttpServletBean、FrameworkServlet、DispatcherServlet三个不同的类层次,SpringMVC的设计者将三种不同的职责分别抽象,运用模版方法设计模式分别固定在三个类层次中。其中HttpServletBean完成的是<init-param>配置元素的依赖注入,FrameworkServlet完成的是容器上下文的建立,DispatcherServlet完成的是SpringMVC具体编程元素的初始化策略。

转载于:https://my.oschina.net/silence88/blog/809195

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/1015715
推荐阅读
相关标签
  

闽ICP备14008679号