1. 程式人生 > >SpringMVC的預設配置與修改

SpringMVC的預設配置與修改

【1】SpringBoot對SpringMVC的預設配置

官網地址如下:

如下圖所示,SpringBoot提供了SpringMVC的自動配置幫助進行Web專案開發。

這裡寫圖片描述
翻譯如下:

① 配置了檢視解析器bean;
② 靜態資源訪問支援,包括WebJars;
③ 註冊了轉換器和格式化器;
④ 請求響應資訊轉換器;
⑤ 註冊資訊校驗解析器–定義錯誤程式碼生成規則;
⑥ 靜態頁面index.html支援(歡迎頁);
⑦ 專案瀏覽器圖示支援;
⑧ 可配置web資料繫結初始化器bean的自動使用。

【2】Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

ContentNegotiatingViewResolver:組合所有的檢視解析器的,原始碼分析如下。

首先在WebMvcAutoConfiguration配置類中新增元件ContentNegotiatingViewResolver

@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    ContentNegotiatingViewResolver resolver = new
ContentNegotiatingViewResolver(); resolver.setContentNegotiationManager( beanFactory.getBean(ContentNegotiationManager.class)); //注意這裡 // ContentNegotiatingViewResolver uses all the other view resolvers to locate // a view so it should have a high precedence resolver.setOrder(Ordered.HIGHEST_PRECEDENCE); return
resolver; }

ContentNegotiatingViewResolver類如下:

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

    private ContentNegotiationManager contentNegotiationManager;

    private final ContentNegotiationManagerFactoryBean cnmFactoryBean = new ContentNegotiationManagerFactoryBean();

    private boolean useNotAcceptableStatusCode = false;

    private List<View> defaultViews;

    private List<ViewResolver> viewResolvers;
    // 這裡有一個檢視解析器list

    private int order = Ordered.HIGHEST_PRECEDENCE;
    //...
}

initServletContext方法如下:

@Override
    protected void initServletContext(ServletContext servletContext) {
        Collection<ViewResolver> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), ViewResolver.class).values();
        if (this.viewResolvers == null) {
            this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.size());
            for (ViewResolver viewResolver : matchingBeans) {
                if (this != viewResolver) {
                    this.viewResolvers.add(viewResolver);
                }
            }
        }
        else {
            for (int i = 0; i < viewResolvers.size(); i++) {
                if (matchingBeans.contains(viewResolvers.get(i))) {
                    continue;
                }
                String name = viewResolvers.get(i).getClass().getName() + i;
                getApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolvers.get(i), name);
            }

        }
        if (this.viewResolvers.isEmpty()) {
            logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " +
                    "'viewResolvers' property on the ContentNegotiatingViewResolver");
        }
        AnnotationAwareOrderComparator.sort(this.viewResolvers);
        this.cnmFactoryBean.setServletContext(servletContext);
    }

即,拿到系統中所有的檢視解析器!

那麼如何定製自己的檢視解析器呢?

可以自己給容器中新增一個檢視解析器,SpringBoot 會自動的將其組合進來。

示例如下(使用@Bean在容器中新增自己的檢視解析器):

    @Bean
    public ViewResolver myViewReolver(){
        return new MyViewResolver();
    }

【3】Automatic registration of Converter, GenericConverter, and Formatter beans.

Converter:轉換器;如頁面表單資料到後臺public String hello(User user):型別轉換使用Converter。

Formatter 格式化器; 如頁面表單中日期2017.12.17到後臺Date。

原始碼如下:

@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {
    return new DateFormatter(this.mvcProperties.getDateFormat());
}

@Override
public void addFormatters(FormatterRegistry registry) {
    for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
        registry.addConverter(converter);
    }
    //自己新增的格式化器轉換器,我們只需要放在容器中即可
    for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
        registry.addConverter(converter);
    }
    for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
        registry.addFormatter(formatter);
    }
}

private <T> Collection<T> getBeansOfType(Class<T> type) {
    return this.beanFactory.getBeansOfType(type).values();
}

其中預設的日期格式如下:

    /**
     * Date format to use (e.g. dd/MM/yyyy).
     */
    private String dateFormat;

通常需要在配置檔案中修改:

