Introduction to the

Like many other Web frameworks, SpringMVC uses a front-end controller design pattern, where a Servlet with a DispatcherServlet at its core provides a shared algorithm for handling requests, while the actual work is performed by configurable delegate components. This pattern is both flexible and supports diverse workflows. Like other servlets, dispatcherServlets need to declare and map urls in either web.xml or Java configuration according to the Servlet specification. The DispatcherServlet then uses Spring’s configuration to find delegate components for mapping urls, parsing views, exception handling, and so on. The following example uses a Java configuration to register and initialize a DispatcherServlet class that is automatically discovered by the Servlet container.

public class MyWebApplicationInitializer implements WebApplicationInitializer {    @Override    public void onStartup(ServletContext servletCxt) {        // Load Spring web application configuration        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();        ac.register(AppConfig.class);        ac.refresh();        // Create and register the DispatcherServlet        DispatcherServlet servlet = new DispatcherServlet(ac);        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);        registration.setLoadOnStartup(1);        registration.addMapping("/app/*");    }}Copy the code

The following example is registered and initialized using web.xml

<web-app>    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>    <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>/WEB-INF/app-context.xml</param-value>    </context-param>    <servlet>        <servlet-name>app</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value></param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>app</servlet-name>        <url-pattern>/app/*</url-pattern>    </servlet-mapping></web-app>Copy the code
hierarchy

The DispatcherServlet requires an extended ApplicationContext WebApplicationContext to configure its information. WebApplicationContext contains a reference to the ServletContext of the Servlet, You can use static methods in RequestContextUtils to find the ServletContext from the WebApplicationContext. For most applications a WebApplicationContext is sufficient. Of course, WebApplicatioContext can also be hierarchical, such as a root WebApplicationContext shared by multiple servlets, each of which has its own child WebApplicationContext. The root WebApplicationContext typically includes the underlying beans that need to be shared across multiple servlets, such as data warehouses, business logic, and so on. In the Servlet specification, these beans can be effectively inherited and overwritten, and the child WebApplicationContext is contained only in the Servlet to which it belongs. The following example is a hierarchical configuration of WebApplicationContext

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<? >[] getRootConfigClasses() { return new Class<? >[] { RootConfig.class }; } @Override protected Class<? >[] getServletConfigClasses() { return new Class<? >[] { App1Config.class }; } @Override protected String[] getServletMappings() { return new String[] { "/app1/*" }; }}Copy the code

Similarly, configuration in web.xml

<web-app>    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>    <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>/WEB-INF/root-context.xml</param-value>    </context-param>    <servlet>        <servlet-name>app1</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value>/WEB-INF/app1-context.xml</param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>app1</servlet-name>        <url-pattern>/app1/*</url-pattern>    </servlet-mapping></web-app>Copy the code
A special bean type

The DispatcherServlet delegates specialized beans to handle the request and render the response. Dedicated beans are spring-managed instances that implement the Conventions of the WebFlux framework. These beans are generally built in conventions, but you can customize their properties, extend them, or replace them.

Bean type explain
HandlerMapping Processor mapping, implemented by its subclasses. Two important subclasses,RequestMappingHandlerMapping.SimpleUrlHandlerMapping
HandlerAdapter auxiliaryDispatcherServletExecute a specific processor.
HandlerExceptionResolver Redirects the exception to another processor or to an error screen that displays HTML.
ViewResolver The specific view is found and rendered by the view string returned by the processor.
LocaleResolver, LocaleContextResolver Support for internationalization pages that resolve localization issues using, for example, time zones.
ThemeResolver Parsing the topics available to the application, such as providing a personalization framework
MultipartResolver Handling uploaded Files
FlashMapManager Saves and retrieves a FlashMap of inputs and outputs that can pass properties from one request to the inputs and outputs of another request, typically used in redirection.
Web MVC configuration

Applications can declare beans listed in special bean types to handle requests. The DispatcherServlet checks the WebApplicationContext for each bean. If you do not specify a bean, then use a DispatcherServlet. The properties defined in the bean. The MVC configuration will be detailed later.

The Servlet configuration

In Servlet 3.0+, you can use a programmatic alternative to web.xml configuration. Here is an example of programmatically registering a DispatcherServlet

import org.springframework.web.WebApplicationInitializer; public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext container) { XmlWebApplicationContext appContext = new XmlWebApplicationContext(); appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml"); ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext)); registration.setLoadOnStartup(1); registration.addMapping("/"); }}Copy the code

WebApplicationInitializer is provided by for SpringMVC interface, to ensure the implementation can be support the Servlet 3.0 container automatically detect and initialized. Abstract class AbstractDispatcherServletInitializerl implements register DispathcerServlet WebApplicationInitializer can more easily. Here is Spring configured using Java

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<? >[] getRootConfigClasses() { return null; } @Override protected Class<? >[] getServletConfigClasses() { return new Class<? >[] { MyWebConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; }}Copy the code

If you are using the XML configuration, it is necessary to inherit AbstractDispatcherServletInitializer directly

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {    @Override    protected WebApplicationContext createRootApplicationContext() {        return null;    }    @Override    protected WebApplicationContext createServletApplicationContext() {        XmlWebApplicationContext cxt = new XmlWebApplicationContext();        cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");        return cxt;    }    @Override    protected String[] getServletMappings() {        return new String[] { "/" };    }}Copy the code

AbstractDispatcherServletInitializer also provides a convenient function to add the filter.

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {    // ...    @Override    protected Filter[] getServletFilters() {        return new Filter[] {            new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };    }}Copy the code

Each filter is given a default name based on its specific type and is automatically mapped to the DispatcherServlet. IsAsyncSupported method is AbstractDispatcherServletInitializer protect method, If you want to define your own DispatcherServlet, you can override the createDispatcherServlet method.

Handle the request

The flow of DispatcherServlet processing requests is as follows:

  • Find the WebApplicationContext and save it as a property of the Request so that other controllers or components in the processing chain can use it. The default save key is dispatcherservlet.web_application_context_attribute

  • The localization parser is stored in the request so that other components in the processing chain can use it to process requests for localization. If you don’t need localization, then you don’t need to use it.

  • The topic resolver is stored in the request for use by other components, such as view finders, and is ignored if theme customization is not required.

  • If the specified file upload parser, then will check request for file upload, if have request MultipartHttpServletRequest encapsulation, so that other components processing.

  • Find the appropriate handler to process the request. If the handler is found, the components on the processing chain are executed in turn, returning a Model or view. Annotated controllers can also render directly without returning to the view.

  • If a Model is returned, a view is rendered. If model is not returned, there is no need to render the view because the view might already be rendered.

If an exception occurs during request processing, you can use HandlerExceptionResolver in WebApplicatioContext to customize exception handling. SpringMVC also supports returning last-modify-date, which is very straightforward on how to determine if last-modify-date is present for a given request processing: The DispatcherServlet finds the appropriate processor and checks if it implements the LastModified interface, and if it does, calls Long getLastModified(Request) back to the client. The DispatcherServlet can be customized using the initialization parameters of the Servlet in web.xml.

parameter explain
contextClass implementationWebApplicationContextClass, used by defaultXmlWebApplicationContext
contextConfigLocation The string passed to the Context instance contains the bean definition
namespace WebApplicationContextNamespace, default[servlet-name]-servlet
The interceptor

HandlerMapping supports interceptors, which are useful when adding processing to certain requests, such as permission checking. Interceptors must implement org. Springframework. Web. Servlet HandlerInterceptor in the package, this interface has three processing function corresponding to the request processing, after the request processing, complete the request processing.

  • preHandle(..) Before the request is processed

  • postHandle(..) The request is processed and executed

  • afterCompletion(..) Execute preHandle(..) after the entire request is processed. Returns a Boolean value. You can use this value to interrupt the processing request chain. When true is returned, processing continues, if false is returned, DispatcherSert assumes that the interceptor has handled the request correctly, such as rendering a page, etc. The request processing chain will be broken. Note that the postHandle method rarely uses @responseBody and ResponseEntity. Because the response has already been returned by a HandlerAdapter before postHandle executes. Means it’s too late to modify the response at postHandle. Corresponding to this scenario can inherit ResponseBodyAdvice or implement ControllerAdvice or configuration RequestMappingHandlerAdapter directly.

Exception handling

If an exception is thrown during request mapping or processing, the DispatcherSert delegates HandlerExceptionResolver to resolve the exception and provide alternative handling, which returns an error response. The following table is an implementation of HandlerExceptionResolver

HandlerExceptionResolver describe
SimpleMappingExceptionResolver Mapping of exception class names to error page names. This is useful when browsers render error pages
DefaultHandlerExceptionResolver Parsing exceptions thrown by SpringMVC and mapping them to HTTP error codes
ResponseStatusExceptionResolver Parse the @responseStatus annotation and map it to the HTTP error code based on its annotation value
ExceptionHandlerExceptionResolver Call the @Controller or @ControllerAdvice method that uses the @ExceptionHandler annotation
Parsing the chain

Can declare multiple in the Spring configuration HandlerExceptionResolverbean, to form a chain of exception handling parsing, if necessary, at the same time can be set up their order. The larger the serial number, the farther back the processing. HandlerExceptionResolver can return:

  • The ModelAndView pointing to the error page

  • Return empty ModelAndView if the exception is handled in the parse chain

  • If the exception returns null for being handled, subsequent parsing will continue to handle the exception. If the exception is never handled, it will bubble up to the Servlet container to handle Spring MVC exceptions that are automatically declared by the MVC configuration. The @responseStatus annotation exception, Exception that supports the @ExceptionHandler method. These processors are customizable and replaceable

    Servlet container exception

    If HandlerExceptionResolver cannot handle the exception, the exception will continue to propagate, or it will return an incorrect HTTP status code, such as 4xx, 5xx. The Servlet container might render an incorrect page. This page can also be customized:

    <error-page><location>/error</location></error-page>Copy the code

According to the code above, when an exception occurs that cannot be handled or an error code is returned, the container will return an incorrect URL based on the configuration. This request will continue to be handled by a DispatcherServlet, such as mapping to an @controller error handler:

@RestControllerpublic class ErrorController {    @RequestMapping(path = "/error")    public Map<String, Object> handle(HttpServletRequest request) {        Map<String, Object> map = new HashMap<String, Object>();        map.put("status", request.getAttribute("javax.servlet.error.status_code"));        map.put("reason", request.getAttribute("javax.servlet.error.message"));        return map;    }}Copy the code
View resolution

Spring MVC defines the ViewResolver and View interfaces so that we can render views directly by returning models without specifying a particular View technology. ViewResolver provides the mapping between view names and views. The View prepares the data before submitting it to a particular View technology. The following table shows the hierarchy of a ViewResolver:

ViewResolver describe
AbstractCachingViewResolver Caching parsed views. You can disable caching by setting the cache property to false. Can be called in scenarios where the cache needs to be refreshedremoveFromCache(String viewName, Locale loc)The refresh.
XmlViewResolver implementationViewResolverCan receive an XML configuration file with the same DTD as the Spring XML bean. The default in/WEB – INF/views. XML
ResourceBundleViewResolver Parsing is defined inResourceBundleClass as the view class and viewname.url as the viewname
UrlBasedViewResolver You don’t need to explicitly specify a mapping. You can find the view name by parsing the URL.
InternalResourceViewResolver implementationUrlBasedViewResolver.JstlView.TilesViewTo supportInternalResourceViewFor example: JSP, servlet class and so on.
FreeMarkerViewResolver UrlBasedViewResolverSubclass to support FreeMarker
ContentNegotiatingViewResolver The view is determined by the requested filename or Accept
The view

If desired, you can declare multiple view handlers and determine their order by setting the order property. The larger the order, the later the processing. By default, ViewResolver can return NULL to indicate that the view was not found. Of course in the JSP, use InternalResourceViewResolver to check whether the JSP is the only way is through the RequestDispatcher performs a scheduling. Therefore InternalResourceViewResolver must be the last view parser.

View redirect

View prefix Redirect: Indicates that the view needs to perform a redirect. UrlBasedViewResolver and its subclasses will recognize that this is being redirected, and all that’s left is the view name. This effect is the same as the Controller returning a RedirectView, but with this directive, the Controller can simply return the view name. View name redirect: / myapp/some/resource will return relative to the current Servlet Context view, redirect:http://myhost.com/some/arbitrary/path this will return an absolute URL. Note that if a Controller is decorated with @responseStatus, the annotation value takes precedence over RedirectView

View Forwarding

View prefix forward: indicates that the view implements forwarding. Also resolved by UrlBasedViewResolver and its subclasses. By creating InternalResourceView execution RequestDispatcher. Forward () implementation. So this instruction for InternalResourceViewResolver and InternalResourceViewResolver futile, but to use the technology of other views but still want to use force when using JSP or Servlet is very useful.

View Content negotiation

ContentNegotiatingViewResolver will not parse the view, but its entrusted to other views parser, and select select view client request description. The description can be an Accept header or an argument, such as /path? Format = pdfContentNegotiatingViewResolver request by comparing the media type and ViewResolvers support media type to choose the right View. The first View in the selected list will be returned to the client.

localization

Like Spring MVC, most Spring architectures support internationalization. DispatcherServlet automatically parses messages using the LocaleResolver based on the client region. When the request comes in, the DispatcherServlet looks for the localization parser and if it finds it, it sets the localization. The locale language that is parsed by the locale parser is available through the requestContext.getLocale () method. To automate the parsing, specific scenarios can be localized through interceptors, such as by request parameters. Localization parser and interceptors defined in org. Springframework. Web. Servlet. I18n package, can be configured in the application. Here are some configurations that Spring uses

TimeZone

Localization is done by obtaining the client’s time zone. The LocaleContextResolver interface extends LocalResolver to provide a LocaleContext that may contain time zone information. If you can, the user’s time zone can pass the RequestContext. GetTimeZone () method. Time zone information can be automatically used by Converter and Formatter registered with the ConversionService date and time in Spring.

Header resolver

The parser checks the Accept-language header, which typically contains the region of the client’s operating system. Note that time zones are not supported.

Cookie resolver

The parser checks for timezones and locales that may be contained in the cookie. Use it with the following definition:

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="cookieName"  value="clientlanguage"/> <! -- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) --> <property name="cookieMaxAge" value="100000"/></bean>Copy the code

CookieLocaleResolver properties:

The name The default value describe
cookieName classname + LOCALE Name of the cookie
cookieMaxAge Servlet container default value Cookie validity time
cookiePath / Cookie location
Session resolver

SessionLocaleResolver checks for possible timezones and locales from the session. In contrast to CookieLocaleResolver, it stores information in an HttpSession.

Locale interceptor

It is possible to initiate localization policies for certain mappings using interceptors, such as the following:

<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="siteLanguage"/></bean><bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/><bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="localeChangeInterceptor"/> </list> </property> <property name="mappings"> <value>/**/*.view=someController</value>  </property></bean>Copy the code
The theme

You can improve the user experience by setting the theme of Spring MVC to set up the overall look and feel of your application. A theme is a collection of static resources, mainly style sheets and images that can affect the look and feel. In order to apply the theme, first to set up an org. Springframework. UI. Context. ThemeSource interface. WebApplicationContext inherits ThemeSource, but delegates its implementation to subclasses. Default is org. Springframework. UI. Context. Support. ResourceBundleThemeSource come from the root directory of the classpath loading configuration files. The configuration file format is as follows:

styleSheet=/themes/cool/style.css
background=/themes/cool/img/coolBg.jpgCopy the code

The name of the configuration file is the variable name in the view code. For a JSP it would look like this:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%><html>    <head>        <link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/>    </head>    <body style="background=<spring:theme code='background'/>">        ...    </body></html>Copy the code

By default ResourceBundleThemeSource prefix name using the empty, this configuration file directly from the root directory of the classpath under loading. So it can be cool. The properties defined in the root directory of the classpath, ResourceBundleThemeSource default using the standard Java resource loading tool, but also fully supports internationalization, So support cool_nl.properties by naming it.

Analytical theme

The DispatcherServlet looks for the implementation of themeResolver by the bean name themeResolver. ThemeResolver is implemented as follows:

Class describe
FixedThemeResolver Select a fixed theme and setdefaultThemeNameattribute
SessionThemeResolver Topics are maintained by user sessions. Each session needs to be set only once
CookieThemeResolver Select a theme through cookies
Multipart resolver

Org. Springframework. Web. The MultipartResolver multipart is used for handling multipart request. There are two implementations, Common Fileupload and Servlet 3.0. To use multipart, you need to declare a bean named multipartResolver in the Spring configuration of the DispatcherServlet. When the POST request is the content-type multipart/form – the data, the parser parses the request and packages it into MultipartHttpServletRequest to handle the request.

Apache FileUpload

To use Apache Commons FileUpload, simply configure a bean of type CommonsMultipartResolver named multipartResolver. You also need to add Commons-fileupload to dependencies.

The Servlet 3.0

Using Servlet 3.0 requires the configuration of the Servlet container

  • Using Java configuration, set MultipartConfigElement in the Servlet registry.

  • Use web.xml to add the following configuration to

    using Java:

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {    // ...    @Override    protected void customizeRegistration(ServletRegistration.Dynamic registration) {        // Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold        registration.setMultipartConfig(new MultipartConfigElement("/tmp"));    }}Copy the code

Servlet 3.0 configuration, only need to add the type of StandardServletMultipartResolver, name for the multipartResolver configuration.