我们都知道,想要构建 Spring MVC 环境,有两种方式,一种利用 XML 来实现,另一种是通过 Java 代码来实现,具体可以通过 《Spring MVC 0-XML 配置的原理》 查看

那么 SpringBoot 是如何实现的呢?其实主要思路差不多,我们可以具体看一下:

@EnableAutoConfiguration

首先通过该注解,来开启自动配置,所谓自动配置,就是预读某些一开始就定义好需要加载到 Spring 容器中的 bean,而 @EnableAutoConfiguration 是通过 @Import(AutoConfigurationImportSelector.class) 来实现

META-INF/spring.factories

而 AutoConfigurationImportSelector.class 主要就是通过读取 “META-INF/spring.factories” 来实现的

  1. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  2. MultiValueMap<String, String> result = cache.get(classLoader);
  3. if (result != null) {
  4. return result;
  5. }
  6. try {
  7. Enumeration<URL> urls = (classLoader != null ?
  8. classLoader.getResources("META-INF/spring.factories") :
  9. ClassLoader.getSystemResources("META-INF/spring.factories"));
  10. result = new LinkedMultiValueMap<>();
  11. while (urls.hasMoreElements()) {
  12. URL url = urls.nextElement();
  13. UrlResource resource = new UrlResource(url);
  14. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  15. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  16. String factoryTypeName = ((String) entry.getKey()).trim();
  17. for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
  18. result.add(factoryTypeName, factoryImplementationName.trim());
  19. }
  20. }
  21. }
  22. cache.put(classLoader, result);
  23. return result;
  24. }
  25. catch (IOException ex) {
  26. throw new IllegalArgumentException("Unable to load factories from location [" +
  27. FACTORIES_RESOURCE_LOCATION + "]", ex);
  28. }
  29. }

我们可以看到 “META-INF/spring.factories” 中预加载了 **DispatcherServletAutoConfiguration**

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\

所以 DispatcherServletAutoConfiguration 这个类在 SpringBoot 启动的时候就被实例化了

DispatcherServletAutoConfiguration

在看 DispatcherServletAutoConfiguration 这个类,首先它是一个配置类,既然是配置类的话,Spring 容器在启动的时候,就会加载它,所以它定义了很多 @Bean 方法

  1. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  2. @Configuration(proxyBeanMethods = false)
  3. @ConditionalOnWebApplication(type = Type.SERVLET)
  4. @ConditionalOnClass(DispatcherServlet.class)
  5. @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
  6. public class DispatcherServletAutoConfiguration {
  7. // ...
  8. }

DispatcherServlet

对于 SpringMVC 来说,DispatcherServlet 再熟悉不过了,主要就是来处理和分发请求的,我们看看 SpringBoot 是如何做的

DispatcherServletAutoConfiguration 中,定义了 **DispatcherServlet 的 @Bean**

  1. @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
  2. public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
  3. // 无参的 DispatcherServlet
  4. DispatcherServlet dispatcherServlet = new DispatcherServlet();
  5. dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
  6. dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
  7. dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
  8. dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
  9. dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
  10. return dispatcherServlet;
  11. }

我们可以看到,其实 SpringBoot 就是 new 了一个 DispatcherServlet 而已,然后把它放到容器当中

这里有没有发现很奇怪,DispatcherServlet 只是 new 了一个无参的构造方法,但是我们记得,DispatcherServlet 是需要要给 SpringContext 上下文对象作为参数的呀,这里是不是有问题?

Spring MVC 的 0-XML 实现

我们可以看到 new DispatcherServlet(ac); 传递了一个 Spring 的上下文容器

  1. public class MyWebApplicationInitializer implements WebApplicationInitializer {
  2. public void onStartup(ServletContext servletContext) throws ServletException {
  3. AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
  4. ac.register(AppConfig.class);
  5. DispatcherServlet servlet = new DispatcherServlet(ac);
  6. ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
  7. registration.setLoadOnStartup(1);
  8. registration.addMapping("*.do");
  9. }
  10. }

SpringBoot 的实现

而 SpringBoot 只是简单 new 了一个 DispatcherServlet,没有传递任何参数,这样的话,我们继续跟代码

我们发现 DispatcherServlet 继承了 FrameworkServlet

  1. public class DispatcherServlet extends FrameworkServlet

而 FrameworkServlet 又实现了 ApplicationContextAware

  1. public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware

ApplicationContextAware

这个接口,是一个 Aware 接口,在 Spring 容器中,会调用自动调用 Aware 接口,传递一些参数,而 ApplicationContextAware 会调用其 setApplicationContext 传递上下文容器

  1. public interface ApplicationContextAware extends Aware {
  2. void setApplicationContext(ApplicationContext var1) throws BeansException;
  3. }

那么真相就大白了,原来 Spring 容器初始化完成以后,会调用 ApplicationContextAware 的setApplicationContext 方法,传递上下文容器

  1. @Override
  2. public void setApplicationContext(ApplicationContext applicationContext) {
  3. if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
  4. this.webApplicationContext = (WebApplicationContext) applicationContext;
  5. this.webApplicationContextInjected = true;
  6. }
  7. }

这样,就等于给 DispatcherServlet 传递了 **applicationContext

DispatcherServletRegistrationBean

同时 SpringBoot 又会继续调用 dispatcherServletRegistration 完成剩余的参数配置

  1. @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
  2. @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
  3. public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
  4. WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
  5. DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
  6. webMvcProperties.getServlet().getPath());
  7. registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
  8. registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
  9. multipartConfig.ifAvailable(registration::setMultipartConfig);
  10. return registration;
  11. }

至此 SpringBoot 就完成了 SpringMVC 的核心 DispathcServlet 的自动配置
**

总结:

Spring MVC 是把 Spring 容器放到 DispatchServlet 中
Spring Boot 是把 DispatchServlet 放到容器中,再由容器调用 ApplicationAware 给 DispathServlet 添加容器属性