spring.mvc.date-format=yyyy-MM-dd//根據需要自定義

【4】HttpMessageConverters

SpringMVC用來轉換Http請求和響應的,如將返回值User物件轉換為JSON型別。SpringBoot預設從容器中獲取所有的HttpMessageConverter。

如果自己給容器中新增HttpMessageConverter,則只需將自己的元件註冊到容器中。

官方文件說明如下:

Spring MVC uses the HttpMessageConverter interface to convert HTTP requests and responses. Sensible defaults are included out of the box. For example, objects can be automatically converted to JSON (by using the Jackson library) or XML (by using the Jackson XML extension, if available, or by using JAXB if the Jackson XML extension is not available). By default, strings are encoded in UTF-8.

If you need to add or customize converters, you can use Spring Boot’s HttpMessageConverters class, as shown in the following listing:

import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration
public class MyConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = ...
        HttpMessageConverter<?> another = ...
        return new HttpMessageConverters(additional, another);
    }

}

SpringBoot中,返回型別為type bean或map時,將會自動轉換為JSON,使用的轉換器為MappingJackson2HttpMessageConverter,原始碼如下:

/**
 * Implementation of {@link org.springframework.http.converter.HttpMessageConverter} that can read and
 * write JSON using <a href="http://wiki.fasterxml.com/JacksonHome">Jackson 2.x's</a> {@link ObjectMapper}.
 *
 * <p>This converter can be used to bind to typed beans, or untyped {@code HashMap} instances.
 *
 * <p>By default, this converter supports {@code application/json} and {@code application/*+json}
 * with {@code UTF-8} character set. This can be overridden by setting the
 * {@link #setSupportedMediaTypes supportedMediaTypes} property.
 *
 * <p>The default constructor uses the default configuration provided by {@link Jackson2ObjectMapperBuilder}.
 *
 * <p>Compatible with Jackson 2.6 and higher, as of Spring 4.3.
 *
 * @author Arjen Poutsma
 * @author Keith Donald
 * @author Rossen Stoyanchev
 * @author Juergen Hoeller
 * @author Sebastien Deleuze
 * @since 3.1.2
 */
public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
//...
}

【5】修改SpringBoot的預設配置

首先說明一下幾點:

1SpringBoot在自動配置很多元件的時候,先看容器中有沒有使用者自己配置的(@Bean、@Component)如果有就用使用者配置的,如果沒有,才自動配置;如果有些元件可以有多個(ViewResolver)將使用者配置的和自己預設的組合起來;

2)、在SpringBoot中會有非常多的xxxConfigurer幫助我們進行擴充套件配置;

3)、在SpringBoot中會有很多的xxxCustomizer幫助我們進行定製配置。

其次,官網文件說明如下:

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

5.1 擴充套件預設配置

如下,編寫一個配置類,是WebMvcConfigurerAdapter型別,不能標註@EnableWebMvc:

@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    //重寫方法
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
       // super.addViewControllers(registry);
       //瀏覽器傳送 /success 請求來到 success
        registry.addViewController("/success").setViewName("success");
    }
    //新增元件
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }

其中viewController對應以前SpringMVC的xml配置如下:

<mvc:view‐controller path="/success" viewname="success"/>

原理分析原始碼如下:

① WebMvcAutoConfiguration是SpringMVC的自動配置類;

② 在做其他自動配置時會匯入;@Import(EnableWebMvcConfiguration.class)

@Configuration
@Import(EnableWebMvcConfiguration.class)//這裡
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {

    private static final Log logger = LogFactory
            .getLog(WebMvcConfigurerAdapter.class);

    private final ResourceProperties resourceProperties;

    private final WebMvcProperties mvcProperties;

    private final ListableBeanFactory beanFactory;

    private final HttpMessageConverters messageConverters;

    final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
    //...
}

EnableWebMvcConfiguration類如下:

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
    //...
}

同樣是個配置類,繼承DelegatingWebMvcConfiguration ,其類如下:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
//拿到系統中所有的WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
        this.configurers.addWebMvcConfigurers(configurers);
    }
}
//...
}

WebMvcConfigurerAdapter是WebMvcConfigurer的實現類,WebMvcAutoConfigurationAdapter繼承自WebMvcConfigurerAdapter。

