SpringMVC源码解析(一) - 图1

文章结构

  1. 源码阅读环境的搭建
  2. SpringBoot中SpringMVC自动配置原理
  3. DispatcherServlet的初始化逻辑
  4. web容器的初始化

源码阅读环境搭建

为了简单起见,再一个就是现在这个年代也没有啥项目使用JSP了。所以本次分析使用SpringBoot结合thymeleaf来搞

首先pom文件依赖

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.0.0.RELEASE</version>
  5. </parent>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-web</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  13. </dependency>

application.properties

  1. spring.thymeleaf.prefix=classpath:/templates/
  2. spring.thymeleaf.suffix=.html
  3. spring.thymeleaf.mode=LEGACYHTML5
  4. spring.thymeleaf.encoding=UTF-8
  5. spring.thymeleaf.content-type=text/html
  6. spring.thymeleaf.cache=false

test.html

  1. <!DOCTYPE HTML>
  2. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta content="text/html;charset=UTF-8"/>
  5. </head>
  6. <body>
  7. <span th:text="'用户名:'+${name}+',年龄:'+${age}">
  8. </span>
  9. </body>
  10. </html>

Controller

  1. @Controller
  2. public class MVCDemoController {
  3. @RequestMapping(value = "/testMVC",method = RequestMethod.GET )
  4. public String testMVC(Model model){
  5. model.addAttribute("name","张三");
  6. model.addAttribute("age","18");
  7. return "test";
  8. }
  9. }

启动demo

  1. @SpringBootApplication(scanBasePackages="cn.shiyujun.controller")
  2. public class MVCDemo {
  3. public static void main (String args[]){
  4. SpringApplication.run(MVCDemo.class, args);
  5. }
  6. }

至此Deno工程搭建完毕,有需要源码的同学可以从下方地址获取
https://github.com/shiyujun/spring-framework

源码分析

SpringMVC自动配置

我们知道在SpringBoot中使用SpringMVC的时候是不需要像传统Spring中配置web.xml和配置文件等等的。那么大家知道这是为什么吗

