springboot的Web開發-Web相關配置
一:Spring Boot提供自動配置
通過檢視WebMvcAutoConfiguration及WebMvcProperties的原始碼,可以發現Spring Boot為我們提供瞭如下的自動配置。
1,自動配置的ViewResolver
1)ContentNegotiatingViewResolver
這是Spring MVC提供的一個特殊的ViewResolver,ContentNegotiatingViewResolver不是自己處理View,而是代理給不同的ViewResolver來處理不同的View,所以它有最高的優先順序。
2)BeanNameViewResolver
在控制器(@Controller)中的一個方法的返回值的字串(檢視名)會根據 BeanNameViewResolver去查詢Bean的名稱為返回字串的View來渲染檢視,下面舉個例子
定義BeanNameViewResolver的Bean
@Bean
public BeanNameViewResolver beanNameViewResolver(){
BeanNameViewResolver resolver= new BeanNameViewResolver();
return resolver
}
定義一個View的Bean,名稱為jsonView
@Bean
public MappingJackson2JsonView jsonView(){
MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
return jsonView;
}
在控制器中,返回值為字串jsonView,它會找Bean的名稱為jsonView的檢視來渲染:
@RequestMapping(value = "json",produces = {MediaType.APPLICATION_JSON_VALUE}) public String json(Model model){ Person single = new Person("aa",11); model.addAttribute("single",single); return "jsonView"; }
3)InternalResourceViewResolver
這是一個常用的ViewResolver,主要通過設定字首,字尾,以及控制器中方法來返回檢視名的字串,以得到實際頁面,Spring Boot的原始碼如下:
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
下面是WebMvcAutoConfiguration的原始碼:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure.web;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ResourceProperties.Chain;
import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter;
import org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter;
import org.springframework.boot.web.filter.OrderedRequestContextFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.io.Resource;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.filter.HttpPutFormContentFilter;
import org.springframework.web.filter.RequestContextFilter;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.config.annotation.ResourceChainRegistration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
import org.springframework.web.servlet.mvc.ParameterizableViewController;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
import org.springframework.web.servlet.resource.GzipResourceResolver;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.servlet.resource.ResourceResolver;
import org.springframework.web.servlet.resource.VersionResourceResolver;
import org.springframework.web.servlet.view.BeanNameViewResolver;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
public static final String DEFAULT_PREFIX = "";
public static final String DEFAULT_SUFFIX = "";
public WebMvcAutoConfiguration() {
}
@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();
}
static class OptionalPathExtensionContentNegotiationStrategy implements ContentNegotiationStrategy {
private static final String SKIP_ATTRIBUTE = PathExtensionContentNegotiationStrategy.class.getName() + ".SKIP";
private final ContentNegotiationStrategy delegate;
OptionalPathExtensionContentNegotiationStrategy(ContentNegotiationStrategy delegate) {
this.delegate = delegate;
}
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
Object skip = webRequest.getAttribute(SKIP_ATTRIBUTE, 0);
return skip != null && Boolean.parseBoolean(skip.toString()) ? Collections.emptyList() : this.delegate.resolveMediaTypes(webRequest);
}
}
static final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {
private static final Log logger = LogFactory.getLog(WebMvcAutoConfiguration.WelcomePageHandlerMapping.class);
private WelcomePageHandlerMapping(Resource welcomePage, String staticPathPattern) {
if (welcomePage != null && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage);
ParameterizableViewController controller = new ParameterizableViewController();
controller.setViewName("forward:index.html");
this.setRootHandler(controller);
this.setOrder(0);
}
}
public Object getHandlerInternal(HttpServletRequest request) throws Exception {
Iterator var2 = this.getAcceptedMediaTypes(request).iterator();
MediaType mediaType;
do {
if (!var2.hasNext()) {
return null;
}
mediaType = (MediaType)var2.next();
} while(!mediaType.includes(MediaType.TEXT_HTML));
return super.getHandlerInternal(request);
}
private List<MediaType> getAcceptedMediaTypes(HttpServletRequest request) {
String acceptHeader = request.getHeader("Accept");
return MediaType.parseMediaTypes(StringUtils.hasText(acceptHeader) ? acceptHeader : "*/*");
}
}
private static class ResourceChainResourceHandlerRegistrationCustomizer implements WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer {
@Autowired
private ResourceProperties resourceProperties;
private ResourceChainResourceHandlerRegistrationCustomizer() {
this.resourceProperties = new ResourceProperties();
}
public void customize(ResourceHandlerRegistration registration) {
Chain properties = this.resourceProperties.getChain();
this.configureResourceChain(properties, registration.resourceChain(properties.isCache()));
}
private void configureResourceChain(Chain properties, ResourceChainRegistration chain) {
Strategy strategy = properties.getStrategy();
if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
chain.addResolver(this.getVersionResourceResolver(strategy));
}
if (properties.isGzipped()) {
chain.addResolver(new GzipResourceResolver());
}
if (properties.isHtmlApplicationCache()) {
chain.addTransformer(new AppCacheManifestTransformer());
}
}
private ResourceResolver getVersionResourceResolver(Strategy properties) {
VersionResourceResolver resolver = new VersionResourceResolver();
if (properties.getFixed().isEnabled()) {
String version = properties.getFixed().getVersion();
String[] paths = properties.getFixed().getPaths();
resolver.addFixedVersionStrategy(version, paths);
}
if (properties.getContent().isEnabled()) {
String[] paths = properties.getContent().getPaths();
resolver.addContentVersionStrategy(paths);
}
return resolver;
}
}
interface ResourceHandlerRegistrationCustomizer {
void customize(ResourceHandlerRegistration var1);
}
@Configuration
@ConditionalOnEnabledResourceChain
static class ResourceChainCustomizerConfiguration {
ResourceChainCustomizerConfiguration() {
}
@Bean
public WebMvcAutoConfiguration.ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
return new WebMvcAutoConfiguration.ResourceChainResourceHandlerRegistrationCustomizer();
}
}
@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 = (WebMvcProperties)mvcPropertiesProvider.getIfAvailable();
this.mvcRegistrations = (WebMvcRegistrations)mvcRegistrationsProvider.getIfUnique();
this.beanFactory = beanFactory;
}
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null ? true : this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
return this.mvcRegistrations != null && this.mvcRegistrations.getRequestMappingHandlerAdapter() != null ? this.mvcRegistrations.getRequestMappingHandlerAdapter() : super.createRequestMappingHandlerAdapter();
}
@Bean
@Primary
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return super.requestMappingHandlerMapping();
}
@Bean
public Validator mvcValidator() {
return !ClassUtils.isPresent("javax.validation.Validator", this.getClass().getClassLoader()) ? super.mvcValidator() : WebMvcValidator.get(this.getApplicationContext(), this.getValidator());
}
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return this.mvcRegistrations != null && this.mvcRegistrations.getRequestMappingHandlerMapping() != null ? this.mvcRegistrations.getRequestMappingHandlerMapping() : super.createRequestMappingHandlerMapping();
}
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
try {
return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
} catch (NoSuchBeanDefinitionException var2) {
return super.getConfigurableWebBindingInitializer();
}
}
protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
return this.mvcRegistrations != null && this.mvcRegistrations.getExceptionHandlerExceptionResolver() != null ? this.mvcRegistrations.getExceptionHandlerExceptionResolver() : super.createExceptionHandlerExceptionResolver();
}
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
super.configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
this.addDefaultHandlerExceptionResolvers(exceptionResolvers);
}
if (this.mvcProperties.isLogResolvedException()) {
Iterator var2 = exceptionResolvers.iterator();
while(var2.hasNext()) {
HandlerExceptionResolver resolver = (HandlerExceptionResolver)var2.next();
if (resolver instanceof AbstractHandlerExceptionResolver) {
((AbstractHandlerExceptionResolver)resolver).setWarnLogCategory(resolver.getClass().getName());
}
}
}
}
@Bean
public ContentNegotiationManager mvcContentNegotiationManager() {
ContentNegotiationManager manager = super.mvcContentNegotiationManager();
List<ContentNegotiationStrategy> strategies = manager.getStrategies();
ListIterator iterator = strategies.listIterator();
while(iterator.hasNext()) {
ContentNegotiationStrategy strategy = (ContentNegotiationStrategy)iterator.next();
if (strategy instanceof PathExtensionContentNegotiationStrategy) {
iterator.set(new WebMvcAutoConfiguration.OptionalPathExtensionContentNegotiationStrategy(strategy));
}
}
return manager;
}
}
@Configuration
@Import({WebMvcAutoConfiguration.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 WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, @Lazy HttpMessageConverters messageConverters, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConverters = messageConverters;
this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
}
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.addAll(this.messageConverters.getConverters());
}
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
Long timeout = this.mvcProperties.getAsync().getRequestTimeout();
if (timeout != null) {
configurer.setDefaultTimeout(timeout.longValue());
}
}
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaTypes = this.mvcProperties.getMediaTypes();
Iterator var3 = mediaTypes.entrySet().iterator();
while(var3.hasNext()) {
Entry<String, MediaType> mediaType = (Entry)var3.next();
configurer.mediaType((String)mediaType.getKey(), (MediaType)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(2147483637);
return resolver;
}
@Bean
@ConditionalOnBean({ViewResolver.class})
@ConditionalOnMissingBean(
name = {"viewResolver"},
value = {ContentNegotiatingViewResolver.class}
)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
resolver.setOrder(-2147483648);
return resolver;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(
prefix = "spring.mvc",
name = {"locale"}
)
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
} else {
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());
}
public MessageCodesResolver getMessageCodesResolver() {
if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());
return resolver;
} else {
return null;
}
}
public void addFormatters(FormatterRegistry registry) {
Iterator var2 = this.getBeansOfType(Converter.class).iterator();
while(var2.hasNext()) {
Converter<?, ?> converter = (Converter)var2.next();
registry.addConverter(converter);
}
var2 = this.getBeansOfType(GenericConverter.class).iterator();
while(var2.hasNext()) {
GenericConverter converter = (GenericConverter)var2.next();
registry.addConverter(converter);
}
var2 = this.getBeansOfType(Formatter.class).iterator();
while(var2.hasNext()) {
Formatter<?> formatter = (Formatter)var2.next();
registry.addFormatter(formatter);
}
}
private <T> Collection<T> getBeansOfType(Class<T> type) {
return this.beanFactory.getBeansOfType(type).values();
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Integer cachePeriod = this.resourceProperties.getCachePeriod();
if (!registry.hasMappingForPattern("/webjars/**")) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(cachePeriod));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(this.resourceProperties.getStaticLocations()).setCachePeriod(cachePeriod));
}
}
}
@Bean
public WebMvcAutoConfiguration.WelcomePageHandlerMapping welcomePageHandlerMapping(ResourceProperties resourceProperties) {
return new WebMvcAutoConfiguration.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(-2147483647);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", this.faviconRequestHandler()));
return mapping;
}
@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(this.resourceProperties.getFaviconLocations());
return requestHandler;
}
}
}
}
WebMvcProperties的原始碼如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure.web;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.MediaType;
import org.springframework.validation.DefaultMessageCodesResolver.Format;
@ConfigurationProperties(
prefix = "spring.mvc"
)
public class WebMvcProperties {
private Format messageCodesResolverFormat;
private Locale locale;
private WebMvcProperties.LocaleResolver localeResolver;
private String dateFormat;
private boolean dispatchTraceRequest;
private boolean dispatchOptionsRequest;
private boolean ignoreDefaultModelOnRedirect;
private boolean throwExceptionIfNoHandlerFound;
private boolean logResolvedException;
private Map<String, MediaType> mediaTypes;
private String staticPathPattern;
private final WebMvcProperties.Async async;
private final WebMvcProperties.Servlet servlet;
private final WebMvcProperties.View view;
public WebMvcProperties() {
this.localeResolver = WebMvcProperties.LocaleResolver.ACCEPT_HEADER;
this.dispatchTraceRequest = false;
this.dispatchOptionsRequest = true;
this.ignoreDefaultModelOnRedirect = true;
this.throwExceptionIfNoHandlerFound = false;
this.logResolvedException = false;
this.mediaTypes = new LinkedHashMap();
this.staticPathPattern = "/**";
this.async = new WebMvcProperties.Async();
this.servlet = new WebMvcProperties.Servlet();
this.view = new WebMvcProperties.View();
}
public Format getMessageCodesResolverFormat() {
return this.messageCodesResolverFormat;
}
public void setMessageCodesResolverFormat(Format messageCodesResolverFormat) {
this.messageCodesResolverFormat = messageCodesResolverFormat;
}
public Locale getLocale() {
return this.locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public WebMvcProperties.LocaleResolver getLocaleResolver() {
return this.localeResolver;
}
public void setLocaleResolver(WebMvcProperties.LocaleResolver localeResolver) {
this.localeResolver = localeResolver;
}
public String getDateFormat() {
return this.dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
public boolean isIgnoreDefaultModelOnRedirect() {
return this.ignoreDefaultModelOnRedirect;
}
public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
}
public boolean isThrowExceptionIfNoHandlerFound() {
return this.throwExceptionIfNoHandlerFound;
}
public void setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound) {
this.throwExceptionIfNoHandlerFound = throwExceptionIfNoHandlerFound;
}
public boolean isLogResolvedException() {
return this.logResolvedException;
}
public void setLogResolvedException(boolean logResolvedException) {
this.logResolvedException = logResolvedException;
}
public Map<String, MediaType> getMediaTypes() {
return this.mediaTypes;
}
public void setMediaTypes(Map<String, MediaType> mediaTypes) {
this.mediaTypes = mediaTypes;
}
public boolean isDispatchOptionsRequest() {
return this.dispatchOptionsRequest;
}
public void setDispatchOptionsRequest(boolean dispatchOptionsRequest) {
this.dispatchOptionsRequest = dispatchOptionsRequest;
}
public boolean isDispatchTraceRequest() {
return this.dispatchTraceRequest;
}
public void setDispatchTraceRequest(boolean dispatchTraceRequest) {
this.dispatchTraceRequest = dispatchTraceRequest;
}
public String getStaticPathPattern() {
return this.staticPathPattern;
}
public void setStaticPathPattern(String staticPathPattern) {
this.staticPathPattern = staticPathPattern;
}
public WebMvcProperties.Async getAsync() {
return this.async;
}
public WebMvcProperties.Servlet getServlet() {
return this.servlet;
}
public WebMvcProperties.View getView() {
return this.view;
}
public static enum LocaleResolver {
FIXED,
ACCEPT_HEADER;
private LocaleResolver() {
}
}
public static class View {
private String prefix;
private String suffix;
public View() {
}
public String getPrefix() {
return this.prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return this.suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
public static class Servlet {
private int loadOnStartup = -1;
public Servlet() {
}
public int getLoadOnStartup() {
return this.loadOnStartup;
}
public void setLoadOnStartup(int loadOnStartup) {
this.loadOnStartup = loadOnStartup;
}
}
public static class Async {
private Long requestTimeout;
public Async() {
}
public Long getRequestTimeout() {
return this.requestTimeout;
}
public void setRequestTimeout(Long requestTimeout) {
this.requestTimeout = requestTimeout;
}
}
}
2,自動配置的靜態資源
在自動配置類的addResourceHandlers方法中定義了以下靜態資源的自動配置。
1)類路徑檔案
把類路徑下的/static,/public,/resources和/META-INF/resources資料夾下的靜態檔案直接對映為/**,可以通過http://localhost:8080/**來訪問。
2)webjar
何謂webjar,webjar就是將是我們常用的指令碼框架封裝在jar包中的jar包,更多關於webjar的內容請訪問http://www.webjars.org網站
把webjar的/META-INF/resources/webjars/下的靜態檔案對映為/webjar/**,可以通過http://localhost:8080/webjar/**來訪問
3,自動配置的Formatter和Converter
關於自動配置的Formatter和Converter,我們可以看一下WebMvcAutoConfiguration類中的定義:
public void addFormatters(FormatterRegistry registry) {
Iterator var2 = this.getBeansOfType(Converter.class).iterator();
while(var2.hasNext()) {
Converter<?, ?> converter = (Converter)var2.next();
registry.addConverter(converter);
}
var2 = this.getBeansOfType(GenericConverter.class).iterator();
while(var2.hasNext()) {
GenericConverter converter = (GenericConverter)var2.next();
registry.addConverter(converter);
}
var2 = this.getBeansOfType(Formatter.class).iterator();
while(var2.hasNext()) {
Formatter<?> formatter = (Formatter)var2.next();
registry.addFormatter(formatter);
}
}
從程式碼中可以看出,只要我們定義了Converter,GenericConverter和Formatter介面的事項類的Bean,這些Bean就會自動註冊到Spring MVC中。
4,自動配置的HttpMessageConverters
在WebMvcAutoConfiguration中,我們註冊了messageConverters,程式碼如下:
@Configuration
@Import({WebMvcAutoConfiguration.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 WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, @Lazy HttpMessageConverters messageConverters, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConverters = messageConverters;
this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
}
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.addAll(this.messageConverters.getConverters());
}
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
Long timeout = this.mvcProperties.getAsync().getRequestTimeout();
if (timeout != null) {
configurer.setDefaultTimeout(timeout.longValue());
}
}
在這裡直接注入了HttpMessageConverters的Bean,而這個Bean是在HttpMessageConvertersAutoConfiguration類中定義的,我們自動掃描註冊的HttpMessage Converter除了Spring MVC預設的ByteArrayHttpMessageConverter,StringHttpMessage Converter,Resource HttpMessageConverter等外,還自動配置檔案裡引入了JacksonHttpMessageConverters Configuration和GsonHttpMessage ConverterConfiguration,使我們獲得了額外的HttpMessageConverter:
若jackson的jar包在路徑上,則Spring Boot通過JacksonHttpMessage Converters Configuration增加了MappingJackson2HttpMessage Converter和Mapping Jackson2XmlHttpMessageConverter
若gson的jar包在路徑上,則Spring Boot通過GsonHttpMessageConverterConfiguration增加GsonHttpMessageConverter
在Spring Boot中如果要新增自定義的HttpMessageConverter,則只需定義一個你自己的HttpMessageConverters的Bean,然後在此Bean中註冊自定義的HttpMessageConverter即可,如下:
@Bean
public HttpMessageConverters customConverters(){
HttpMessageConverter<?> customConverter1 = new CustomConverter1();
HttpMessageConverter<?> customConverter2 = new CustomConverter2();
return new HttpMessageConverters(customConverter1,customConverter2)
}
5,靜態首頁的支援
把靜態index.html檔案放置在如下目錄
classpath:/META-INF/resources/index.html
classpath:/resources/index.html
classpath:/static/index.html
classpath:/public/index.html
當我們訪問應用根目錄http://localhost:8080/時,會直接對映
二:接管Spring Boot的Web配置
如果Spring Boot提供的Spring MVC預設配置不符合需求,則可以通過一個配置類(註解有@Configuration的類)加上@EnableWebMvc註解來實現完全自己控制的MVC配置。
通常情況下,Spring Boot的自動配置是符合我們大多數需求的。在你既需要保留Spring Boot提供的便利,又需要增加自己額外的配置的時候,可以定義一個配置類並繼承WebMvcConfigurerAdapter,無須使用@EnableWebMvc,例如:
package jack.springmvc.config;
import jack.springmvc.interceptor.DemoInterceptor;
import jack.springmvc.messageconverter.MyMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
import java.util.List;
/**
* Created by jack on 2017/7/16.
*/
@Configuration
@EnableWebMvc //開啟Spring MVC支援,若無此句,重寫WebMvcConfigurerAdapter方法無效
@ComponentScan("jack.springmvc")
@EnableScheduling //開啟計劃任務的支援
//繼承WebMvcConfigurerAdapter類,重寫其方法可對Spring MVC進行配置
public class MyMvcConfig extends WebMvcConfigurerAdapter{
/**
* 配置攔截器的Bean
* @return
*/
@Bean
public DemoInterceptor demoInterceptor(){
return new DemoInterceptor();
}
/**
* 配置檔案上傳Bean
* @return
*/
@Bean
public MultipartResolver multipartResolver(){
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
/**
* c重寫addInterceptors方法,註冊攔截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//super.addInterceptors(registry);
registry.addInterceptor(demoInterceptor());
}
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
//viewResolver.setPrefix("/WEB-INF/classes/views/");
viewResolver.setPrefix("/WEB-INF/classes/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//super.addResourceHandlers(registry);
//addResourceLocations指的是檔案放置的目錄,addResourceHandler指的是對外暴露的訪問路徑
registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
}
/**
* 統一處理沒啥業務邏輯處理的controller請求,實現程式碼的簡潔
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//super.addViewControllers(registry);
registry.addViewController("/index").setViewName("/index");
registry.addViewController("/toUpload").setViewName("upload");
registry.addViewController("/converter").setViewName("/converter");
registry.addViewController("/sse").setViewName("/sse");
registry.addViewController("/async").setViewName("/async");
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
//super.configurePathMatch(configurer);
configurer.setUseSuffixPatternMatch(false);
}
/**
* 配置自定義的HttpMessageConverter的bean,在spring mvc裡註冊HttpMessageConverter有兩個方法:
* configureMessageConverters:過載會覆蓋掉Spring MVC預設註冊的多個HttpMessageConverter
* extendMessageConverters:僅新增一個自定義的HttpMessageConverter,不覆蓋預設註冊的HttpMessageConverter
* @param converters
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//super.extendMessageConverters(converters);
converters.add(converter());
}
@Bean
public MyMessageConverter converter(){
return new MyMessageConverter();
}
}
上注意,上面重寫了了addViewController方法,並不會覆蓋WebMvcAutoConfiguration中的addViewController(在此方法中Spring Boot將“/”對映至index.html),這也就意味著我們自己的配置和Spring Boot的自動配置同時有效,這也是推薦新增自己的MVC配置的方式。
三:接註冊Servlet,Filter,Listener
當使用嵌入式的Servlet容器時,我們通過將Servlet,Filter和Listener宣告為Spring Bean而達到註冊的效果;或者註冊ServletRegistrationBean,FilterRegistrationBean和ServletListenerRegistrationBean的Bean。
1)直接註冊Bean示例,程式碼如下:
@Bean
public XxServlet xxServlet(){
return new XxServlet();
}
@Bean
public YyFilter yyFilter(){
return new YyFilter();
}
@Bean
public ZzListener zzListener(){
return new ZzListener()
}
2)通過RegistrationBean
@Bean
public ServeletRegistrationBean serveletRegistrationBean(){
return new ServeletRegistrationBean(new Xxservlet(),"/xx/*");
}
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new YyFilter());
registrationBean.setOrder(2);
return registrationBean;
}
@Bean
public ServletListenerRegistrationBean<ZzListener> zzListenerServletListenerRegistrationBean(){
return new ServletListenerRegistrationBean<ZzListener>(new ZzListener())