總結 : 容器中所有的WebMvcConfigurer都會一起起作用,我們的配置類也會被呼叫。

根據原始碼分析,還可以在自定義配置類中使用如下方式進行配置擴充套件:

 //所有的WebMvcConfigurerAdapter元件都會一起起作用
 @Bean //將元件註冊在容器
 public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
     WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
         @Override
         public void addViewControllers(ViewControllerRegistry registry) {
             registry.addViewController("/").setViewName("login");
             registry.addViewController("/index.html").setViewName("login");
             registry.addViewController("/main.html").setViewName("dashboard");
         }

         //註冊攔截器
         @Override
         public void addInterceptors(InterceptorRegistry registry) {
             //super.addInterceptors(registry);
             //靜態資源;  *.css , *.js
             //SpringBoot已經做好了靜態資源對映
//                registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
//                        .excludePathPatterns("/index.html","/","/user/login");
         }
     };
     return adapter;
 }

5.2 全面接管SpringMVC

需要在配置類中新增@EnableWebMvc即可,所有的SpringMVC的自動配置都失效了,通常不這麼幹。

//使用WebMvcConfigurerAdapter可以來擴充套件SpringMVC的功能
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
    // super.addViewControllers(registry);
    //瀏覽器傳送 /success 請求來到 success
    registry.addViewController("/success").setViewName("success");
    }
}

為什麼新增該註解,就會將SpringBoot對SpringMVC的預設自動配置失效呢?

分析原始碼如下 :

① @EnableWebMvc的核心

@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
    //..
}

② DelegatingWebMvcConfiguration

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

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

很眼熟對不對,這貨繼承自WebMvcConfigurationSupport 。

③ 檢視WebMvcAutoConfiguration

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
        WebMvcConfigurerAdapter.class })
//注意這裡!!!
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

只有在WebMvcConfigurationSupport不存在的情況下,WebMvcAutoConfiguration 才生效!

而@EnableWebMvc將WebMvcConfigurationSupport元件匯入進來,但是WebMvcConfigurationSupport只有SpringMVC最基本的功能。

【6】DispatcherServletAutoConfiguration與WebMvcAutoConfiguration

在這兩個類中,SpringBoot為我們預設配置了SpringMVC的諸多元件,包括DispatcherServlet等,可以追蹤原始碼進行檢視。

