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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

前言

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

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

spring mvc 零配置

项目结构图如下:

20191017100468\_1.png

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

20191017100468\_2.png
这是因为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中配置的

        

    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文件
        
        4.0.0
        com.jihegupiao.demo
        spring4
        0.0.1-SNAPSHOT
        war
    
        
            3.1
            1.8
            UTF-8
        
    
        
            
                javax.servlet
                javax.servlet-api
                3.0.1
                provided
            
    
            
                org.springframework
                spring-webmvc
                4.2.8.RELEASE
            
    
            
                org.aspectj
                aspectjrt
                1.7.3
            
            
                org.aspectj
                aspectjweaver
                1.7.3
    
            
    
        
        
    
            
    
                
                    org.apache.maven.plugins
                    maven-war-plugin
                    2.6
                    
                        false
                    
                
    
                
                    org.apache.maven.plugins
                    maven-compiler-plugin
                    ${maven-compiler-plugin.version}
                    
                        ${project.compiler.version}
                        ${project.compiler.version}
                        ${project.build.sourceEncoding}
                    
                
    
                
                    org.apache.tomcat.maven
                    tomcat7-maven-plugin
                    2.2
                    
                        http://localhost:8080/manager/text
                        Tomcat7
                        admin
                        admin
                        8082
                        UTF-8
                        /
                        ${basedir}/target/${project.build.finalName}.war
                    
                
            
        
        
    1. 通过tomcat7:run 的方式启动,访问 测试链接 测试一下吧,如果正常的话,返回如下结果:

    20191017100468\_3.png

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> webAppInitializerClasses, ServletContext servletContext)
                throws ServletException {
    
            List initializers = new LinkedList();
    
            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 继承结构如下:

    20191017100468\_4.png
    因此会最终调用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类图如下:

20191017100468\_5.png

通过查看源码可知,真正生效的配置是在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时代的配置:

        
  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.singletonList(new JsonViewRequestBodyAdvice()));
                adapter.setResponseBodyAdvice(
                        Collections.>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 exceptionResolvers = new ArrayList();
            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 零配置的源码就分析完了,关于这部分的流程图如下:

20191017100468\_6.png


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot 源码解析15-spring mvc零配置

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