SpringBoot------MVC装配原理以及SpringMVC扩容(十三)

SpringBoot------MVC装配原理

官方文档:https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications
SpringMVC自动配置:
1.包含一些视图解析器,(ContentNegotiatingViewResolver、BeanNameViewResolver)
2.支持静态资源,包括webJars
3.自动注册类型转换器以及格式化器(FormatterRegistry)。(前端提供对象,后台自动封装接收)【默认使用dd/MM/yyyy格式】,可以通过在yaml文件中自定义日期格式化规则。
在这里插入图片描述

4.支持HttpMessage消息转换。转换http的请求和响应。
5.自动注册定义一些错误消息的控制 。
6.首页映射
7.图标自定义
8.支持一些初始化配置的数据绑定。
SpringBoot在自动配置很多组件的时候,先看容器中是否有用户自己的配置(如果用户自己配置@Bean),如果有用户配置的,如果没有就用自动配置的;如果组件可以存在多个,比如我们的视图解析器,就将用户配置的和自己默认的组合起来。
如果想自定义的添加配置这些内容,只需要在配置类(配置类需要实现WebMvcConfigurer)上加入@Configuration,并且不能写@EnableWebMvc注解(这个注解代表mvc全面接管)。

ContentNegotiatingViewResolver

import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;

ViewResolver视图解析器接口。

public class ContentNegotiatingViewResolver extends 
WebApplicationObjectSupport implements ViewResolver, Ordered, InitializingBean {
    
    

实现了视图解析器接口(ViewResolver )的类,可以称作视图解析器。
看一下ViewResolver的源码:

public interface ViewResolver {
    
    
    @Nullable
    View resolveViewName(String var1, Locale var2) throws Exception;
    //用于解析视图
}

这个ViewResolver接口中只有一个方法,看ContentNegotiatingViewResolver 是怎么重写该方法的。

 @Nullable
    public View resolveViewName(String viewName, Locale locale) throws Exception {
    
    
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
        List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
        if (requestedMediaTypes != null) {
    
    
        	//获取候选的视图
            List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
            //选择最好的视图
            View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
            if (bestView != null) {
    
    
                return bestView;
            }
        }

        String mediaTypeInfo = this.logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : "";
        if (this.useNotAcceptableStatusCode) {
    
    
            if (this.logger.isDebugEnabled()) {
    
    
                this.logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);
            }

            return NOT_ACCEPTABLE_VIEW;
        } else {
    
    
            this.logger.debug("View remains unresolved" + mediaTypeInfo);
            return null;
        }
    }

this.getCandidateViews:获取候选的视图。
通过不断的遍历,获取候选视图,并将候选视图加入到List集合中,获取所有的候选视图。本质上是从容器中获取这些视图,也就是从Bean中获得,Spring所有东西都是在IOC容器中。

  private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
    
    
        List<View> candidateViews = new ArrayList();
        if (this.viewResolvers != null) {
    
    
            Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
            Iterator var5 = this.viewResolvers.iterator();

            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;
    }

自己实现一个视图解析器。

//全面扩展SpringMVC. 所有请求经过DispatcherServlet 
//将这个类变成配置类
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    
    
//通过@Bean将视图解析器放到容器中
    @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 .doDispatch

public class DispatcherServlet extends FrameworkServlet {
    
    
.....
 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;

                try {
    
    
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
    
    
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
    
    
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
    
    
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    
    
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
    
    
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
    
    
                    dispatchException = var20;
                } catch (Throwable var21) {
    
    
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
    
    
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
    
    
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
    
    
            if (asyncManager.isConcurrentHandlingStarted()) {
    
    
                if (mappedHandler != null) {
    
    
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
    
    
                this.cleanupMultipart(processedRequest);
            }

        }
    }

在doDispatch方法上打上断点,DeBug启动项目,访问页面时能看到会执行到doDispatch方法。
在这里插入图片描述
其中自定义的视图解析器正在其中。自己写一个视图解析器,并且把它注册到Bean里面,就会自动装配。
如果想定制化的功能,只要写这个组件,然后将它交给SpringBoot,SpringBoot就会自动装配。
在这里插入图片描述

视图跳转

//全面扩展SpringMVC.建议使用@Configuration这种操作
//将这个类变成配置类
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    
    