DispatcherServletAutoConfiguration 原始碼如下:

 */
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

    /*
     * The bean name for a DispatcherServlet that will be mapped to the root URL "/"
     */
    public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

    /*
     * The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
     */
    public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

    @Configuration
    @Conditional(DefaultDispatcherServletCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)
    protected static class DispatcherServletConfiguration {

        private final WebMvcProperties webMvcProperties;

        public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
            this.webMvcProperties = webMvcProperties;
        }

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServlet dispatcherServlet() {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(
                    this.webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(
                    this.webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(
                    this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
            return dispatcherServlet;
        }

        @Bean
        @ConditionalOnBean(MultipartResolver.class)
        @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
        public MultipartResolver multipartResolver(MultipartResolver resolver) {
            // Detect if the user has created a MultipartResolver but named it incorrectly
            return resolver;
        }

    }

    @Configuration
    @Conditional(DispatcherServletRegistrationCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)
    @Import(DispatcherServletConfiguration.class)
    protected static class DispatcherServletRegistrationConfiguration {

        private final ServerProperties serverProperties;

        private final WebMvcProperties webMvcProperties;

        private final MultipartConfigElement multipartConfig;

        public DispatcherServletRegistrationConfiguration(
                ServerProperties serverProperties, WebMvcProperties webMvcProperties,
                ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
            this.serverProperties = serverProperties;
            this.webMvcProperties = webMvcProperties;
            this.multipartConfig = multipartConfigProvider.getIfAvailable();
        }

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public ServletRegistrationBean dispatcherServletRegistration(
                DispatcherServlet dispatcherServlet) {
            ServletRegistrationBean registration = new ServletRegistrationBean(
                    dispatcherServlet, this.serverProperties.getServletMapping());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(
                    this.webMvcProperties.getServlet().getLoadOnStartup());
            if (this.multipartConfig != null) {
                registration.setMultipartConfig(this.multipartConfig);
            }
            return registration;
        }

    }

    @Order(Ordered.LOWEST_PRECEDENCE - 10)
    private static class DefaultDispatcherServletCondition extends SpringBootCondition {

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context,
                AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage
                    .forCondition("Default DispatcherServlet");
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            List<String> dispatchServletBeans = Arrays.asList(beanFactory
                    .getBeanNamesForType(DispatcherServlet.class, false, false));
            if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
                return ConditionOutcome.noMatch(message.found("dispatcher servlet bean")
                        .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
            }
            if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
                return ConditionOutcome
                        .noMatch(message.found("non dispatcher servlet bean")
                                .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
            }
            if (dispatchServletBeans.isEmpty()) {
                return ConditionOutcome
                        .match(message.didNotFind("dispatcher servlet beans").atAll());
            }
            return ConditionOutcome.match(message
                    .found("dispatcher servlet bean", "dispatcher servlet beans")
                    .items(Style.QUOTE, dispatchServletBeans)
                    .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
        }

    }

    @Order(Ordered.LOWEST_PRECEDENCE - 10)
    private static class DispatcherServletRegistrationCondition
            extends SpringBootCondition {

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context,
                AnnotatedTypeMetadata metadata) {
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
            if (!outcome.isMatch()) {
                return outcome;
            }
            return checkServletRegistration(beanFactory);
        }

        private ConditionOutcome checkDefaultDispatcherName(
                ConfigurableListableBeanFactory beanFactory) {
            List<String> servlets = Arrays.asList(beanFactory
                    .getBeanNamesForType(DispatcherServlet.class, false, false));
            boolean containsDispatcherBean = beanFactory
                    .containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            if (containsDispatcherBean
                    && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
                return ConditionOutcome
                        .noMatch(startMessage().found("non dispatcher servlet")
                                .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
            }
            return ConditionOutcome.match();
        }

        private ConditionOutcome checkServletRegistration(
                ConfigurableListableBeanFactory beanFactory) {
            ConditionMessage.Builder message = startMessage();
            List<String> registrations = Arrays.asList(beanFactory
                    .getBeanNamesForType(ServletRegistrationBean.class, false, false));
            boolean containsDispatcherRegistrationBean = beanFactory
                    .containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
            if (registrations.isEmpty()) {
                if (containsDispatcherRegistrationBean) {
                    return ConditionOutcome
                            .noMatch(message.found("non servlet registration bean").items(
                                    DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
                }
                return ConditionOutcome
                        .match(message.didNotFind("servlet registration bean").atAll());
            }
            if (registrations
                    .contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
                return ConditionOutcome.noMatch(message.found("servlet registration bean")
                        .items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
            }
            if (containsDispatcherRegistrationBean) {
                return ConditionOutcome
                        .noMatch(message.found("non servlet registration bean").items(
                                DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
            }
            return ConditionOutcome.match(message.found("servlet registration beans")
                    .items(Style.QUOTE, registrations).append("and none is named "
                            + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
        }

        private ConditionMessage.Builder startMessage() {
            return ConditionMessage.forCondition("DispatcherServlet Registration");
        }

    }

}

WebMvcAutoConfiguration 原始碼如下:

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
        WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

    public static final String DEFAULT_PREFIX = "";

    public static final String DEFAULT_SUFFIX = "";

    @Bean
    @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
    public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
        return new OrderedHiddenHttpMethodFilter();
    }

    @Bean
    @ConditionalOnMissingBean(HttpPutFormContentFilter.class)
    @ConditionalOnProperty(prefix = "spring.mvc.formcontent.putfilter", name = "enabled", matchIfMissing = true)
    public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {
        return new OrderedHttpPutFormContentFilter();
    }

    // Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not
    // on the classpath
    @Configuration
    @Import(EnableWebMvcConfiguration.class)
    @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
    public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {

        private static final Log logger = LogFactory
                .getLog(WebMvcConfigurerAdapter.class);

        private final ResourceProperties resourceProperties;

        private final WebMvcProperties mvcProperties;

        private final ListableBeanFactory beanFactory;

        private final HttpMessageConverters messageConverters;

        final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;

        public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
                WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
                @Lazy HttpMessageConverters messageConverters,
                ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
            this.resourceProperties = resourceProperties;
            this.mvcProperties = mvcProperties;
            this.beanFactory = beanFactory;
            this.messageConverters = messageConverters;
            this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
                    .getIfAvailable();
        }

        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.addAll(this.messageConverters.getConverters());
        }

        @Override
        public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
            Long timeout = this.mvcProperties.getAsync().getRequestTimeout();
            if (timeout != null) {
                configurer.setDefaultTimeout(timeout);
            }
        }

        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            Map<String, MediaType> mediaTypes = this.mvcProperties.getMediaTypes();
            for (Entry<String, MediaType> mediaType : mediaTypes.entrySet()) {
                configurer.mediaType(mediaType.getKey(), mediaType.getValue());
            }
        }

        @Bean
        @ConditionalOnMissingBean
        public InternalResourceViewResolver defaultViewResolver() {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix(this.mvcProperties.getView().getPrefix());
            resolver.setSuffix(this.mvcProperties.getView().getSuffix());
            return resolver;
        }

        @Bean
        @ConditionalOnBean(View.class)
        @ConditionalOnMissingBean
        public BeanNameViewResolver beanNameViewResolver() {
            BeanNameViewResolver resolver = new BeanNameViewResolver();
            resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
            return resolver;
        }

        @Bean
        @ConditionalOnBean(ViewResolver.class)
        @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
        public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
            ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
            resolver.setContentNegotiationManager(
                    beanFactory.getBean(ContentNegotiationManager.class));
            // ContentNegotiatingViewResolver uses all the other view resolvers to locate
            // a view so it should have a high precedence
            resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
            return resolver;
        }

        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
        public LocaleResolver localeResolver() {
            if (this.mvcProperties
                    .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
                return new FixedLocaleResolver(this.mvcProperties.getLocale());
            }
            AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
            localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
            return localeResolver;
        }

        @Bean
        @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
        public Formatter<Date> dateFormatter() {
            return new DateFormatter(this.mvcProperties.getDateFormat());
        }

        @Override
        public MessageCodesResolver getMessageCodesResolver() {
            if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
                DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
                resolver.setMessageCodeFormatter(
                        this.mvcProperties.getMessageCodesResolverFormat());
                return resolver;
            }
            return null;
        }

        @Override
        public void addFormatters(FormatterRegistry registry) {
            for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
                registry.addConverter(converter);
            }
            for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
                registry.addConverter(converter);
            }
            for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
                registry.addFormatter(formatter);
            }
        }

        private <T> Collection<T> getBeansOfType(Class<T> type) {
            return this.beanFactory.getBeansOfType(type).values();
        }

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            if (!this.resourceProperties.isAddMappings()) {
                logger.debug("Default resource handling disabled");
                return;
            }
            Integer cachePeriod = this.resourceProperties.getCachePeriod();
            if (!registry.hasMappingForPattern("/webjars/**")) {
                customizeResourceHandlerRegistration(
                        registry.addResourceHandler("/webjars/**")
                                .addResourceLocations(
                                        "classpath:/META-INF/resources/webjars/")
                        .setCachePeriod(cachePeriod));
            }
            String staticPathPattern = this.mvcProperties.getStaticPathPattern();
            if (!registry.hasMappingForPattern(staticPathPattern)) {
                customizeResourceHandlerRegistration(
                        registry.addResourceHandler(staticPathPattern)
                                .addResourceLocations(
                                        this.resourceProperties.getStaticLocations())
                        .setCachePeriod(cachePeriod));
            }
        }

        @Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(
                ResourceProperties resourceProperties) {
            return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
                    this.mvcProperties.getStaticPathPattern());
        }

        private void customizeResourceHandlerRegistration(
                ResourceHandlerRegistration registration) {
            if (this.resourceHandlerRegistrationCustomizer != null) {
                this.resourceHandlerRegistrationCustomizer.customize(registration);
            }

        }

        @Bean
        @ConditionalOnMissingBean({ RequestContextListener.class,
                RequestContextFilter.class })
        public static RequestContextFilter requestContextFilter() {
            return new OrderedRequestContextFilter();
        }

        @Configuration
        @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
        public static class FaviconConfiguration {

            private final ResourceProperties resourceProperties;

            public FaviconConfiguration(ResourceProperties resourceProperties) {
                this.resourceProperties = resourceProperties;
            }

            @Bean
            public SimpleUrlHandlerMapping faviconHandlerMapping() {
                SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
                mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
                mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
                        faviconRequestHandler()));
                return mapping;
            }

            @Bean
            public ResourceHttpRequestHandler faviconRequestHandler() {
                ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
                requestHandler
                        .setLocations(this.resourceProperties.getFaviconLocations());
                return requestHandler;
            }

        }

    }

    /**
     * Configuration equivalent to {@code @EnableWebMvc}.
     */
    @Configuration
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

        private final WebMvcProperties mvcProperties;

        private final ListableBeanFactory beanFactory;

        private final WebMvcRegistrations mvcRegistrations;

        public EnableWebMvcConfiguration(
                ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
                ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
                ListableBeanFactory beanFactory) {
            this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
            this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
            this.beanFactory = beanFactory;
        }

        @Bean
        @Override
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
            adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null ? true
                    : this.mvcProperties.isIgnoreDefaultModelOnRedirect());
            return adapter;
        }

        @Override
        protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
            if (this.mvcRegistrations != null
                    && this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) {
                return this.mvcRegistrations.getRequestMappingHandlerAdapter();
            }
            return super.createRequestMappingHandlerAdapter();
        }

        @Bean
        @Primary
        @Override
        public RequestMappingHandlerMapping requestMappingHandlerMapping() {
            // Must be @Primary for MvcUriComponentsBuilder to work
            return super.requestMappingHandlerMapping();
        }

        @Bean
        @Override
        public Validator mvcValidator() {
            if (!ClassUtils.isPresent("javax.validation.Validator",
                    getClass().getClassLoader())) {
                return super.mvcValidator();
            }
            return WebMvcValidator.get(getApplicationContext(), getValidator());
        }

        @Override
        protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
            if (this.mvcRegistrations != null
                    && this.mvcRegistrations.getRequestMappingHandlerMapping() != null) {
                return this.mvcRegistrations.getRequestMappingHandlerMapping();
            }
            return super.createRequestMappingHandlerMapping();
        }

        @Override
        protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
            try {
                return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
            }
            catch (NoSuchBeanDefinitionException ex) {
                return super.getConfigurableWebBindingInitializer();
            }
        }

        @Override
        protected ExceptionHandlerExceptionResolver 
            
           

