SPRINGMVC configuration of alternatives custom Dispatherservlet configuration
The three methods we wrote earlier in Spittrwebappinitializer are simply the abstract methods that must be overloaded. However, there are many more ways to overload, allowing for additional configuration.
For example, Customizeregistration (). This method is called after Abstractannotationconfigdispatcherservletinitializer will dispatcherservlet the main lane servlet container. And pass in the registration.dynamic that the servlet has registered. For example, we are going to plan to use Servlet3.0 for multipart support later, then we need to use Dispatcherservlet's registration to enable the multipart request.
@Overrideprotected void customizeRegistration(Dynamic registration) { registration.setMultipartConfig( new MultipartConfigElement("/tmp/spittr/uploads"));}
Use the Customizeregistration () method's servletregistration.dynamic to set the multipartconfigelement.
Add additional servlets and filter
One benefit of the Java-based initializer (initializer) is that we can define any number of initializer classes.
Therefore, if you need to define additional components, simply create the appropriate initialization class. The simplest approach is to implement the spring Webapplicationinitializer interface.
import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletRegistration.Dynamic;import org.springframework.web.WebApplicationInitializer;import com.myapp.MyServlet;public class MyServletInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { // 定义servlet Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class); // 映射servlet myServlet.addMapping("/custom/**"); }}
This code registers a servlet and maps it to a path. We can also use this method to manually register Dispatcherservlet. Similarly, we can also register filter and listener.
@Overridepublic void onStartup(ServletContext servletContext) throws ServletException { // 注册一个filter javax.servlet.FilterRegistration.Dynamic filter = servletContext.addFilter("myFilter", MyFilter.class); // 添加映射 filter.addMappingForUrlPatterns(null, false, "/custom/*");}
Webapplicationinitializer is a recommended way to register a servlet, filter, listener, and of course you are using Java-based configuration and deploying the application on the Servlet3.0 container. If you just need to register a filter and map it to Dispatcherservlet, you can also use Abstractannotationconfigdispatcherservletinitializer. To register multiple filter and map them to Dispatcherservlet, all you have to do is rewrite the Getservletfilters () method. Like what:
@Overrideprotected Filter[] getServletFilters() { return new Filter[] { new MyFilter() };}
As you can see, the method returns an array of Javax.servlet.Filter, where only one filter is returned, but it returns a number of them. At the same time, it is no longer necessary to declare mappings for these filter, because the filter returned by Getservletfilters () is automatically mapped to Dispatcherservlet.
Declaring Dispatcherservlet in Web. xml
Before we used Abstractannoatationconfigdispatcherservletinitializer to automatically register Dispatcherservlet and Contextloaderlistener. However, you can also register in Web. Xml as a traditional method.
<?xml version= "1.0" encoding= "UTF-8"? ><web-app version= "2.5" xmlns= "Http://java.sun.com/xml/ns/javaee" Xmlns:xsi= "Http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation= "http://java.sun.com/xml/ns/javaeehttp ://java.sun.com/xml/ns/javaee/web-app_2_5.xsd "> <context-param> <param-name>contextconfiglocation </param-name> <param-value>/WEB-INF/spring/root-context.xml</param-value> </CONTEXT-PARAM&G T <listener> <!--registration Contextloaderlistener--<listener-class>org.springframework.web.contex t.contextloaderlistener</listener-class> </listener> <servlet> <SERVLET-NAME>APPSERVL Et</servlet-name> <!--registration Dispatcherservlet-<servlet-class>org.springframework.web.serv Let. dispatcherservlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> < ;! --Dispatcherservlet mapping--> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pat Tern> </servlet-mapping></web-app>
Setting Web. XML to use Java-based configuration
<?xml version= "1.0" encoding= "UTF-8"? ><web-app version= "2.5" xmlns= "Http://java.sun.com/xml/ns/javaee" Xmlns:xsi= "Http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation= "http://java.sun.com/xml/ns/javaeehttp ://java.sun.com/xml/ns/javaee/web-app_2_5.xsd "> <!--using Java configuration--<context-param> <param-na Me>contextclass</param-name> <param-value> Org.springframework.web.context.support.Annota Tionconfigwebapplicationcontext </param-value> </context-param> <!--Specify the Java configuration class that you use-- <context-param> <param-name>contextConfigLocation</param-name> <param-value>spitt r.config.rootconfig</param-value> </context-param> <listener> <listener-class> Org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <serv Let> <servlet-name>appsErvlet</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherservlet</ Servlet-class> <!--using Java configuration-<init-param> <param-name>contextclass</p Aram-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApp Licationcontext </param-value> </init-param> <!--Specify the configuration class for Dispatcherservlet- <init-param> <param-name>contextConfigLocation</param-name> <param-value& Gt Spittr.config.WebConfigConfig </param-value> </init-param> <LOAD-ON-STARTUP>1&L t;/load-on-startup> </servlet> <servlet-mapping> <servlet-name>appservlet</servlet-na Me> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
Processing data in multipart form
Users often upload content in a web app. The SPITTR application requires a file upload in two places. When a new user registers the app, they will need to upload an image. And when they submit new spittle, they may upload images. The result of a general form submission is simple: multiple Name-value with & as a separator. Although this encoding is simple, it is inappropriate for binary data such as images. The data in multipart format splits a form into multiple sections, one for each input field. The following shows the Mulrtipart's request body:
------WebKitFormBoundaryqgkaBn8IHJCuNmiWContent-Disposition: form-data; name="firstName"Charles------WebKitFormBoundaryqgkaBn8IHJCuNmiWContent-Disposition: form-data; name="lastName"Xavier------WebKitFormBoundaryqgkaBn8IHJCuNmiWContent-Disposition: form-data; name="email"[email protected]------WebKitFormBoundaryqgkaBn8IHJCuNmiWContent-Disposition: form-data; name="username"professorx------WebKitFormBoundaryqgkaBn8IHJCuNmiWContent-Disposition: form-data; name="password"letmein01------WebKitFormBoundaryqgkaBn8IHJCuNmiWContent-Disposition: form-data; name="profilePicture"; filename="me.jpg"Content-Type: image/jpeg[[ Binary image data goes here ]]------WebKitFormBoundaryqgkaBn8IHJCuNmiW--
Although multipart looks complicated, it is easy to handle in SPRINGM. and first we need to configure a multipart parser.
Configuring the Multipart Parser
Dispatcherservlet does not have any functionality to parse multipart request data. He delegated this task to the implementation of the Multipartresolver policy interface in spring, starting with the Spring3.1, which has two multipartresolver implementations for us to choose from:
- Commonmultipartresolver: Use Jakarta Commons FileUpload to parse the multipart request;
- Standardservletmultipartresolver: Dependent on Servlet3.0 support for multipart request (starting from Spring3.1)
Generally choose Standardservletmultipartresolver. Standardservletmultipartresolver that are compatible with Servlet3.0 have no constructor parameters and no properties to set. Therefore it is very simple to declare it as a bean in the spring context, as follows:
@Bean public MultipartResolver multipartResolver() throws IOException { return new StandardServletMultipartResolver(); }
So how do you configure the Standardservletmultipartresolver restrictions? We will specify the multipart qualification in the servlet. At least write the path to the temporary file that was written during the file upload. If you do not set this most basic configuration, Standardservletmultipartresolver will not work properly. So we'll take the specifics of multipart as part of the Dispatcherservlet configuration in the Web. Xml or servlet initialization class. Configured with the Servlet initialization class:
DispatcherServlet ds=new DispatcherServlet();Dynamic registration=context.addServlet("appServlet",ds);registration.addMapping("/");registration.asetMultipartConfig( new MultipartCofigElement("/tmp/spittr/upolads"));
If the configured Servlet initialization class inherits Abstractannotationconfigdispatcherservletinitializer or Abstractdispatcherservletinitializer, You can overload the Customizeregistration () method to configure the specifics of the multipart.
//设置multipart上传配置,路径,文件不超过2MB,请求不超过4MB @Override protected void customizeRegistration(ServletRegistration.Dynamic registration){ registration.setMultipartConfig( new MultipartConfigElement("C:/test",2097152,4194304,0)); }
The Multipartconfigelement constructor can also perform some other settings:
- The maximum file upload value (byte), which is not limited by default;
- The maximum file size (byte) for all multipart requests, regardless of the number of requests, the default is unlimited;
- The maximum value (byte) of uploading a file directly (without storing it to a temporary directory), the default is 0, which means that all files are written to the hard disk;
If you are using the traditional Web. Xml method to set the Dispatcherservlet, then you need to use multiple
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <multipart-config> <location>/tmp/spittr/uploads</location> <max-file-size>2097152</max-file-size> <max-request-size>4194304</max-request-size> </multipart-config></servlet>
Handling multipart Requests
When the processing of the multipart request is configured, the next step is to write the Controller method to accept the uploaded file. The most common way to achieve this is to add @requestpart annotations on a controller method parameter.
Assuming you want users to be able to upload images at registration, you need to make changes to the registration form so that the user can select a picture and change the Processregistration () method in Spittercontroller to get the uploaded file. All you need to do now is update the Processregistration () method:
@RequestMapping(value = "/register", method = RequestMethod.POST)public String processRegistration(@RequestPart("profilePicture") byte[] profilePicture, @Valid Spitter spitter, Errors errors) { ··· }
When the registration form commits, the data in the request portion is assigned to the Profilepicture attribute, and if the user does not select a file, the array is a null value (NOT NULL). Now that you have obtained the uploaded file, the following is what you need to save the file.
Handling Exceptions
No matter what happens, the output of the servlet request is a servlet response, so if an exception occurs, its output is still a servlet response, and one must be converted in some way to a response. Spring provides a variety of ways to convert a one to a response:
- Some spring exceptions are automatically mapped to specific HTTP status codes;
- Use @responsestatus annotations to map an exception to an HTTP status code;
- Methods that use Exceptionhandler annotations can be used to handle exceptions
To map an exception to an HTTP status code
By default, spring converts some of its own exceptions to the appropriate status code.
Spring Exception |
HTTP status Code |
Bindexception |
400-bad Request |
Conversionnotsupportedexception |
500-internal Server Error |
Httpmediatypenotacceptableexception |
406-not acceptable |
Httpmediatypenotsupportedexception |
415-unsupported Media Type |
Httpmessagenotreadableexception |
400-bad Request |
Httpmessagenotwritableexception |
500-internal Server Error |
Httprequestmethodnotsupportedexception |
405-method not allowed |
Methodargumentnotvalidexception |
400-bad Request |
Missingservletrequestparameterexception |
400-bad Request |
Missingservletrequestpartexception |
400-bad Request |
Nosuchrequesthandlingmethodexception |
404-not Found |
Typemismatchexception |
400-bad Request |
To map an exception to a specific status code
import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.ResponseStatus;@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Spittle Not Found")public class SpittleNotFoundException extends Exception {}
How to write exception handling
Mapping exceptions to status codes is more simple and effective in most cases, but what if you want the response to be more than just a single status code? Maybe you want to do something about the exception, just the same as the request.
For example, the Save () method of Spittlerepository throws a duplicatespittleexception when the user repeatedly creates the spittle, so Spittlecontroller savespittle () method is required to handle the exception. As shown in the following code, the Savespittle () method can handle the exception directly:
@RequestMapping(method = RequestMethod.POST)public String saveSpittle(SpittleForm form, Model model) { try { spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude())); return "redirect:/spittles"; } catch (DuplicateSpittleException e) { return "error/duplicate"; }}
This method has two paths, we can deal with the exception in other ways, and this method can be a bit simpler. The Savespittle method that first handles the correct path:
@RequestMapping(method = RequestMethod.POST)public String saveSpittle(SpittleForm form, Model model) { spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude())); return "redirect:/spittles";}
Now add a new method to the Spittlecontroller:
@ExceptionHandler(DuplicateSpittleException.class)public String handleDuplicateSpittle() { return "error/duplicate";}
The @ExceptionHandler annotation is applied on the Handleduplicatespittle () method to specify that it is executed when a Duplicatespittleexception exception is thrown. It is worth noting that the method of @ExceptionHandler annotations is a common sum in the same controller, that is, whichever method of Spittlecontroller throws Duplicatespittleexception exception, The Handleduplicatespittle () method can handle it without having to capture every occurrence of an exception. So, can the method of @ExceptionHandler annotations capture the exceptions in other controllers? It is possible in Spring3.2, but it is only limited to the method defined in the Controller notification class.
What is the Controller notification class? That's what we're going to introduce.
To add a notification to a controller
This can be very handy if specific facets of the controller class are used across all controllers in the application. For example, @ExceptionHandler method can handle exceptions thrown by multiple controllers. If more than one controller class throws the same exception, you might be writing a duplicate @exceptionhandler method in these controllers. Alternatively, you can write a base class for exception handling for other @exceptionhandler methods to inherit.
Spring3.2 brings another workaround: Controller notification. The controller notification is any class with @controlleradvice annotations, and this class contains one or more of the following types of methods:
- @ExceptionHandler annotations.
- @InitBinder annotations.
- @ModelAttribute annotations.
These methods in @ControllerAdvice annotated classes are applied on all @requestmapping annotations of all controllers in the entire application.
The @ControllerAdvice annotations themselves use @component annotations, so classes that use @controlleradvice annotations are extracted when the component is scanned, just like classes that use @controller annotations. One of the most useful features of @ControllerAdvice is to integrate all @exceptionhandler methods into a class so that exceptions in all controllers can be handled in one place. For example, if you want to handle all the duplicatespittleexception exceptions in your app, you can use the following method:
import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;// 声明控制器增强@ControllerAdvicepublic class AppWideExceptionHandler { // 定义异常处理方法 @ExceptionHandler(DuplicateSpittleException.class) public String handleDuplicateSpittle() { return "error/duplicate"; } @ExceptionHandler(SpittleNotFoundException.class) public String handleSpittleNotFound() { return "error/duplicate"; }}
Passing data across redirect requests
The redirect should normally be performed after the POST request has been processed. This prevents the client from re-executing the dangerous POST request when the user clicks the browser's Refresh button or Back button. In the fifth chapter, the redirect: prefix has been used in the view name returned by the Controller method, and the returned string is not used to find the view, but rather a path for the browser to jump:
return "redirect:/spitter/" + spitter.getUsername();
You might think that spring handles redirects only this way, but: spring can do more.
In particular, how does a redirect method send data to a method that handles redirection? In general, when a processing function finishes, the model data in the method is copied to the request as the request property, and the request is passed to the view for parsing. Because the controller and the view face the same request, the request property is preserved at forward.
However, when a controller returns a redirect, the original request terminates and a new HTTP request is opened. All model data in the request will be emptied. The new request will not have any model data.
Obviously, it is no longer possible to use model to pass data when redirect. But there are other ways to get the data from the redirected method:
- Convert data to Path parameter or query parameter
- Sending data in the Flash properties
redirect via URL template
@RequestMapping(value="/",method=POST)public String processRegistration(Spitter spitter,Model model){ spitterRepository.save(spitter); model.addAttribute("username",spitter.getUsername()); model.addAttribute("spitterId",spitter.getId()); return "redirect:/spitter/{username}";}
The redirected string returned does not change, but because the Spitterid property in the model does not map to a placeholder in the URL, it is automatically used as a query parameter.
If username is Habuma,spitterid is 42, then the redirected path returned will be/spitter/habuma?spitterid=42.
Passing data using path parameters and query parameters is simple, but it also has limitations. It only applies to passing simple values, such as strings and numbers, and does not convey more complex things, then we need flash properties to help.
Using the Flash Properties
@RequestMapping(value="/",method=POST)public String processRegistration(Spitter spitter,RedirectAttributes model){ spitterRepository.save(spitter); model.addAttribute("username",spitter.getUsername()); model.addFlashAttribute("spitter",spitter); return "redirect:/spitter/{username}";}
We pass a Spitter object to the Addflashattribute () method, and all flash properties are copied to the session before redirection, and after redirection, the Flash attributes in the session are removed and transferred from the session to the model. The method of handling redirection can access the Spitter object from the model.
Spring Combat Seventh Chapter ———— SPRINGMVC configuration Alternatives