拓展springmvc原理详解

Posted Fire king

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了拓展springmvc原理详解相关的知识,希望对你有一定的参考价值。

拓展springmvc原理

springboot自定义配置采用了策略模式,配置同样的组件采用不同的实现方法,因此必定有一个接口封装了一系列“算法”,这个接口就是WebMvcConfigurer
在这里插入图片描述
可以重写的方法:
在这里插入图片描述
public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered, InitializingBean {实现了视图解析器接口的类,就可以看成是一个视图解析器,那么问题来了,我们也要做一个实现视图解析器接口的类,就要知道它的规则,规则应该怎么找呢?因此,我们即使在ContentNegotiatingViewResolver很难找到规则,可以到它的父类或者实现的接口去找,这里选了ViewResolver:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet;

import java.util.Locale;
import org.springframework.lang.Nullable;

public interface ViewResolver {
    @Nullable
    View resolveViewName(String var1, Locale var2) throws Exception;
}

然后就可以到ContentNegotiatingViewResolver找到resolveViewName方法:

 if (requestedMediaTypes != null) {
            List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
            View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
            if (bestView != null) {
                return bestView;
            }

这个方法获取候选视图,得到最好的视图,如何获取候选视图:
进入方法getCandidateViews:这个方法遍历所有的视图解析器,获取视图,封装到view,view添加到候选视图candidateViews,最后返回。


            while(var5.hasNext()) {
                ViewResolver viewResolver = (ViewResolver)var5.next();
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    candidateViews.add(view);
                }

                Iterator var8 = requestedMediaTypes.iterator();

                while(var8.hasNext()) {
                    MediaType requestedMediaType = (MediaType)var8.next();
                    List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
                    Iterator var11 = extensions.iterator();

                    while(var11.hasNext()) {
                        String extension = (String)var11.next();
                        String viewNameWithExtension = viewName + '.' + extension;
                        view = viewResolver.resolveViewName(viewNameWithExtension, locale);
                        if (view != null) {
                            candidateViews.add(view);
                        }
                    }
                }
            }
        }

        if (!CollectionUtils.isEmpty(this.defaultViews)) {
            candidateViews.addAll(this.defaultViews);
        }

        return candidateViews;

如何自定义视图解析器:

@Configuration
//@EnableWebMvc这个不能标注,否则就会被springboot的mvc全面接管
public class MyConfiger implements WebMvcConfigurer {
    
    //将自定义视图解析器交给spring管理
    @Bean
    public  ViewResolver myViewResolver(){
        return new MyViewResolver();
    }
    
    //自定义视图解析器
    public static class MyViewResolver implements ViewResolver{

        @Override
        public View resolveViewName(String s, Locale locale) throws Exception {
            return null;
        }
    }

}

那么如何得知我们配置的视图解析器配置成功呢?
答案:利用dispatcherServlet(前端控制器),因为所有请求都会经过前端控制器,前端控制器把请求发给前端控制器,进入DispatcherServlet的doService方法的this.doDispatch(request, response);方法,所有的请求和响应都经过这里


    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

因此在这里断点
在这里插入图片描述
结果:其中thymeleaf是我们导入thymeleaf启动器后有的,而MyConfiger是我们自己配置的,其余都是默认的。
在这里插入图片描述

实现WebMvcConfigurer中的方法

@Configuration
//@EnableWebMvc这个不能标注,否则就会被springboot的mvc全面接管
public class MyConfiger implements WebMvcConfigurer {


    //视图跳转
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/xlh").setViewName("yyds");
    }
}

在这里插入图片描述
为什么拓展mvc不能导入@EnableWebMvc

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

@EnableWebMvc导入了DelegatingWebMvcConfiguration.class

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

DelegatingWebMvcConfiguration继承了WebMvcConfigurationSupport

进入WebMvcAutoConfiguration类中

 @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
    @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
        private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
        private final ResourceProperties resourceProperties;
        private final WebMvcProperties mvcProperties;
        private final ListableBeanFactory beanFactory;
        private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;
        final WebMvcAutoConfiguration.ResourceHandlerRegistration

进入@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})中EnableWebMvcConfiguration.class

 public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
        private final ResourceProperties resourceProperties;
        private final WebMvcProperties mvcProperties;

进入DelegatingWebMvcConfiguration,下面方法加入容器中所有的WebMvcConfigurer

  @Autowired(
        required = false
    )
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }

    }

所以说@EnableWebMvc从容器中获取所有的WebMvcConfig。

回归正题,WebMvcAutoConfiguration有效的条件:@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})不存在

@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {

而@EnableWebMvc会导入DelegatingWebMvcConfiguration,DelegatingWebMvcConfiguration继承WebMvcConfigurationSupport,因此自动配置部分失效,但是自定义拓展的(例如手动导入的thymeleaf还能用)还能用:
在这里插入图片描述
在这里插入图片描述
总结:
1.只要看到xxxConfiguration,一定是springmvc拓展功能。
2.WebMvcAutoConfiguration、xxxConfiguration以及自己定义的拓展的xxxConfiguration层级关系:
WebMvcAutoConfiguration是自动配置类,WebMvcAutoConfiguration包含xxxConfiguration,自己定义的拓展的xxxConfiguration和xxxConfiguration同层级,都是组件,里面的是写组件功能(方法),都实现WebMvcConfig接口,WebMvcConfig接口里面有一些webmvc的一些没有实现的功能。

以上是关于拓展springmvc原理详解的主要内容,如果未能解决你的问题,请参考以下文章

面试题:SpringMVC工作原理详解

详解Spring mvc工作原理及源码分析

SpringMVC原理及非注解配置详解

详解SpringMVC中Controller的方法中参数的工作原理

你真的了解吗?一文详解底层原理!

MVC - 参数原理详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]