spring boot 源码解析15-spring mvc零配置

前言

spring boot 是基于spring 4 的基础上的一个框架,spring 4 有一个新特效–>基于java config 实现零配置.而在企业的实际工作中,spring 都是和spring mvc 紧密结合的,这篇文章从以下4个方面进行阐述:

  1. spring mvc 零配置
  2. spring mvc 零配置源码分析

spring mvc 零配置

项目结构图如下:

《spring boot 源码解析15-spring mvc零配置》

这里需要设定project Facets 中的 web 版本为 3.0 以上.如图:

《spring boot 源码解析15-spring mvc零配置》
这是因为spring mvc 零配置 是基于servlet 3.0 规范的.

  1. 在Servlet3.0规范,支持将web.xml相关配置也硬编码到代码中[servlet,filter,listener,等等],并由javax.servlet.ServletContainerInitializer的实现类负责在容器启动时进行加载.
    spring提供了一个实现类org.springframework.web.SpringServletContainerInitializer,
    该类会调用所有org.springframework.web.WebApplicationInitializer的实现类的onStartup(ServletContext servletContext)方法,将相关的组件注册到服务器.

    spring同时提供了一些WebApplicationInitializer的实现类供我们继承,以简化相关的配置,比如:org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer : 注册spring DispatcherServlet.

    因此我们只需继承AbstractAnnotationConfigDispatcherServletInitializer 即可.

    代码如下:

    package com.demo.config;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
    
    // 指定spring application 的配置
    @Override
    protected Class<?>[] getRootConfigClasses() {
         return new Class<?>[] { RootConfig.class };
    }
    
    // 指定spring web 配置
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
    }
    
    // 配置dispatcherServlet的映射路径
    @Override
    protected String[] getServletMappings() {
         return new String[] { "/" };
    }
    }
    

    RootConfig,用来指定扫描除去@Controller注解的类, 代码如下,

    @Configuration
    @ComponentScan(basePackages = { "com.demo" },
    excludeFilters={@Filter(type=FilterType.ANNOTATION,classes=Controller.class)})
    public class RootConfig {
    }

    其中,@Configuration 声明该类是一个配置类,@ComponentScan 声明了扫描包的范围,可配置多个, excludeFilters 用来去除扫描@Controller注解的类.

    WebConfig用来配置Spring mvc 相关的配置,这里我们继承WebMvcConfigurerAdapter,用来简化配置.代码如下:

    package com.demo.config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.servlet.view.InternalResourceViewResolver;
    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages = "com.demo.controller", useDefaultFilters = false, includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = { Controller.class }) })
    public class WebConfig extends WebMvcConfigurerAdapter {
    
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/jsp/function/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
    
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    
        configurer.enable();
    }
    }
    

    其中@Configuration 声明该类是一个配置类,@EnableWebMvc 用来导入WebMvcConfigurationSupport中对于spring mvc的配置.@ComponentScan 用来指定扫描com.demo.controller包下被@Controller注解的类.

    此外,还声明了对于jsp的ViewResolver,和覆盖了configureDefaultServletHandling来实例化DefaultServletHttpRequestHandler,该配置相当于在xml中配置的

    <mvc:default-servlet-handler />

    DefaultServletHttpRequestHandler的作用是它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理

    1. service 包的代码如下:
    package com.demo.service;
    public interface TestService {
    
    String sayHello();
    }
    

    实现类:

    package com.demo.service;
    import org.springframework.stereotype.Service;
    @Service
    public class TestServiceImpl implements TestService {
    
    @Override
    public String sayHello() {
        return "hi ,spring 4 ";
    }
    }
    
    1. controller代码:
    package com.demo.controller;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import com.demo.service.TestService;
    @Controller
    public class TestController {
    
    @Autowired
    private TestService testService;
    
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    @ResponseBody
    public String test() {
        return testService.sayHello();
    }
    }
    
    1. pom文件
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.jihegupiao.demo</groupId>
    <artifactId>spring4</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    
    
    <properties>
        <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
        <project.compiler.version>1.8</project.compiler.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.2.8.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.3</version>
    
        </dependency>
    
    </dependencies>
    <build>
    
        <plugins>
    
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
    
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${project.compiler.version}</source>
                    <target>${project.compiler.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
    
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <url>http://localhost:8080/manager/text</url>
                    <server>Tomcat7</server>
                    <username>admin</username>
                    <password>admin</password>
                    <port>8082</port>
                    <uriEncoding>UTF-8</uriEncoding>
                    <path>/</path>
                    <warFile>${basedir}/target/${project.build.finalName}.war</warFile>
                </configuration>
            </plugin>
        </plugins>
    </build>
    </project>
    1. 通过tomcat7:run 的方式启动,访问 测试链接 测试一下吧,如果正常的话,返回如下结果:

    《spring boot 源码解析15-spring mvc零配置》

spring mvc 零配置源码分析

  1. 前言部分已经有提到,spring mvc 4 零配置是基于 servlet 3.0 规范的,在该规范中,是通过ServletContainerInitializer进行配置的,而在spring mvc中有一个唯一的实现–>SpringServletContainerInitializer,它就是打开宝箱的钥匙.代码如下:

    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    
        @Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {
    
        List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
    
        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                // Be defensive: Some servlet containers provide us with invalid classes,
                // no matter what @HandlesTypes says...
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer) waiClass.newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }
    
        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }
    
        servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
        AnnotationAwareOrderComparator.sort(initializers);
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }
    }

    由于该类声明了@HandlesTypes(WebApplicationInitializer.class),则实现了Servlet 3.0 +规范的容器会依次扫描类路径下实现了WebApplicationInitializer接口的类传递到onStartup中.处理逻辑如下:

    1. 依次遍历webAppInitializerClasses,如果该类不是接口并且不是抽象类并且是WebApplicationInitializer的子类,则实例化后加入到initializers中.
    2. 如果initializers 为空集合,则打印一条日志后直接return.如下:

      No Spring WebApplicationInitializer types detected on classpath

    3. 否则,排序后(如果它们存在@Order 注解,或者实现了Ordered 接口),依次调用其onStartup方法,进行启动.

  2. WebApplicationInitializer 继承结构如下:

    《spring boot 源码解析15-spring mvc零配置》
    因此会最终调用AbstractDispatcherServletInitializer#onStartup方法,代码如下:

    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
        registerDispatcherServlet(servletContext);
    }
    1. 调用父类AbstractContextLoaderInitializer的onStartup以注册WebApplicationContext.
    2. 调用registerDispatcherServlet以注册DispatcherServlet.
  3. AbstractContextLoaderInitializer#onStartup 代码如下:

        @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        registerContextLoaderListener(servletContext);
    }

    调用

    protected void registerContextLoaderListener(ServletContext servletContext) {
        // 1. 创建WebApplicationContext 上下文
        WebApplicationContext rootAppContext = createRootApplicationContext();
        if (rootAppContext != null) {
            // 2. 实例化ContextLoaderListener
            ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
            listener.setContextInitializers(getRootApplicationContextInitializers());
            // 3. 添加到ServletContext 中
            servletContext.addListener(listener);
        }
        else {
            logger.debug("No ContextLoaderListener registered, as " +
                    "createRootApplicationContext() did not return an application context");
        }
    }

    3件事:

    1. 调用抽象方法createRootApplicationContext来创建WebApplicationContext.该方法的实现在AbstractAnnotationConfigDispatcherServletInitializer中,代码如下:

      protected WebApplicationContext createRootApplicationContext() {
      // 1. 获得RootConfigClasses
      Class<?>[] configClasses = getRootConfigClasses();
      if (!ObjectUtils.isEmpty(configClasses)) {
          // 2. 初始化AnnotationConfigWebApplicationContext,并进行注册
          AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
          rootAppContext.register(configClasses);
          return rootAppContext;
      }
      else {
          return null;
      }
      }
      1. 调用抽象方法getRootConfigClasses,获得关于Spring的配置类.而对于我们当前来说,该方法的返回值就是demo工程中的RootConfig.class
      2. 如果getRootConfigClasses返回空数组,则返回null,否则进入第3步.
      3. 实例化AnnotationConfigWebApplicationContext,并进行注册.
    2. 如果创建失败,则打印日志,否则进入第3步.
    3. 实例化ContextLoaderListener,并添加到ServletContext中.
  4. AbstractDispatcherServletInitializer#registerDispatcherServlet 代码如下:

    protected void registerDispatcherServlet(ServletContext servletContext) {
        String servletName = getServletName();
        Assert.hasLength(servletName, "getServletName() must not return empty or null");
    
        // 1. 初始化WebApplicationContext
        WebApplicationContext servletAppContext = createServletApplicationContext();
        Assert.notNull(servletAppContext,
                "createServletApplicationContext() did not return an application " +
                "context for servlet [" + servletName + "]");
    
        // 2. 舒适化DispatcherServlet
        FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
        dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
    
        // 3. 向servletContext 添加Servlet
        ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
        Assert.notNull(registration,
                "Failed to register servlet with name '" + servletName + "'." +
                "Check if there is another servlet registered under the same name.");
    
        registration.setLoadOnStartup(1);
        registration.addMapping(getServletMappings());
        registration.setAsyncSupported(isAsyncSupported());// 默认支持异步
    
        // 4. 获得Filter的配置,并依次进行注册
        Filter[] filters = getServletFilters();
        if (!ObjectUtils.isEmpty(filters)) {
            for (Filter filter : filters) {
                registerServletFilter(servletContext, filter);
            }
        }
    
        // 5. 自定义初始化
        customizeRegistration(registration);
    }

    5件事:

    1. 调用createServletApplicationContext直接初始化WebApplicationContext,代码如下:

      @Override
      protected WebApplicationContext createServletApplicationContext() {
      // 1. 初始化AnnotationConfigWebApplicationContext
      AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
      // 2. 获得ServletConfigClasses
      Class<?>[] configClasses = getServletConfigClasses();
      if (!ObjectUtils.isEmpty(configClasses)) {
          // 3. 向AnnotationConfigWebApplicationContext 注册
          servletAppContext.register(configClasses);
      }
      return servletAppContext;
      }
      1. 实例化AnnotationConfigWebApplicationContext.
      2. 调用getServletConfigClasses 方法获得关于spring mvc的配置,如果不为空,则向AnnotationConfigWebApplicationContext进行注册,对于当前来说,是WebConfig.class.
    2. 舒适化DispatcherServlet,并设置ContextInitializers,一般来说,getServletApplicationContextInitializer方法返回的是null.
    3. 将dispatcherServlet 向servletContext进行注册.
    4. 获得Filter的配置,并依次进行注册
    5. 自定义初始化,默认是空实现