答案就在这个类WebMvcAutoConfiguration里面

  1. @Configuration
  2. @ConditionalOnWebApplication(type = Type.SERVLET)
  3. @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
  4. @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
  5. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
  6. @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
  7. ValidationAutoConfiguration.class })
  8. public class WebMvcAutoConfiguration {
  9. public static final String DEFAULT_PREFIX = "";
  10. public static final String DEFAULT_SUFFIX = "";
  11. private static final String[] SERVLET_LOCATIONS = { "/" };
  12. @Bean
  13. @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
  14. public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
  15. return new OrderedHiddenHttpMethodFilter();
  16. }
  17. @Bean
  18. @ConditionalOnMissingBean(HttpPutFormContentFilter.class)
  19. @ConditionalOnProperty(prefix = "spring.mvc.formcontent.putfilter", name = "enabled", matchIfMissing = true)
  20. public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {
  21. return new OrderedHttpPutFormContentFilter();
  22. }
  23. @Configuration
  24. @Import(EnableWebMvcConfiguration.class)
  25. @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
  26. public static class WebMvcAutoConfigurationAdapter
  27. implements WebMvcConfigurer, ResourceLoaderAware {
  28. @Bean
  29. @ConditionalOnMissingBean
  30. public InternalResourceViewResolver defaultViewResolver() {
  31. InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  32. resolver.setPrefix(this.mvcProperties.getView().getPrefix());
  33. resolver.setSuffix(this.mvcProperties.getView().getSuffix());
  34. return resolver;
  35. }
  36. @Bean
  37. @ConditionalOnBean(View.class)
  38. @ConditionalOnMissingBean
  39. public BeanNameViewResolver beanNameViewResolver() {
  40. BeanNameViewResolver resolver = new BeanNameViewResolver();
  41. resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
  42. return resolver;
  43. }
  44. @Bean
  45. @ConditionalOnBean(ViewResolver.class)
  46. @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
  47. public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
  48. ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
  49. resolver.setContentNegotiationManager(
  50. beanFactory.getBean(ContentNegotiationManager.class));
  51. // ContentNegotiatingViewResolver uses all the other view resolvers to locate
  52. // a view so it should have a high precedence
  53. resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
  54. return resolver;
  55. }
  56. @Bean
  57. @ConditionalOnMissingBean
  58. @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
  59. public LocaleResolver localeResolver() {
  60. if (this.mvcProperties
  61. .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
  62. return new FixedLocaleResolver(this.mvcProperties.getLocale());
  63. }
  64. AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
  65. localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
  66. return localeResolver;
  67. }
  68. @Bean
  69. public WelcomePageHandlerMapping welcomePageHandlerMapping(
  70. ApplicationContext applicationContext) {
  71. return new WelcomePageHandlerMapping(
  72. new TemplateAvailabilityProviders(applicationContext),
  73. applicationContext, getWelcomePage(),
  74. this.mvcProperties.getStaticPathPattern());
  75. }
  76. @Bean
  77. @ConditionalOnMissingBean({ RequestContextListener.class,
  78. RequestContextFilter.class })
  79. public static RequestContextFilter requestContextFilter() {
  80. return new OrderedRequestContextFilter();
  81. }
  82. @Configuration
  83. @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
  84. public static class FaviconConfiguration implements ResourceLoaderAware {
  85. @Bean
  86. public SimpleUrlHandlerMapping faviconHandlerMapping() {
  87. SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
  88. mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
  89. mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
  90. faviconRequestHandler()));
  91. return mapping;
  92. }
  93. @Bean
  94. public ResourceHttpRequestHandler faviconRequestHandler() {
  95. ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
  96. requestHandler.setLocations(resolveFaviconLocations());
  97. return requestHandler;
  98. }
  99. }
  100. }
  101. /**
  102. * Configuration equivalent to {@code @EnableWebMvc}.
  103. */
  104. @Configuration
  105. public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
  106. @Bean
  107. @Override
  108. public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
  109. RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
  110. adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null
  111. || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
  112. return adapter;
  113. }
  114. @Bean
  115. @Primary
  116. @Override
  117. public RequestMappingHandlerMapping requestMappingHandlerMapping() {
  118. // Must be @Primary for MvcUriComponentsBuilder to work
  119. return super.requestMappingHandlerMapping();
  120. }
  121. @Bean
  122. @Override
  123. public FormattingConversionService mvcConversionService() {
  124. WebConversionService conversionService = new WebConversionService(
  125. this.mvcProperties.getDateFormat());
  126. addFormatters(conversionService);
  127. return conversionService;
  128. }
  129. @Bean
  130. @Override
  131. public Validator mvcValidator() {
  132. if (!ClassUtils.isPresent("javax.validation.Validator",
  133. getClass().getClassLoader())) {
  134. return super.mvcValidator();
  135. }
  136. return ValidatorAdapter.get(getApplicationContext(), getValidator());
  137. }
  138. @Bean
  139. @Override
  140. public ContentNegotiationManager mvcContentNegotiationManager() {
  141. ContentNegotiationManager manager = super.mvcContentNegotiationManager();
  142. List<ContentNegotiationStrategy> strategies = manager.getStrategies();
  143. ListIterator<ContentNegotiationStrategy> iterator = strategies.listIterator();
  144. while (iterator.hasNext()) {
  145. ContentNegotiationStrategy strategy = iterator.next();
  146. if (strategy instanceof PathExtensionContentNegotiationStrategy) {
  147. iterator.set(new OptionalPathExtensionContentNegotiationStrategy(
  148. strategy));
  149. }
  150. }
  151. return manager;
  152. }
  153. }
  154. @Configuration
  155. @ConditionalOnEnabledResourceChain
  156. static class ResourceChainCustomizerConfiguration {
  157. @Bean
  158. public ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
  159. return new ResourceChainResourceHandlerRegistrationCustomizer();
  160. }
  161. }
  162. }

仔细看这个类,你就会发现这些自动注入的一些类都是之前需要我们在xml文件中配置的,只不过是SpringBoot帮我们做了这个操作。只有当我们需要自定义一些东西的才有必要去关心这些配置,平常使用的时候拿来就用即可。这就是大名鼎鼎的约定大于配置

初始化DispatcherServlet

Tomcat.java
->loadServlet()
->GenericServlet.init()
->Servlet.init()
image.png

DispatcherServlet是一个实现了Servlet接口的类,Servlet的初始化阶段会调用它的init()方法,而DispatcherServlet的方法是继承自父类HttpServletBean

  1. public final void init() throws ServletException {
  2. if (logger.isDebugEnabled()) {
  3. logger.debug("Initializing servlet '" + getServletName() + "'");
  4. }
  5. //处理init-param参数,但是SpringBoot中默认是没有的
  6. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  7. if (!pvs.isEmpty()) {
  8. try {
  9. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  10. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
  11. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
  12. initBeanWrapper(bw);
  13. bw.setPropertyValues(pvs, true);
  14. }
  15. catch (BeansException ex) {
  16. if (logger.isErrorEnabled()) {
  17. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
  18. }
  19. throw ex;
  20. }
  21. }
  22. // 初始化Servlet,往下看
  23. initServletBean();
  24. if (logger.isDebugEnabled()) {
  25. logger.debug("Servlet '" + getServletName() + "' configured successfully");
  26. }
  27. }
  28. protected final void initServletBean() throws ServletException {
  29. getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
  30. if (this.logger.isInfoEnabled()) {
  31. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
  32. }
  33. long startTime = System.currentTimeMillis();
  34. try {
  35. //初始化web容器
  36. this.webApplicationContext = initWebApplicationContext();
  37. //扩展点
  38. initFrameworkServlet();
  39. }
  40. catch (ServletException ex) {
  41. this.logger.error("Context initialization failed", ex);
  42. throw ex;
  43. }
  44. catch (RuntimeException ex) {
  45. this.logger.error("Context initialization failed", ex);
  46. throw ex;
  47. }
  48. if (this.logger.isInfoEnabled()) {
  49. long elapsedTime = System.currentTimeMillis() - startTime;
  50. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
  51. elapsedTime + " ms");
  52. }
  53. }

