Document directory
- @ Requestmapping
- Flexible request processing method Signature
- Extract and parse parameters with @ requestparam
- Continue @ requestmapping
- Eliminate class-level request ing
- @ Modelattribute Annotation
- Store attributes with @ sessionattributes
- Custom Data Binding
- Data Binding result and Verification
The Spring framework has been committed to providing powerful, non-invasive solutions for complex problems since its creation. Spring 2.0 introduces the custom namespace function to reduce the number of xml configuration files, since then, it is deeply rooted in core spring frameworks (AOP, context, Jee, JMS, Lang, TX and util namespaces), spring portfolio projects (such as spring Security) and non-spring projects (such as cxf ).
Spring 2.5 released a complete set of annotations as a replacement scheme for XML-based configuration. Annotations can be used for automatic discovery, dependency injection, lifecycle methods, web layer configuration, and unit/integration testing of spring management objects.
The annotation Technology series introduced in spring 2.5 consists of three parts. This article is the second one, which mainly describes the annotation support in the web layer. The last article focuses on other features that can be used for integration and testing.
The first part of this series of articles discusses how Java annotations replace XML to configure spring management objects and dependency injection. Let's review the following example:
@Controller public class ClinicController { private final Clinic clinic; @Autowired public ClinicController(Clinic clinic) { this.clinic = clinic; } ...
@ Controller indicates that cliniccontroller is a web layer component and @ autowired requests a dependent injection clinic instance. In this example, only a few XML statements are required to enable the container to identify two annotations and limit the scanning scope of the component:
<context:component-scan base-package="org.springframework.samples.petclinic"/>
This is a good news for the web layer, because the xml configuration files on this layer of spring have become increasingly bloated and may even be less useful than the configuration at the layer. The Controller has many attributes, such as the view name, form object name, And validator type. These attributes are mostly related to configuration and little about dependency injection. Using Bean definition inheritance, or avoiding attributes that are not frequently changed in configurations, you can also effectively manage similar configurations. However, in my experience, many developers will not do this. The result is that the XML file is larger than actually needed. However, @ controller and @ autowired play a positive role in web layer configuration.
In the second part of the series, we will continue to discuss this issue and explore the annotation Technology of spring 2.5 on the web layer. These annotations are informal called @ MVC, which involves spring MVC and spring porlet MVC. In fact, most of the functions discussed in this article can be applied to these two frameworks.
From controller to @ Controller
Compared with the annotation discussed in the first part, @ MVC is not just a simple replacement solution for configuration. Consider the famous spring MVC controller signature below:
public interface Controller { ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; }
All spring MVC controllers either directly implement the Controller Interface, or expand the base class implementation like abstractcontroller, simpleformcontroller, multiactioncontroller, or abstractwizardformcontroller. The Controller interface allows spring MVC's dispatcherservlet to regard all the above objects as "handlers" and call them with the help of an adapter named simplecontrollerhandleradapter.
@ MVC has changed the programming model from three important aspects:
- No interface or base class is required.
- Any number of request processing methods are allowed.
- High flexibility in method signatures.
Considering the above three points, it can be fair to say that @ MVC is not only a replacement solution, but also an important step in the evolution of spring MVC controller technology.
Dispatcherservlet calls the annotated controller with the help of an adapter named annotationmethodhandleradapter. It is this adapter that has done a lot of work to support the annotations we will discuss later, and it also effectively replaces the requirements for the controller base class.
@ Requestmapping
We start with a controller similar to the traditional spring MVC controller:
@Controller public class AccountsController { private AccountRepository accountRepository; @Autowired public AccountsController(AccountRepository accountRepository) { this.accountRepository = accountRepository; } @RequestMapping("/accounts/show") public ModelAndView show(HttpServletRequest request, HttpServletResponse response) throws Exception { String number = ServletRequestUtils.getStringParameter(request, "number"); ModelAndView mav = new ModelAndView("/WEB-INF/views/accounts/show.jsp"); mav.addObject("account", accountRepository.findAccount(number)); return mav; } }
The difference here is that the controller does not extend the Controller Interface, and it uses the @ requestmapping annotation to specify show () is the request processing method mapped to the URI path "/accounts/Show. In addition, the rest of the code is the content of a typical spring MVC controller.
After fully converting the above method to @ MVC, we will look back at @ requestmapping. However, before that, please note that, the above request ing URI can also match the URI path with any extension, for example:
/accounts/show.htm /accounts/show.xls /accounts/show.pdf ...
Flexible request processing method Signature
We promised to provide flexible method signatures. Now let's take a look at the results. The input parameter removes the response object and adds a map representing the model. The returned value is no longer modelandview, but a string that specifies the view name to use when the response is presented:
@RequestMapping("/accounts/show") public String show(HttpServletRequest request, Map<String, Object> model) throws Exception { String number = ServletRequestUtils.getStringParameter(request, "number"); model.put("account", accountRepository.findAccount(number)); return "/WEB-INF/views/accounts/show.jsp"; }
The map input parameter is an "implicit" model, which is convenient for us to create before calling a method. The added key-value pair data can be easily parsed and applied in the view. In this example, the show. jsp page is displayed.
@ MVC can accept multiple types of input parameters, such as httpservletrequest/httpservletresponse, httpsession, locale, inputstream, outputstream, and file []. Their order is not limited; it also allows multiple return types, such as modelandview, MAP, string, or nothing. You can view the javadoc of @ requestmapping to understand all the input and return parameter types it supports.
It is interesting to see what happens when the method does not specify a view (for example, if the return type is void). According to the Convention, dispatcherservlet will use the request URI path information again, however, you must remove the front slash and extension. Let's change the return type to void:
@RequestMapping("/accounts/show") public void show(HttpServletRequest request, Map<String, Object> model) throws Exception { String number = ServletRequestUtils.getStringParameter(request, "number"); model.put("account", accountRepository.findAccount(number)); }
For the ing between the given request processing method and "/accounts/Show" request, we can expect dispatcherservlet to get the default view name of "accounts/Show, when it works together with the following appropriate view parser, the results will be the same as the previously specified returned view Name:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> </bean>
It is strongly recommended that the view name be dependent on the Convention, because hard-coded view names can be removed from the Controller code. If you want to customize the dispatcherservlet method to get the default view name, configure your own requesttoviewnametranslator implementation in the servlet context environment and assign the bean ID "viewnametranslator ".
Extract and parse parameters with @ requestparam
@ MVC another feature is its ability to extract and parse request parameters. Let's continue to refactor the above method and add the @ requestparam annotation in it:
@RequestMapping("/accounts/show") public void show(@RequestParam("number") String number, Map<String, Object> model) { model.put("account", accountRepository.findAccount(number)); }
Here, the @ requestparam annotation can be used to extract a string-type parameter named "Number" and pass it as an input parameter. @ Requestparam supports type conversion and optional parameters. Currently, type conversion supports all basic java types. You can extend the range by customizing propertyeditors. The following are some examples, including required and optional parameters:
@RequestParam(value="number", required=false) String number @RequestParam("id") Long id @RequestParam("balance") double balance @RequestParam double amount
Note that the last example does not provide a clear parameter name. When the code is compiled with a debug symbol, the result extracts the parameter "amount". Otherwise, the illegalstateexception is thrown because the current information is insufficient to extract the parameter from the request. For this reason, it is best to explicitly specify the parameter name during encoding.
Continue @ requestmapping
It is legal to put @ requestmapping at the class level, which allows it to work with the @ requestmapping annotation at the method level to narrow the selection scope. The following are some examples.
Class level:
RequestMapping("/accounts/*")
Method level:
@ Requestmapping (value = "delete", method = requestmethod. Post)
@ Requestmapping (value = "Index", method = requestmethod. Get, Params = "type = checking ")
@ Requestmapping
The first method-level request ing works with class-level ing. When the HTTP method is post, it matches the path "/accounts/Delete". The second method adds a requirement, the request parameter named "type" and Its Value "checking" need to appear in the request; the third does not specify the path at all. This method matches all HTTP methods, you can use its method name if necessary. Next we will rewrite our method so that it can match by method name. The program is as follows:
@Controller @RequestMapping("/accounts/*") public class AccountsController { @RequestMapping(method=RequestMethod.GET) public void show(@RequestParam("number") String number, Map<String, Object> model) { model.put("account", accountRepository.findAccount(number)); } ...
The request for method matching is "/accounts/Show", based on the matching path "/accounts/*" specified by @ requestmapping at the class level and the method name "show ".
Eliminate class-level request ing
There is a factual basis for frequent criticism of web layer annotations, that is, the URI path of the embedded source code. This problem is well corrected. The matching relationship between the URI path and the Controller class is managed using the xml configuration file, and the @ requestmapping annotation is only used in method-level ing.
We will configure a controllerclassnamehandlermapping which maps the URI to the controller using the Convention that relies on the Controller Class Name:
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
Now all requests such as "/accounts/*" are matched to the accountscontroller. It works well with the @ requestmapping annotation at the method level. You only need to add the method name to complete the above ing. In addition, since our method does not return the view name, we can now match the class name, method name, URI path, and view name according to the Convention.
After @ controller is fully converted to @ MVC, the program is written as follows:
@Controller public class AccountsController { private AccountRepository accountRepository; @Autowired public AccountsController(AccountRepository accountRepository) { this.accountRepository = accountRepository; } @RequestMapping(method=RequestMethod.GET) public void show(@RequestParam("number") String number, Map<String, Object> model) { model.put("account", accountRepository.findAccount(number)); } ...
The xml configuration file is as follows:
<context:component-scan base-package="com.abc.accounts"/> <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> </bean>
You can see that this is the most refined XML. The URI path is not embedded in the annotation in the program, and the view name is not explicitly specified. The request processing method only has a very simple line. The method signature exactly matches our requirements, other request processing methods are also easy to add. No base class or XML (or at least no controller is configured directly), we can gain all the advantages above.
You can see how effective this program design model is.
@ MVC Form Processing
A typical form processing scenario includes obtaining editable objects, displaying the data it holds in editing mode, allowing users to submit and ultimately verify and save the changed data. Spring MVC provides the following features to assist in all the above activities: the data binding mechanism fills an object completely with the data obtained from the request parameters; supports error processing and verification; JSP form tag library; base controller. Using @ MVC, except for the existence of annotation @ modelattribute, @ initbinder, and @ sessionattributes, the base class controller is no longer required.
@ Modelattribute Annotation
Take a look at the request processing method signature:
@RequestMapping(method=RequestMethod.GET) public Account setupForm() { ... } @RequestMapping(method=RequestMethod.POST) public void onSubmit(Account account) { ... }
They are very valid request processing method signatures. The first method processes the initial http get request, prepares the edited data, and returns an account object for the spring MVC form tag. The second method processes subsequent http post requests when users submit changes, and receives an account object as the input parameter. It is automatically filled by the Data Binding Mechanism of spring MVC with the parameters in the request. This is a very simple program model.
The account object contains the data to be edited. In spring MVC, an account is called a form model object. This object must use a name to let the form tag (and Data Binding Mechanism) know its existence. The following is a part of the code captured from the JSP page, referencing a form model object named "Account:
<Form: Form modelattribute = "Account" method = "Post">
Account Number: <form:input path="number"/><form:errors path="number"/> ... </form>
Even if we do not specify the "Account" name anywhere, this JSP program will work very well with the method signature mentioned above. This is because @ MVC uses the type name of the returned object as the default value. Therefore, an account-type object corresponds to a form model object named "Account" by default. If it is not appropriate by default, you can use @ modelattribute to change its name, as shown below:
@RequestMapping(method=RequestMethod.GET) public @ModelAttribute("account") SpecialAccount setupForm() { ... } @RequestMapping(method=RequestMethod.POST) public void update(@ModelAttribute("account") SpecialAccount account) { ... }
@ Modelattribute can also be placed at the method level, and the effect is slightly different:
@ModelAttribute public Account setupModelAttribute() { ... }
Here setupmodelattribute () is not a request processing method, but a method used to prepare a form item model object before any request processing method is called. For old users familiar with spring MVC, this is very similar to the formbackingobject () method of simpleformcontroller.
In the initial get method, we get a form model object. In the subsequent post method, when we overwrite the existing account object with user changes based on the data binding mechanism, we will get it for the second time. It is very useful to put @ modelattribute on the method in this form processing scenario. Of course, as a replacement scheme for getting objects twice, we can also save it into the http session during the two request processes ), this is what we will analyze below.
Store attributes with @ sessionattributes
@ Sessionattributes annotation can be used to specify the name or type of the form model object to be put into the session during the request process. The following are some examples:
@Controller @SessionAttributes("account") public class AccountFormController { ... } @Controller @SessionAttributes(types = Account.class) public class AccountFormController { ... }
According to the above annotation, accountformcontroller will put the form model object named "account" (or, as in the second example, store all form Model Objects of Account type into http session. However, when a change occurs consecutively, the attribute object should be removed from the session. We can use the sessionstatus instance to do this. If we add it to the onsubmit method signature, @ MVC will complete this task:
@RequestMapping(method=RequestMethod.POST) public void onSubmit(Account account, SessionStatus sessionStatus) { ... sessionStatus.setComplete(); // Clears @SessionAttributes }
Custom Data Binding
Sometimes data binding needs to be customized. For example, we may need to specify the required domain or register the custom propertyeditors for similar things such as date and currency amount. It is very easy to implement these functions with @ MVC:
@InitBinder public void initDataBinder(WebDataBinder binder) { binder.setRequiredFields(new String[] {"number", "name"}); }
The @ initbinder annotation method can access the @ MVC databinder instance used to bind request parameters. It allows us to customize required items for each controller.
Data Binding result and Verification
Data Binding may cause errors similar to type conversion or missing fields. No matter what happens, we want to return the edited form and let the user correct it. To achieve this goal, we can directly append a bindingresult object to the form model object of the method signature. The routine is as follows:
@RequestMapping(method=RequestMethod.POST) public ModelAndView onSubmit(Account account, BindingResult bindingResult) { if (bindingResult.hasErrors()) { ModelAndView mav = new ModelAndView(); mav.getModel().putAll(bindingResult.getModel()); return mav; } // Save the changes and redirect to the next view... }
When an error occurs, we return to the problematic view and add the attributes obtained from bindingresult to the model. In this way, errors in specific fields can be fed back to users. It should be noted that, instead of specifying an explicit view name, dispatcherservlet is allowed to rely on the default view name matching the entry URI path information.
Call the validator object and pass the bindingresult to it. Only this line of code can be used for verification. This allows us to collect binding and verification errors in one place:
@RequestMapping(method=RequestMethod.POST) public ModelAndView onSubmit(Account account, BindingResult bindingResult) { accountValidator.validate(account, bindingResult); if (bindingResult.hasErrors()) { ModelAndView mav = new ModelAndView(); mav.getModel().putAll(bindingResult.getModel()); return mav; } // Save the changes and redirect to the next view... }
Now it is time to end our spring 2.5 web layer annotation (the informal name is @ MVC) journey.
Summary
The web layer annotation has proved to be quite useful, not only because it can greatly reduce the number of xml configuration files, it also makes it a refined, flexible, and concise programming model with free access to spring MVC controller technology. We strongly recommend that you use the Convention-over-Configuration feature and a processor-mapped-centered policy to distribute requests to the Controller, avoid embedding the URI path in the source code or defining explicit view name references.
Finally, this article does not discuss some important Spring MVC extensions. The latest spring web flow version 2 adds some features, such as spring MVC Based on the JSF view and spring JavaScript library, as well as advanced state and Navigation Management that support more advanced editing scenarios.
View Original English text:Spring 2.5: New features in spring MVC.