@EnableWebMvc源码分析

WebConfig声明了@EnableWebMvc,该注解通过@Import(DelegatingWebMvcConfiguration.class)的方式导入了DelegatingWebMvcConfiguration的配置,通过前几篇文章可以知道,此时spring 将加载DelegatingWebMvcConfiguration的配置. DelegatingWebMvcConfiguration类图如下:

《spring boot 源码解析15-spring mvc零配置》

通过查看源码可知,真正生效的配置是在WebMvcConfigurationSupport中,该类配置了如下几个bean:

  1. RequestMappingHandlerMapping order 为0,用来处理被@controller注解的类的方法.代码如下:

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        // 1. 实例化RequestMappingHandlerMapping
        RequestMappingHandlerMapping handlerMapping = createRequestMappingHandlerMapping();
        handlerMapping.setOrder(0);
        // 2. 设置拦截器,默认注册的有ConversionServiceExposingInterceptor,ResourceUrlProviderExposingInterceptor
        handlerMapping.setInterceptors(getInterceptors());
        handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
        // 跨域设置
        handlerMapping.setCorsConfigurations(getCorsConfigurations());
    
        // 3. 实例化PathMatchConfigurer,该类是用来配置路径匹配的
        PathMatchConfigurer configurer = getPathMatchConfigurer();
        if (configurer.isUseSuffixPatternMatch() != null) {
            handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
        }
        if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
            handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
        }
        if (configurer.isUseTrailingSlashMatch() != null) {
            handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
        }
        UrlPathHelper pathHelper = configurer.getUrlPathHelper();
        if (pathHelper != null) {
            handlerMapping.setUrlPathHelper(pathHelper);
        }
        PathMatcher pathMatcher = configurer.getPathMatcher();
        if (pathMatcher != null) {
            handlerMapping.setPathMatcher(pathMatcher);
        }
    
        return handlerMapping;
    }
    1. 实例化RequestMappingHandlerMapping,设置order为0
    2. 设置拦截器,默认注册的有ConversionServiceExposingInterceptor,ResourceUrlProviderExposingInterceptor,可通过覆写addInterceptors来添加拦截器
    3. 跨域设置,默认没有配置,可通过覆写addCorsMappings进行添加跨域映射规则
    4. 获得PathMatchConfigurer,该类是用来配置路径匹配的,可通过configurePathMatch来个性化配置.
  2. HandlerMapping,order 为1,用来处理URL path 直接映射到view 的名称,代码如下:

    @Bean
    public HandlerMapping viewControllerHandlerMapping() {
        // 注册HandlerMapping
        ViewControllerRegistry registry = new ViewControllerRegistry();
        registry.setApplicationContext(this.applicationContext);
        addViewControllers(registry);
    
        AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
        handlerMapping = (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping());
        handlerMapping.setPathMatcher(mvcPathMatcher());
        handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
        handlerMapping.setInterceptors(getInterceptors());
        handlerMapping.setCorsConfigurations(getCorsConfigurations());
        return handlerMapping;
    }
    1. 实例化ViewControllerRegistry
    2. 调用addViewControllers 进行配置.默认空实现,可以通过覆写的方式添加映射规则
    3. 对HandlerMapping 进行设置.

    该类的具体使用可以参考如下链接:

  3. BeanNameUrlHandlerMapping,order 为2,用来处理URL path 直接映射到controller的名字.代码如下:

    @Bean
    public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
        BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
        mapping.setOrder(2);
        mapping.setInterceptors(getInterceptors());
        mapping.setCorsConfigurations(getCorsConfigurations());
        return mapping;
    }
  4. HandlerMapping,order 为Integer.MAX_VALUE-1,用来处理静态资源.代码如下:

    @Bean
    public HandlerMapping resourceHandlerMapping() {
        ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
                this.servletContext, mvcContentNegotiationManager());
        addResourceHandlers(registry);
    
        AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
        if (handlerMapping != null) {
            handlerMapping.setPathMatcher(mvcPathMatcher());
            handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
            handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
            handlerMapping.setCorsConfigurations(getCorsConfigurations());
        }
        else {
            handlerMapping = new EmptyHandlerMapping();
        }
        return handlerMapping;
    }
    1. 实例化ResourceHandlerRegistry
    2. 调用addResourceHandlers配置映射规则,默认空实现,可以通过覆写的方式添加映射规则
    3. 对HandlerMapping进行配置.

      1. 如果在addResourceHandlers中配置了映射规则,则会对其设置PathMatcher,UrlPathHelper,拦截器为ResourceUrlProviderExposingInterceptor
      2. 否则, HandlerMapping 为EmptyHandlerMapping 默认为EmptyHandlerMapping.

    声明该bean,相当于在xml时代的配置:

    <mvc:resources location="/html/" mapping="/html/**" />
  5. HandlerMapping,order 为Integer.MAX_VALUE,用来处理转发请求到DispatcherServlet.代码如下:

    @Bean
    public HandlerMapping defaultServletHandlerMapping() {
        DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext);
        configureDefaultServletHandling(configurer);
        AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping();
        handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
        return handlerMapping;
    }
  6. RequestMappingHandlerAdapter,处理请求通过被@controller注解的类的方法.

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
        adapter.setContentNegotiationManager(mvcContentNegotiationManager());
        adapter.setMessageConverters(getMessageConverters());
        adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
        adapter.setCustomArgumentResolvers(getArgumentResolvers());
        adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
    
        if (jackson2Present) {
            adapter.setRequestBodyAdvice(
                    Collections.<RequestBodyAdvice>singletonList(new JsonViewRequestBodyAdvice()));
            adapter.setResponseBodyAdvice(
                    Collections.<ResponseBodyAdvice<?>>singletonList(new JsonViewResponseBodyAdvice()));
        }
    
        AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
        configureAsyncSupport(configurer);
        if (configurer.getTaskExecutor() != null) {
            adapter.setTaskExecutor(configurer.getTaskExecutor());
        }
        if (configurer.getTimeout() != null) {
            adapter.setAsyncRequestTimeout(configurer.getTimeout());
        }
        adapter.setCallableInterceptors(configurer.getCallableInterceptors());
        adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
    
        return adapter;
    }
    1. 实例化RequestMappingHandlerAdapter,可通过addArgumentResolvers来配置argument解析器,addReturnValueHandlers来配置返回值处理器,configureMessageConverters 可以配置消息转换器,如果该方法没有复写,则添加默认的
    2. 如果当前类路径存在com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator,则设置请求体拦截器为JsonViewRequestBodyAdvice,响应体拦截器为JsonViewResponseBodyAdvice
    3. 异步配置,可通过configureAsyncSupport进行配置
  7. HttpRequestHandlerAdapter,处理请求通过HttpRequestHandler.调用具体的方法对用户发来的请求来进行处理。当handlerMapping获取到执行请求的controller时,DispatcherServlte会根据controller对应的controller类型来调用相应的HandlerAdapter来进行处理。代码如下:

        @Bean
    public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
        return new HttpRequestHandlerAdapter();
    }
  8. SimpleControllerHandlerAdapter,处理请求通过Controller的实现类.代码如下:

    @Bean
    public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
        return new SimpleControllerHandlerAdapter();
    }
  9. HandlerExceptionResolverComposite.代码如下:

    @Bean
    public HandlerExceptionResolver handlerExceptionResolver() {
        List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<HandlerExceptionResolver>();
        configureHandlerExceptionResolvers(exceptionResolvers);
        if (exceptionResolvers.isEmpty()) {
            addDefaultHandlerExceptionResolvers(exceptionResolvers);
        }
        extendHandlerExceptionResolvers(exceptionResolvers);
        HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
        composite.setOrder(0);
        composite.setExceptionResolvers(exceptionResolvers);
        return composite;
    }
    1. 通过调用configureHandlerExceptionResolvers进行个性化配置
    2. 如果exceptionResolvers为空,也就是意味着第1步没有进行注册,则调用addDefaultHandlerExceptionResolvers添加默认的ExceptionHandlerExceptionResolver–>ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver
    3. 实例化HandlerExceptionResolverComposite
  10. ExceptionHandlerExceptionResolver,处理通过被@ExceptionHandler注解的方法抛出的异常
  11. ResponseStatusExceptionResolver,用来处理被@ResponseStatus注解的所抛出的异常
  12. DefaultHandlerExceptionResolver,用来处理Spring异常体系抛出的异常
  13. AntPathMatcher,代码如下:

        @Bean
        public PathMatcher mvcPathMatcher() {
        // 注册PathMatcher
        PathMatcher pathMatcher = getPathMatchConfigurer().getPathMatcher();
        return (pathMatcher != null ? pathMatcher : new AntPathMatcher());
    }

    默认配置的是AntPathMatcher

  14. UrlPathHelper,代码如下:

    @Bean
    public UrlPathHelper mvcUrlPathHelper() {
        // 注册UrlPathHelper
        UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper();
        return (pathHelper != null ? pathHelper : new UrlPathHelper());
    }

    默认配置的是UrlPathHelper

  15. ContentNegotiationManager,该类的作用是根据请求规则决定返回什么样的内容类型。后缀规则、参数规则、Accept头规则、固定的内容类型等。注意,这里只是决定,不是具体提供内容类型的地方.代码如下:

    @Bean
    public ContentNegotiationManager mvcContentNegotiationManager() {
        // 注册ContentNegotiationManager
        if (this.contentNegotiationManager == null) {
            ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext);
            configurer.mediaTypes(getDefaultMediaTypes());
            configureContentNegotiation(configurer);
            try {
                this.contentNegotiationManager = configurer.getContentNegotiationManager();
            }
            catch (Exception ex) {
                throw new BeanInitializationException("Could not create ContentNegotiationManager", ex);
            }
        }
        return this.contentNegotiationManager;
    }

    lazy-init风格,会在实例化RequestMappingHandlerMapping时进行初始化.

    1. 实例化ContentNegotiationConfigurer
    2. 配置mediaTypes为:

      1. 如果当前类路径存在com.rometools.rome.feed.WireFeed,则注册atom–>application/atom+xml,rss–>application/rss+xml
      2. 如果当前类路径存在javax.xml.bind.Binder或者com.fasterxml.jackson.dataformat.xml.XmlMapper存在,则注册xml–>application/xml
      3. (如果当前类路径存在com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator)或者 (如果当前类路径存在com.google.gson.Gson),则注册json–>application/json
    3. 可通过覆写configureContentNegotiation进行个性化设置
  16. DefaultFormattingConversionService
  17. OptionalValidatorFactoryBean,如果是在JSR-303的环境,则进行注册
  18. HttpMessageConverter,依赖第3放类库
  19. FormattingConversionService,用来格式化的.代码如下:

    @Bean
    public FormattingConversionService mvcConversionService() {
        FormattingConversionService conversionService = new DefaultFormattingConversionService();
        addFormatters(conversionService);
        return conversionService;
    }
  20. Validator,用来mvc校验的.代码如下:

    @Bean
    public Validator mvcValidator() {
        // 1. 该方法默认返回null,可以复写该方法
        Validator validator = getValidator();
        if (validator == null) {
            // 2. 如果存在javax.validation.Validator,则尝试加载org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean
            if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
                Class<?> clazz;
                try {
                    String className = "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean";
                    clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader());
                }
                catch (ClassNotFoundException ex) {
                    throw new BeanInitializationException("Could not find default validator class", ex);
                }
                catch (LinkageError ex) {
                    throw new BeanInitializationException("Could not load default validator class", ex);
                }
                validator = (Validator) BeanUtils.instantiateClass(clazz);
            }
            else {
                // 否则返回NoOpValidator
                validator = new NoOpValidator();
            }
        }
        return validator;
    }
    1. 调用getValidator 获得Validator,该方法默认返回null,可以复写该方法
    2. 如果第1步返回null,如果存在javax.validation.Validator,则尝试加载org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean,否则返回NoOpValidator
  21. CompositeUriComponentsContributor,MvcUriComponentsBuilder会用到,代码如下:

    @Bean
    public CompositeUriComponentsContributor mvcUriComponentsContributor() {
        return new CompositeUriComponentsContributor(
                requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService());
    }
  22. ViewResolver,代码如下:

    @Bean
    public ViewResolver mvcViewResolver() {
        ViewResolverRegistry registry = new ViewResolverRegistry();
        registry.setContentNegotiationManager(mvcContentNegotiationManager());
        registry.setApplicationContext(this.applicationContext);
        configureViewResolvers(registry);
    
        if (registry.getViewResolvers().isEmpty()) {
            String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.applicationContext, ViewResolver.class, true, false);
            if (names.length == 1) {
                registry.getViewResolvers().add(new InternalResourceViewResolver());
            }
        }
    
        ViewResolverComposite composite = new ViewResolverComposite();
        composite.setOrder(registry.getOrder());
        composite.setViewResolvers(registry.getViewResolvers());
        composite.setApplicationContext(this.applicationContext);
        composite.setServletContext(this.servletContext);
        return composite;
    }
    1. 实例化ViewResolverRegistry,并调用configureViewResolvers进行个性化配置
    2. 如果ViewResolverRegistry中ViewResolver为空,则默认添加InternalResourceViewResolver
    3. 初始化ViewResolverComposite
      至此,spring mvc 零配置的源码就分析完了,关于这部分的流程图如下:

《spring boot 源码解析15-spring mvc零配置》

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_26000415/article/details/78957026
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