1. 模板引擎
spring boot提供了大量的模板引擎,包括FreeMark、Groovy、Thymeleaf、Velocity等,但spring boot中推荐用Thymeleaf,因为Thymeleaf提供了完美的spring mvc的支持。
2. 与spring boot集成
在spring mvc中,若要集成一个模板引擎的话,需要定义ViewResolver,而ViewResolver需要定义一个View。Thymeleaf已经定义好了ViewResolver和View,分别是org.thymeleaf.spring4.view.ThymeleafViewResolver(默认使用ThymeleafView作为View)和org.thymeleaf.spring4.view.ThymeleafView。Thymeleaf提供了SpringTemplateEngine类,用来驱动在spring mvc下使用Thymeleaf模板引擎,另外还提供了一个TemplateResolver用来设置通用的模板引擎(包含前缀、后缀等),这使我们在spring mvc中集成Thymeleaf引擎变得十分简单。
上面说的是与spring mvc集成,但是ThymeleafView与Spring boot集成很方便,spring boot通过org.springframework.boot.autoconfigure.thymeleaf包对Thymeleaf进行了自动配置。
通过ThymeleafAutoConfiguration类对集成所需要的bean进行自动配置,包括templateResolver、templateEngine和thymeleafViewResolver的配置。
通过ThymeleafProperties来配置Thymeleaf,在application.properties中,以spring.thymeleaf开头来配置,通过查看ThymeleafProperties的主要源码,可以看出如何设置属性以及默认的配置:
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8"); private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html"); public static final String DEFAULT_PREFIX = "classpath:/templates/"; public static final String DEFAULT_SUFFIX = ".html"; /**
* Check that the template exists before rendering it (Thymeleaf 3+).
*/
private boolean checkTemplate = true; /**
* Check that the templates location exists.
*/
private boolean checkTemplateLocation = true; /**
* Prefix that gets prepended to view names when building a URL.
*/
private String prefix = DEFAULT_PREFIX; /**
* Suffix that gets appended to view names when building a URL.
*/
private String suffix = DEFAULT_SUFFIX; /**
* Template mode to be applied to templates. See also StandardTemplateModeHandlers.
*/
private String mode = "HTML5"; /**
* Template encoding.
*/
private Charset encoding = DEFAULT_ENCODING; /**
* Content-Type value.
*/
private MimeType contentType = DEFAULT_CONTENT_TYPE; /**
* Enable template caching.
*/
private boolean cache = true; /**
* Order of the template resolver in the chain. By default, the template resolver is
* first in the chain. Order start at 1 and should only be set if you have defined
* additional "TemplateResolver" beans.
*/
private Integer templateResolverOrder; /**
* Comma-separated list of view names that can be resolved.
*/
private String[] viewNames; /**
* Comma-separated list of view names that should be excluded from resolution.
*/
private String[] excludedViewNames; /**
* Enable MVC Thymeleaf view resolution.
*/
private boolean enabled = true; public boolean isEnabled() {
return this.enabled;
} public void setEnabled(boolean enabled) {
this.enabled = enabled;
} public boolean isCheckTemplate() {
return this.checkTemplate;
} public void setCheckTemplate(boolean checkTemplate) {
this.checkTemplate = checkTemplate;
} public boolean isCheckTemplateLocation() {
return this.checkTemplateLocation;
} public void setCheckTemplateLocation(boolean checkTemplateLocation) {
this.checkTemplateLocation = checkTemplateLocation;
} 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 String getMode() {
return this.mode;
} public void setMode(String mode) {
this.mode = mode;
} public Charset getEncoding() {
return this.encoding;
} public void setEncoding(Charset encoding) {
this.encoding = encoding;
} public MimeType getContentType() {
return this.contentType;
} public void setContentType(MimeType contentType) {
this.contentType = contentType;
} public boolean isCache() {
return this.cache;
} public void setCache(boolean cache) {
this.cache = cache;
} public Integer getTemplateResolverOrder() {
return this.templateResolverOrder;
} public void setTemplateResolverOrder(Integer templateResolverOrder) {
this.templateResolverOrder = templateResolverOrder;
} public String[] getExcludedViewNames() {
return this.excludedViewNames;
} public void setExcludedViewNames(String[] excludedViewNames) {
this.excludedViewNames = excludedViewNames;
} public String[] getViewNames() {
return this.viewNames;
} public void setViewNames(String[] viewNames) {
this.viewNames = viewNames;
} }
3. web相关配置
3.1 spring boot提供的自动配置
通过查看WebMvcAutoConfiguration及WebMvcProperties的源码,可以发现spring boot提供了如下的自动配置
@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 String DEFAULT_PREFIX = ""; public static 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,
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());
} 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 createExceptionHandlerExceptionResolver() {
if (this.mvcRegistrations != null && this.mvcRegistrations
.getExceptionHandlerExceptionResolver() != null) {
return this.mvcRegistrations.getExceptionHandlerExceptionResolver();
}
return super.createExceptionHandlerExceptionResolver();
} @Override
protected void configureHandlerExceptionResolvers(
List<HandlerExceptionResolver> exceptionResolvers) {
super.configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
addDefaultHandlerExceptionResolvers(exceptionResolvers);
}
if (this.mvcProperties.isLogResolvedException()) {
for (HandlerExceptionResolver resolver : exceptionResolvers) {
if (resolver instanceof AbstractHandlerExceptionResolver) {
((AbstractHandlerExceptionResolver) resolver)
.setWarnLogCategory(resolver.getClass().getName());
}
}
}
} } @Configuration
@ConditionalOnEnabledResourceChain
static class ResourceChainCustomizerConfiguration { @Bean
public ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
return new ResourceChainResourceHandlerRegistrationCustomizer();
} } interface ResourceHandlerRegistrationCustomizer { void customize(ResourceHandlerRegistration registration); } private static class ResourceChainResourceHandlerRegistrationCustomizer
implements ResourceHandlerRegistrationCustomizer { @Autowired
private ResourceProperties resourceProperties = new ResourceProperties(); @Override
public void customize(ResourceHandlerRegistration registration) {
ResourceProperties.Chain properties = this.resourceProperties.getChain();
configureResourceChain(properties,
registration.resourceChain(properties.isCache()));
} private void configureResourceChain(ResourceProperties.Chain properties,
ResourceChainRegistration chain) {
Strategy strategy = properties.getStrategy();
if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
chain.addResolver(getVersionResourceResolver(strategy));
}
if (properties.isGzipped()) {
chain.addResolver(new GzipResourceResolver());
}
if (properties.isHtmlApplicationCache()) {
chain.addTransformer(new AppCacheManifestTransformer());
}
} private ResourceResolver getVersionResourceResolver(
ResourceProperties.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;
} } static final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping { private static final Log logger = LogFactory
.getLog(WelcomePageHandlerMapping.class); private WelcomePageHandlerMapping(Resource welcomePage) {
if (welcomePage != null) {
logger.info("Adding welcome page: " + welcomePage);
ParameterizableViewController controller = new ParameterizableViewController();
controller.setViewName("forward:index.html");
setRootHandler(controller);
setOrder(0);
}
} @Override
public Object getHandlerInternal(HttpServletRequest request) throws Exception {
for (MediaType mediaType : getAcceptedMediaTypes(request)) {
if (mediaType.includes(MediaType.TEXT_HTML)) {
return super.getHandlerInternal(request);
}
}
return null;
} private List<MediaType> getAcceptedMediaTypes(HttpServletRequest request) {
String acceptHeader = request.getHeader(HttpHeaders.ACCEPT);
return MediaType.parseMediaTypes(
StringUtils.hasText(acceptHeader) ? acceptHeader : "*/*");
} } }
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties { /**
* Formatting strategy for message codes (PREFIX_ERROR_CODE, POSTFIX_ERROR_CODE).
*/
private DefaultMessageCodesResolver.Format messageCodesResolverFormat; /**
* Locale to use. By default, this locale is overridden by the "Accept-Language"
* header.
*/
private Locale locale; /**
* Define how the locale should be resolved.
*/
private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER; /**
* Date format to use (e.g. dd/MM/yyyy).
*/
private String dateFormat; /**
* Dispatch TRACE requests to the FrameworkServlet doService method.
*/
private boolean dispatchTraceRequest = false; /**
* Dispatch OPTIONS requests to the FrameworkServlet doService method.
*/
private boolean dispatchOptionsRequest = true; /**
* If the content of the "default" model should be ignored during redirect scenarios.
*/
private boolean ignoreDefaultModelOnRedirect = true; /**
* If a "NoHandlerFoundException" should be thrown if no Handler was found to process
* a request.
*/
private boolean throwExceptionIfNoHandlerFound = false; /**
* Enable warn logging of exceptions resolved by a "HandlerExceptionResolver".
*/
private boolean logResolvedException = false; /**
* Maps file extensions to media types for content negotiation, e.g. yml->text/yaml.
*/
private Map<String, MediaType> mediaTypes = new LinkedHashMap<String, MediaType>(); /**
* Path pattern used for static resources.
*/
private String staticPathPattern = "/**"; private final Async async = new Async(); private final Servlet servlet = new Servlet(); private final View view = new View(); public DefaultMessageCodesResolver.Format getMessageCodesResolverFormat() {
return this.messageCodesResolverFormat;
} public void setMessageCodesResolverFormat(
DefaultMessageCodesResolver.Format messageCodesResolverFormat) {
this.messageCodesResolverFormat = messageCodesResolverFormat;
} public Locale getLocale() {
return this.locale;
} public void setLocale(Locale locale) {
this.locale = locale;
} public LocaleResolver getLocaleResolver() {
return this.localeResolver;
} public void setLocaleResolver(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 Async getAsync() {
return this.async;
} public Servlet getServlet() {
return this.servlet;
} public View getView() {
return this.view;
} public static class Async { /**
* Amount of time (in milliseconds) before asynchronous request handling times
* out. If this value is not set, the default timeout of the underlying
* implementation is used, e.g. 10 seconds on Tomcat with Servlet 3.
*/
private Long requestTimeout; public Long getRequestTimeout() {
return this.requestTimeout;
} public void setRequestTimeout(Long requestTimeout) {
this.requestTimeout = requestTimeout;
} } public static class Servlet { /**
* Load on startup priority of the dispatcher servlet.
*/
private int loadOnStartup = -1; public int getLoadOnStartup() {
return this.loadOnStartup;
} public void setLoadOnStartup(int loadOnStartup) {
this.loadOnStartup = loadOnStartup;
} } public static class View { /**
* Spring MVC view prefix.
*/
private String prefix; /**
* Spring MVC view suffix.
*/
private String suffix; 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 enum LocaleResolver { /**
* Always use the configured locale.
*/
FIXED, /**
* Use the "Accept-Language" header or the configured locale if the header is not
* set.
*/
ACCEPT_HEADER } }
3.2 接管spring boot的web配置
在既需要保留spring boot提供的便利,又需要增加自己的额外的配置的时候,可以定义一个配置类并继承WebMvcConfigurerAdapter,无须使用@EnableWebMvc注解,然后按照spring mvc的配置方法来添加spring boot其他配置。值得指出的是重写addViewControllers方法,并不会覆盖WebMvcConfigurerAdapter中的addViewControllers(在此方法中,spring boot 将“/”映射至index.html),这也就意味着我们自己的配置和spring boot的自动配置同时有效,这也是推荐添加自己的mvc配置的方式。
3.3 注册servlet、filter、Listener
当使用嵌入式的servlet容器(tomcat、jetty等)时,通过将servlet、filter和listener声明为spring bean而达到注册的效果;或者注册ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean的bean。
3.4 Tomcat配置
关于tomcat的所有属性都在org.springframework.boot.autoconfigure.web.ServerProperties配置类中做了定义,我们只需要在application.Properties配置属性做配置即可。通用的Servlet容器配置都以“server”作为前缀,而tomcat特有的配置都以“server.tomcat”作为前缀。配置tomcat还可以用代码的方式配置,以后再说。