初始化web容器

  1. protected WebApplicationContext initWebApplicationContext() {
  2. //获取AnnotationConfigServletWebServerApplicationContext类型的web容器
  3. WebApplicationContext rootContext =
  4. WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  5. WebApplicationContext wac = null;
  6. if (this.webApplicationContext != null) {
  7. wac = this.webApplicationContext;
  8. if (wac instanceof ConfigurableWebApplicationContext) {
  9. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
  10. if (!cwac.isActive()) {
  11. if (cwac.getParent() == null) {
  12. cwac.setParent(rootContext);
  13. }
  14. configureAndRefreshWebApplicationContext(cwac);
  15. }
  16. }
  17. }
  18. if (wac == null) {
  19. wac = findWebApplicationContext();
  20. }
  21. if (wac == null) {
  22. wac = createWebApplicationContext(rootContext);
  23. }
  24. if (!this.refreshEventReceived) {
  25. // 刷新应用上下文,这里是重点,接着往下看
  26. onRefresh(wac);
  27. }
  28. if (this.publishContext) {
  29. // 推送事件通知
  30. String attrName = getServletContextAttributeName();
  31. getServletContext().setAttribute(attrName, wac);
  32. if (this.logger.isDebugEnabled()) {
  33. this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
  34. "' as ServletContext attribute with name [" + attrName + "]");
  35. }
  36. }
  37. return wac;
  38. }

下面会看到一些熟悉的东西

  1. protected void onRefresh(ApplicationContext context) {
  2. initStrategies(context);
  3. }
  4. protected void initStrategies(ApplicationContext context) {
  5. initMultipartResolver(context);
  6. initLocaleResolver(context);
  7. initThemeResolver(context);
  8. initHandlerMappings(context);
  9. initHandlerAdapters(context);
  10. initHandlerExceptionResolvers(context);
  11. initRequestToViewNameTranslator(context);
  12. initViewResolvers(context);
  13. initFlashMapManager(context);
  14. }

可以看到上方这一块代码都是一些常用组件的初始化,初始化的逻辑都比较简单,下面随意挑取2个看一下

  1. private void initMultipartResolver(ApplicationContext context) {
  2. try {
  3. this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
  6. }
  7. }
  8. catch (NoSuchBeanDefinitionException ex) {
  9. // Default is no multipart resolver.
  10. this.multipartResolver = null;
  11. if (logger.isDebugEnabled()) {
  12. logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
  13. "': no multipart request handling provided");
  14. }
  15. }
  16. }
  17. private void initHandlerMappings(ApplicationContext context) {
  18. this.handlerMappings = null;
  19. if (this.detectAllHandlerMappings) {
  20. // 加载所有实现HandlerMapping接口的bean
  21. Map<String, HandlerMapping> matchingBeans =
  22. BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
  23. if (!matchingBeans.isEmpty()) {
  24. this.handlerMappings = new ArrayList<>(matchingBeans.values());
  25. // 排序,Spring处理请求就是根据这个排序的结果进行处理,如果当前handlerMapping不可以处理则抛给下一个
  26. AnnotationAwareOrderComparator.sort(this.handlerMappings);
  27. }
  28. }
  29. else {
  30. try {
  31. HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
  32. this.handlerMappings = Collections.singletonList(hm);
  33. }
  34. catch (NoSuchBeanDefinitionException ex) {
  35. // Ignore, we'll add a default HandlerMapping later.
  36. }
  37. }
  38. // Ensure we have at least one HandlerMapping, by registering
  39. // a default HandlerMapping if no other mappings are found.
  40. if (this.handlerMappings == null) {
  41. this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
  42. if (logger.isDebugEnabled()) {
  43. logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
  44. }
  45. }
  46. }

至此,DispatcherServlet的初始化就完成了