    //视图跳转
    //想要通过地址zhaoyun访问到success页面,那么这个页面必须加入<html lang="en" xmlns:th="http://www.thymeleaf.org"> 并且在templates目录下
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
    
    
        registry.addViewController("/zhaoyun").setViewName("success");
    }
    @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;
        }
    }
}

访问:http://localhost:8080/zhaoyun
在这里插入图片描述
官方文档上同样注明使用@Configuration,但是不能写@EnableWebMvc。

但是为什么不能加入@EnableWebMvc注解

@EnableWebMvc源码:可见EnableWebMvc 没有方法,只有一些注解配置。
导入了一个类:@Import({DelegatingWebMvcConfiguration.class})

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

@EnableWebMvc 这个注解就是导入DelegatingWebMvcConfiguration这个类。
再看WebMvcAutoConfiguration的WebMvcAutoConfigurationAdapter类,也使用到@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})导入的EnableWebMvcConfiguration这个类。


    @Configuration(
        proxyBeanMethods = false
    )
    @Import({
    
    WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
    @EnableConfigurationProperties({
    
    WebMvcProperties.class, ResourceProperties.class, WebProperties.class})
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    
    
        private final WebMvcProperties mvcProperties;
        private final ListableBeanFactory beanFactory;
        private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;
        private final ObjectProvider<DispatcherServletPath> dispatcherServletPath;
        private final ObjectProvider<ServletRegistrationBean<?>> servletRegistrations;
        final WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;

        public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
    
    
            this.mvcProperties = mvcProperties;
            this.beanFactory = beanFactory;

而EnableWebMvcConfiguration继承了 DelegatingWebMvcConfiguration,


    @Configuration(
        proxyBeanMethods = false
    )
    @EnableConfigurationProperties({
    
    WebProperties.class})
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    
    
        private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
        private final Resources resourceProperties;
        private final WebMvcProperties mvcProperties;
        private final WebProperties webProperties;
        private final ListableBeanFactory beanFactory;
        private final WebMvcRegistrations mvcRegistrations;
        private final WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
        private ResourceLoader resourceLoader;

        public EnableWebMvcConfiguration(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, WebProperties webProperties, ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ListableBeanFactory beanFactory) {
    
    
            this.resourceProperties = (Resources)(resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources());
            this.mvcProperties = mvcProperties;
            this.webProperties = webProperties;

DelegatingWebMvcConfiguration 类的setConfigurers方法,

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
    
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    public DelegatingWebMvcConfiguration() {
    
    
    }

    @Autowired(
        required = false
    )
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
    
    
        if (!CollectionUtils.isEmpty(configurers)) {
    
    
        //获取容器中的所有webMvcConfigurer
            this.configurers.addWebMvcConfigurers(configurers);
        }

    }

WebMvcAutoConfiguration有@ConditionalOnMissingBean({WebMvcConfigurationSupport.class}),如果WebMvcConfigurationSupport这个Bean不存在时,这个类才会生效。

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({
    
    Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({
    
    WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({
    
    DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
    
    
    public static final String DEFAULT_PREFIX = "";
    public static final String DEFAULT_SUFFIX = "";
    private static final String SERVLET_LOCATION = "/";

    public WebMvcAutoConfiguration() {
    
    
    }

因此一旦加入@EnableWebMvc注解,DelegatingWebMvcConfiguration类就会生效,而DelegatingWebMvcConfiguration继承了WebMvcConfigurationSupport,一旦WebMvcConfigurationSupport存在WebMvcAutoConfiguration 就不会生效,则整个WebMvc自动装配就不起作用。
在SpringBoot中,有非常多的xxxConfiguration帮助我们扩展配置。它会对Spring原有的东西进行改变或者扩展。

おすすめ

転載: blog.csdn.net/cz_chen_zhuo/article/details/117249908