If you already set up a Spring MVC application earlier, you may be used to writing at least a small portion of XML or a handful of Java annotation configuration classes.
Initialization steps are typically as follows:
However, Spring Boot handles all that work for us. Because this configuration is typically up to your application, you can come up with an unlimited amount of combinations.
Spring boot, in a way, is an opinionated Spring project configurator. It is based on conventions and will enforce them on your project by default.
Let's see what happens behind the curtains.
We will use the default Spring Boot configuration file that was created for us and put it in the debug mode. Add the following line to src/main/resources/application.properties
:
debug=true
Now, if we launch our application again we'll see Spring Boot's autoconfiguration report. It is divided into two parts: positive matches, which list all autoconfigurations that are used by our application; and negative matches, which are Spring Boot autoconfigurations whose requirements weren't met when the application started:
========================= AUTO-CONFIGURATION REPORT ========================= Positive matches: ----------------- DispatcherServletAutoConfiguration - @ConditionalOnClass classes found: org.Springframework.web.servlet.DispatcherServlet (OnClassCondition) - found web application StandardServletEnvironment (OnWebApplicationCondition) EmbeddedServletContainerAutoConfiguration - found web application StandardServletEnvironment (OnWebApplicationCondition) ErrorMvcAutoConfiguration - @ConditionalOnClass classes found: javax.servlet.Servlet,org.springframework.web.servlet.DispatcherServlet (OnClassCondition) - found web application StandardServletEnvironment (OnWebApplicationCondition) HttpEncodingAutoConfiguration - @ConditionalOnClass classes found: org.springframework.web.filter.CharacterEncodingFilter (OnClassCondition) - matched (OnPropertyCondition) <Input trimmed>
Let's take a closer look at DispatcherServletAutoConfiguration
:
/** * {@link EnableAutoConfiguration Auto-configuration} for the Spring * {@link DispatcherServlet}. Should work for a standalone application where an embedded * servlet container is already present and also for a deployable application using * {@link SpringBootServletInitializer}. * * @author Phillip Webb * @author Dave Syer */ @Order(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) protected static class DispatcherServletConfiguration { @Autowired private ServerProperties server; @Autowired(required = false) private MultipartConfigElement multipartConfig; @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { return new DispatcherServlet(); } @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) public ServletRegistrationBean dispatcherServletRegistration() { ServletRegistrationBean registration = new ServletRegistrationBean( dispatcherServlet(), this.server.getServletMapping()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); if (this.multipartConfig != null) { registration.setMultipartConfig(this.multipartConfig); } return registration; } @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; } } @Order(Ordered.LOWEST_PRECEDENCE - 10) private static class DefaultDispatcherServletCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ConditionOutcome outcome = checkServlets(beanFactory); if (!outcome.isMatch()) { return outcome; } return checkServletRegistrations(beanFactory); } } }
This is a typical Spring Boot configuration class:
@Configuration
like any other Spring configuration class.@Order
annotation. You can see that DispatcherServletAutoConfiguration
needs to be configured first.@AutoConfigureAfter
or @AutoConfigureBefore
to further refine the order in which configurations are processed.@ConditionalOnClass(DispatcherServlet.class)
, this particular configuration ensures that our classpath contains DispatcherServlet
, which is a good indication that Spring MVC is in the classpath and the user certainly wants to bootstrap it.This file also contains classic bean declarations for the Spring MVC dispatcher servlet and a multipart resolver. The whole Spring MVC configuration is broken into multiple files.
It is also worth noting that these beans obey certain rules to check whether are active. The ServletRegistrationBean
function will be enabled under the @Conditional(DefaultDispatcherServletCondition.class)
condition, which is a bit complex but checks whether you already have a dispatcher servlet registered in your own configuration.
The MultipartResolver
function will become active only if the condition @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
is met, for example, if we didn't declare it ourselves.
This means Spring boot only gives you a hand in configuring your application according to common use cases. However, at any point, you can override these defaults and declare your own configuration.
So, the DispatcherServletAutoConfiguration
class explains why we have a dispatcher servlet and a multipart resolver.
Another very relevant piece of configuration is WebMvcAutoConfiguration
. It declares the view resolver, the locale resolver, and the location of our static resources. The view resolver is as follows:
@Configuration @Import(EnableWebMvcConfiguration.class) @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter { @Value("${spring.view.prefix:}") private String prefix = ""; @Value("${spring.view.suffix:}") private String suffix = ""; @Bean @ConditionalOnMissingBean(InternalResourceViewResolver.class) public InternalResourceViewResolver defaultViewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix(this.prefix); resolver.setSuffix(this.suffix); return resolver; } }
The view resolver configuration is really typical. What's really interesting here is the use of configuration properties to allow users to customize it.
What it says is "I will look for two variables in the user's application.properties
called spring.view.prefix
and spring.view.suffix
". This is a very handy way to set up the view resolver with only two lines in our configuration.
Keep this in mind for the next chapter. For now, we will just stroll through Spring Boot's code.
Regarding static resources, this configuration includes the following lines:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" }; private static final String[] RESOURCE_LOCATIONS; static { RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length + SERVLET_RESOURCE_LOCATIONS.length]; System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0, SERVLET_RESOURCE_LOCATIONS.length); System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length); } @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/**")) { registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/") .setCachePeriod(cachePeriod); } if (!registry.hasMappingForPattern("/**")) { registry.addResourceHandler("/**") .addResourceLocations(RESOURCE_LOCATIONS) .setCachePeriod(cachePeriod); } }
The declaration of resource locations is a bit convoluted but we can still understand two things:
/META-INF/resources/
, /resources/
, /static/
, or /public/
.WebJars are JAR packages of client JavaScript libraries available on Maven central. They include a Maven project file, which allows for transitive dependencies and works in all JVM-based applications. WebJars are an alternative to JavaScript package managers such as bower or npm. They are great for applications that require just a few JavaScript libraries. Find the list of available WebJars on www.webjars.org.
There is also a part of this file that is dedicated to locale management:
@Bean @ConditionalOnMissingBean(LocaleResolver.class) @ConditionalOnProperty(prefix = "spring.mvc", name = "locale") public LocaleResolver localeResolver() { return new FixedLocaleResolver( StringUtils.parseLocaleString(this.mvcProperties.getLocale())); }
This default locale resolver handles only one locale and allows us to define it via the spring.mvc.locale
configuration property.
3.149.228.138