相關推薦

SpringMVC預設配置修改

【1】SpringBoot對SpringMVC的預設配置 官網地址如下: 如下圖所示,SpringBoot提供了SpringMVC的自動配置幫助進行Web專案開發。 翻譯如下: ① 配置了檢視解析器bean; ② 靜態資源訪問支援,包括Web

SpringBoot日記——SpringMvc自動配置擴展篇

讀取 發送 registry 設置 取數據 gmv inf com 自動配置 為了讓SpringBoot保持對SpringMVC的全面支持和擴展,而且還要維持SpringBoot不寫xml配置的優勢,我們需要添加一些簡單的配置類即可實現; 通常我們使用的最多的註解是:

SpringBoot + mybatis預設配置手動配置步驟對比一(預設配置

前提環境已經搭建完成 Eclipse + SpringBoot + mybatis預設配置: 1.建立工程 File->new->other->Spring Boot -> Spring Starter Project ->選擇jdk等資訊

mysql5.6.34在預設配置檔案修改字符集為utf8後重啟mysql服務沒效果

1:事情是這樣的,我下載了一個mysql5.6.34版本(windows版本的),下載下來后里面只有個my-default.ini,然後我就直接在my-default.ini 裡面配置basedir,datadir,port。 2:然後我的javaweb程式連線資料庫後,出

Tomcat6NIO配置修改

JVM設定 堆的尺寸 -Xmssize in bytes     設定Java堆的初始尺寸,預設尺寸是2097152 (2MB)。這個值必須是1024個位元組(1KB)的倍數,且比它大。(-server選項把預設尺寸增加到32M。) -Xmnsize in bytes     為Eden物件設定初始

#20 ifconfig、route、netstat、ip、ss命令詳解修改主機名網卡配置文件

ifconfig、route、netstat、ip、ss命令詳解與修改主機名與網卡配置文件 網絡的結構: 硬件:計算機、互聯設備、網絡設備 軟件:操作系統、協議、應用程序、數據庫 網絡的功能: 資源共享:目的 數據通信:手段 網絡通信模型 ISO/OSI: 應用層

【編程工具配置】Pycharm安裝修改中文界面教程

激活 load com down col section 復制 工具配置 鏈接 【windows】 1.到官網下載Pycharm最新版 https://www.jetbrains.com/pycharm/download/#section=windows 2

linux查看修改交換內存配置(解決zabbix-agent啟動報錯)

修改 上啟 報錯 mem 原因 mit sys space har 問題 zabbix-agent在一臺centos6.5上啟動報錯: cannot allocate shared memory of size 949056: [28] No space left on

Springmvc攔截器的配置應用

今天介紹下springmvc學習中的攔截器,常用我們在訪問專案的時候會攔截判斷使用者是否登入等,有點類似於我們在servlet中使用的filter過濾器 1.那麼springmvc攔截器是在什麼地方攔截的(執行規則)? 2.springmvc攔截器是怎麼在專案中應用的? 我們定義一個攔截

VS2008中自定義C++工程模板修改新建檔案預設編碼的辦法

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

springmvc 支援物件json 自動轉換的配置

基於maven的工程, 需要在pom.xml中新增如下依賴 <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl<

關於AndroidStudio 配置預設路徑的修改

原文地址:https://my.oschina.net/kingfrog/blog/1633022 AndroidStudio的配置預設路徑在C:\Users\使用者名稱\.AndroidStudio3.0 下,在這裡會有一個缺點是C盤會常常空間不夠用,所以我就想改到其他盤的。看圖:

【SpringBoot】 啟動時,修改預設配置檔名稱

前言 spring boot version : 2.0.0.RELEASE maven version : 3.5.0 在不同的環境下,依賴的外部資源是不一樣的。比如,在windows上開發,在linux下進行測試時,無法將開發環境和測試i環境構造成一致的。

六、springMVC-mvc.xml 配置檔案片段講解 (未使用預設配置檔名)

Xml程式碼 <context:component-scan/> 掃描指定的包中的類上的註解,常用的註解有: @Controller 宣告Action元件 @Service    宣告Service元件    @Service("myMovieLister

SpringSpringMVC配置模板

Spring的配置模板 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

【Vue】quill-editor富文字編輯器元件的運用修改配置使圖片上傳到伺服器

前言:Vue的生態已經越來越繁榮,越來越多有趣好用的元件加入的生態中了。quill-editor富文字編輯器就是很好用的元件之一。 一、quill-editor的安裝與使用 ①、安裝 npm install vue-quill-editor --save ②、

修改IDEA的預設配置

  在使用IDEA的時候,我們一般情況下都不會採用IDEA預設自帶的專案配置,而是往往自定義自己的專案配置資訊,其配置方式如下,雙擊IDEA圖示,進入IDEA的主介面,點選圖示的【Configure】選項:   在彈出的下拉選單中,選擇【Projec

springmvc核心配置檔案前端控制器

<?xml version="1.0"encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-ins

配置阿里雲 II】(續)xampp多站點配置(設定虛擬域名)(頂替預設載入dashboard修改方式)

在xampp環境中,為了方便的管理多個專案,需要通過配置相關檔案來設定多個虛擬站點。        如果不配置虛擬埠,專案上線會發生如下情況:        在域名繫結ip後(應用程式池中網站也設定好指向),dns解析沒問題情況下 但是本地瀏覽器訪問時用域名+專案名訪問時

MySQL列的預設值主鍵索引自增 刪除增加修改

1. 某些列不插入內容,值是多少?  自動填充NULL2. NOT NULL是幹嘛的?  不能為NULL型別,因為NULL型別查詢的時候需要加語句 IS NULL 或者IS NOT NULL  效率低,查詢速度慢,開發中我們一般不可以設定為預設的能插入NULL型別。  所以會使用NOT NULL來限制使用NU