Pages

Monday, 31 March 2014

Lesson 09 - Spring Web MVC Framework - Part A

Note: To reduce the redundant code (like html forms etc..) some places I have used "curl" tool (mac os) to get/post some data to the server. If you are using any unix flavoured machine you are good. Windows machine users consider installing windows version of curl. Apologies for this.

In this chapter we will write some web based applications using spring MVC. If you have already worked with other MVC patterns like struts then you get this chapter quite easily. Like struts has ActionServlet as the basis for main controller which controls the requests, Spring has "DispatcherServlet" to do the same. It dispatches the requests to "handlers" and with the help of "view resolution technique" output is delivered to client with (mostly) a jsp/any app resource. Don't worry to understand the whole thing in one go, we have lots of examples explaining this. Spring support multi part file uploading, locale, theme selection too. We introduce new annotations like @Controller, @RequestMapping, @PathVariable (to create REST style applications) etc..  Spring data binding (binding use inputs to underlying java objects) with out implementing any spring specific APIs is the coolest thing. While binding if any errors found its regarded as validation errors rather application errors. Usually a "Controller" is preparing a Model (map of data) and a "View" name but it can directly write to response/stream as well. You can integrate easily with any other template based rendering technologies like JSP, Velocity, Freemarker or generate XML, JSON, Atom etc.. While going through this chapter we come to see various features of spring mvc.

For some projects other MVC implementations are well suited, the you could easily integrate spring mvc with any other mvc implementation. Simply start up spring root application context using "ContextLoaderListener" and access it through "ServletContext" attribute from struts action.No extra coding/plugins are involved here. You are using spring as a simple library here. With this you could access all spring beans and services are still accessible. In Spring "ContextLoaderListener" is the way to integrate DI to almost all of the application.

DispatcherServlet

Like any other MVC framework, Spring MVC is request driven i.e when a request comes from client, DispatcherServlet (DS) dispatches it to one of the configured controllers and once controller gives back the Model, DS will look for appropriate view resolution mechanism to find out appropriate view to render the output to client. Whole story in short, below copied diagram from spring source explains it:

DS is actually a server inherits from HttpServlet and is defined in web.xml like any other servlet and you map requests to handler using serlvet-mapping tag (standard servlet stuff). Let me show some sample code to understand so far discussion.

Note: when you create project (spring project) in STS choose "Spring MVC Project", With no magic behind it creates a sample project to work on with following code.. 


So FYI I have added dir structure as well.. if you use STS and create a new MVC project its all happens in few seconds. In Web MVC framework each DispatcherServler will have its own ApplicationContext which inherits all the beans already defined in root "WebApplicationContext" (in our case its mentioned in /WEB-INF/spring/root-context.xml)  . Of course as you expect, all these inherited classes can be overridden in the servlet-specific scope along with new beans local to that context.
Upon initialising DS, spring mvc looks for a file called [servlet-name]-serlvet.xml (in our case we explicitly gave file name as "/WEB-INF/spring/appServlet/servlet-context.xml" using contextConfigLocation parameter). Where as in our web.xml we did mention "/WEB-INF/spring/root-context.xml" as well that is used for root spring container (i.e the place to keep items shared by all servlets, filters). In the following sections we see WebApplicationContext (extension of ApplicaitionContext in the context of web to support locale/theme selection etc..). The WebApplicationContext is bound in the ServletContext, and by using static methods on the RequestContextUtils class you can always look up the WebApplicationContext if you need access to it.

Spring maintains some following special beans to process the request and render the views to users. However you dont need to setup all these special beans (as spring beans) since DispatcherServlet.properties files under "org.springframework.web.servlet" package will have what to use as default if user did nt specify these special beans. Sample file is here Once you configure any of the special beans then default value will not be used, for example if you choose to configure "InternalResourceViewResolver" then "ViewResolver" will not be used

Bean typeExplanation
HandlerMappingMaps incoming requests to handlers and a list of pre- and post-processors (handler interceptors) based on some criteria the details of which vary by HandlerMapping implementation. The most popular implementation supports annotated controllers but other implementations exists as well.
HandlerAdapterHelps the DispatcherServlet to invoke a handler mapped to a request regardless of the handler is actually invoked. For example, invoking an annotated controller requires resolving various annotations. Thus the main purpose of a HandlerAdapter is to shield the DispatcherServlet from such details.
HandlerExceptionResolverMaps exceptions to views also allowing for more complex exception handling code.
ViewResolverResolves logical String-based view names to actual View types.
LocaleResolverResolves the locale a client is using, in order to be able to offer internationalized views
ThemeResolverResolves themes your web application can use, for example, to offer personalized layouts
MultipartResolverParses multi-part requests for example to support processing file uploads from HTML forms.
FlashMapManagerStores and retrieves the "input" and the "output" FlashMap that can be used to pass attributes from one request to another, usually across a redirect.
(copied from spring ref documentation)

Life cycle of a web request

Let us understand the sequence of steps in serving the request. When user fires a request, from web.xml based on servlet-mapping one of the DispatcherServlet need to handle the request like below:

a) The WebApplicationContext is searched and bound the incoming request as an attribute. Its bound under "DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE"
b) locale, theme resolvers will be bound to resolve locale, theme requirement if any.
c) if you specify multi part resolver then your request will be wrapped as MultipartHttpServletRequest for further processing by other elements.
d) an matching handler will be searched for. Once its found, chain of handlers are executed (preprocessors, postprocessors, controllers)
e) if model is returned then view is rendered. if no model is returned (some of the pre/post processors must have intercepted the request) then no view is rendered.
f)if there are any exception HandlerExceptionResolvers will pick them up.

In my example I have given "contextConfigLocation" but you could also give "contextClass" - a class implements WebApplicationContext which instantiates the context used by this servlet. By default XmlWebApplicationContext is used. And you could give "namespace" which denotes the namespace of WebApplicationContext.

Controllers - How to Implement them?

Controllers interpret the users input and convert them to model to be rendered by view component.Spring has few annotations such as @RequestMapping, @RequestParam, @ModelAttribute by keeping them we can write powerful controllers with out using any spring specific classes/apis. I think the best approach is to see an example and try to understand. From our above example @Controller tells spring that this class is a controller since @Controller is a stereotype annotation we can use it as a bean and auto searchable (by <context:component-scan> usage) by container. @RequestMapping tells on which url this particular method need to be invoked. In our case on "/" home(...) method will be invoked. We could use @RequestMapping at class level and at method level too. Usually the top level url be used at class level and further narrowing is done at method level. Let me stop the theory and lets swim to example.

If you see this example we have used URI Template pattern where in you find few placeholders wit { and }. When user fills those value and fires a request respective receiving methods will interpret them with the help of @PathVariable annotated variables. Spring support URI patterns with regex as well. If uri is like /getPersonInfo/phone/123-456-789 then following controller will understand 3 parts of it.
@RequestMapping("/getPersonInfo/phone/{firstpart:[0-9]+}-{middlepart:[0-9]+}-{lastpart:[0-9]+}")
  public void decode(@PathVariable String firstpart, @PathVariable String middlepart, String lastpart) {
    // ...
  } }
Added to this, spring supports ant style patterns as well along with few placeholders against system properties or environment variables. It is quite useful if we want to fetch environment variables in the code directly. Spring support matrix variable (Refer documentation as its very rarely used I am not going to cover) using @MatrixVariable as well. RequestMapping allows us to mention the prodcues/consumes attributes to define the media types. So the request will be ONLY matched if "content-type" request header matches with specified media type. Lets see a small example which consumes application/xml and application/json.Same example can be extended for "produces" attribute, in this case method will be called if "Accept"  header is matched by media type mentioned under "produces" attribute of @RequestMapping. (Since with usual browser we cant prove it easily, let me use curl command to prove this)
 
In case if you mention wrong content type (like json and trying to access readxml)
example for json consumption
In case if you mention wrong content type (like xml and trying to access readjson)
if we keep above HTML in browser it looks like this:
Above example demonstrates use of "consumes" attribute..

Defining @RequestMapping handler methods

method marked with @ReuqestMapping can have very flexible signatures (except one condition which we see later). Following sections demonstrates various methods arguments this method can take and possible outcomes of it. To be honest, if you understand this part of this lesson then spring mvc is half done. First lets start with possible/acceptable method arguments to methods marked with @RequestMapping:


Method ArgumentExplanation
Request/Response Objects (classic servlet api stuff)Any HttpServletRequest/HttpServletResponse object (including HttpServletRequest/HttpServletResponse)
Servlet API's SessionThis enforces the presence of a session if not null will be passed. Same rules of session management like "not a thread safe" object applies here too.
WebRequest/NativeWebRequestAllows generic request parameter access + request/session attributes WITHOUT servlet API.
java.util.LocaleLocale of the current request locale resolved by LocaleResolver.
java.io.InputStream / java.io.Reader
&
java.io.OutputStream/ java.io.Writer
to access request's content. This is raw stream given by Servlet API.
&
to generate the response's content (as specified by Servlet API)
java.security.Principalcontains currently authenticated user (more on this later when we talk about spring security +web securtiy)
@PathVariable
@MatrixVariable
As shown above example, variables to interpret path variables passed in URI template.
@RequestParam
@RequestHeader
Annotated parameters can access respective servlet request parameter and request headers respectively.
@RequestBodyAnnotated parameters can access request body and parameter values are converted to the method argument types using HttpMessageConverters
@RequestPartTo access the content of the "multipart/form-data" request part (like when you are uploading files.. we see example below)
HttpEntity<?>to access servlet request http parameters and contents.
Mode/ModelMapTo keep model contents to be used by view component.
RedirectAttributesto mention exact set of attributes to use in case of redirect and also to add flash attributes (only example can explain this better so stay tuned)
CommandForm/Form Object/Backend Objecta java object (a bean) whose properties are set by incoming request parameters.. its like any other java Object (Employee etc..) We will see how the bean properties are automatically set by request parameters.
Errors/BindingResultValidation results for PRECEDING command or form object. NOTE: This parameter should always comes after a command/form object. Ordering is must here.
SessionStatushandle for marking form processing as complete, which triggers the cleanup of session attributes that have been indicated by the @SessionAttributes annotation at the handler type level.
UriComponentBuilderBuilder for preparing the Uri relative to the current request's host, port, context path etc..

Similarly following are valid/supported method return types:



Method Return TypeExplanation
ModelAndViewModel is implicitly enriched with command objects and results of @ModelAttribute annorated reference data accessor methods.
Modelthe view name implicitly determined through a RequestToViewNameTranslator and the model implicitly enriched with command objects and the results of @ModelAttribute annotated reference data accessor methods.
View with the model implicitly determined through command objects and @ModelAttribute annotated reference data accessor methods. The handler method may also programmatically enrich the model by declaring a Model argument (see above).
Mapobject for exposing a model, with the view name implicitly determined through a RequestToViewNameTranslator and the model implicitly enriched with command objects and the results of @ModelAttribute annotated reference data accessor methods.
Stringvalue that is interpreted as the logical view name, with the model implicitly determined through command objects and@ModelAttribute annotated reference data accessor methods. The handler method may also programmatically enrich the model by declaring a Model argument (see above).
void if the method handles the response itself (by writing the response content directly, declaring an argument of type ServletResponse /HttpServletResponse for that purpose) or if the view name is supposed to be implicitly determined through aRequestToViewNameTranslator (not declaring a response argument in the handler method signature).


To understand all of them lets write a sample code snippet. In first example we see all (possibly understandable at the moment) possible method arguments we pass to the methods annotated with @RequestMaping

Above screenshot shows how we can pass around the information to method.. But observe, request body is empty.. why? to understand let us try to POST some data to the above program i.e lets call /checkpost (using CURL since we did nt have any forms to post data as of now) to see what happens when we post something...
 If I keep the output as a html in a file then it looks like below:

This show when we post something then request body is carrying that information like a carrier.In above example we have used @RequesrBody to convert the whole request body to a string. It happens using HttpMessageConverters. Similarly we have @ResponseBody to directly write something to the response. For example in same code above we can write directly to http response stream as shown below:
In the same example we used HttpEntity which is similar to @RequestBody, @ResponseBody with that besides getting access to request and response body it allows access to http request and response headers too. In the below screenshot we are setting a response header and can see thats printed to terminal. And return type of the method is ResponseEntity (a sub class of HttpEntity). As with @RequestBody and @ResponseBody, Spring uses HttpMessageConverter to convert from and to the request and response streams.


Understanding @ModelAttribute (on methods, as arguments to methods)

We can use @ModelAttribute on method as well as method arguments. When its used on methods, it means purpose of that method to add one or more model attributes. Those methods support same arguments supported by @RequestMapping BUT they cant be mapped by requests. These methods will be invoked BEFORE any @RequestMapping methods are invoked. Because of this we usually use them to load the model with commonly needed attributes (like drop downs etc..) a controller can have any number of @Modelattribute methods and all will be invoked before @RequestMapping methods. But what will happen if model attribute name is not explicitly given? In such cases a default name is assigned to the model attribute based on its type. E.g. if model return an object of type Employee, then name of the model object will be "employee". You can change that through the value of the @ModelAttribute annotation. If adding attributes directly to the Model, use the appropriate overloaded addAttribute(..) method - i.e., with or without an attribute name. Below example shows this behaviour. 




If we are using @ModelAttribute on method arguments then its is expected to retrieve that model object form model. if its not present in model, then new object will be instantiated and ADDED to model. Once present in model the arguments fields will be automatically populated from all request parameters that have matching names. Its called as "DATA BINDING" in spring mvc. Its quite powerful (if you see above example Newton has been set to all objects), Usually we populate model attributes and might use them across request with the help of @SessionAttrbutes and in some cases its useful to retrieve a URI template parameter and using a type converter we fetch the complete model object. In below case, based on {account} value we populate "Account account" argument with the help of Converter<String, Account>
@RequestMapping(value="/accounts/{account}", method = RequestMethod.PUT)
public String save(@ModelAttribute("account") Account account) {

}

WebDataBinder is responsible for binding matching request parameters to model attributes. wherever needed type conversion will be applied.  So as a result if there any matching errors then they would be validation errors rather application errors. Those errors are captured in "BindingResult" argument of @RequestMappping method. In addition to that we could have custom validators and passing the BindingResult to it will allow to consolidate all errors in one place.You could invoke validation automatically by keeping a @Valid attribute before the model attribute annotation. Lets see an example to understand this.
So this shows how to use conversion service and use ModelAttributes. If you want to keep some data to access across requests use @SessionAttributes as shown below:
@Controller
@RequestMapping("/editProd.do")
@SessionAttributes("product")
public class EditProductForm {
    // ...
}
We can use @RequestHeader annotation to grab request header attributes as shown in above examples @RequestHeader (value="User-Agent") String browser will interpret user's user-agent (mostly browser) in to a string variable called "browser". Similarly @CookieValue("cookiename") will gets the cookie value to the method argument itself.

DataBinding using WebDataBinder

we have seen all request parameters, attributes, headers are automatically converted and assigned to method arguments but we can still customise it further using WebDataBinder mechanism. To customise request parameter binding with PropertyEditors through Spring's WebDataBinder, you can use @InitBinder-annotated methods within your controller OR @InitBinder methods within an @ControllerAdvice class OR  provide a custom WebBindingInitializer. 

Intercepting requests using HandlerInterceptor

This mechanism is quite cool if you want to perform some custom logic before serving the request such as certain functionality to certain requests. any intercetor used for this method must implement "HandlerInterceptor" which has preHandle, postHandle, afterCompletion works like you expected: before the actual handler is executed, after the actual handle is executed, after the complete request is completed. Usually value returned by preHandle will decide whether to break or continue processing the execution chain. Similarly if you have multiple interceptor you can order them by its order property (but I realised, that concept of ordering works with <bean> style of configuring interceptors only. I did nt find a way in <mvc:interceptors> style). Following example checks if todays day is tuesday or not, if its then we will be forwarded to wikipedia page on tuesday or else it will show us the response on the screen. In the example rather implementing HandlerInterceptor we are extending its implementation called "HandlerInterceptorAdapter"

So Lets move to next topic called Views..

Resolving Views

all MVC frameworks provides a mechanism to show the model to the client. In spring mvc we have concept of view resolvers (which enables to render model to browser without tying you to a specific view technology) and views (like jsp, velocity, xslt etc...) We are going to look at 2 main interfaces called "ViewResolver" (which maps the logical name of the view and actual view) and "View" interface which hands the request over to one of the view technologies.. As we have seen previously, all controller handler methods must resolve to a logical view name, either explicitly (e.g., by returning a String, View, or ModelAndView) or implicitly (i.e., based on conventions). Out of all many available view resolvers we primarily use "UrlBasedViewResolver" and "InternalResourceViewResolver" (a subclass of previous one). We have used "InternalResourceViewResolver" in "servlet-context.xml" like below:


<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>

so whenever we mention logical view name as "welcome" then mvc gets the view file as "/WEB-INF/View/welcome.jsp" from above definition. this is straight forward if we use only one view technology (like jsp). if you are using various view technologies (jsp, velocity etc..) then use ResourceBundleViewResolver like below:


<bean id="viewResolver"
      class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename" value="views"/>
    <property name="defaultParentView" value="parentView"/>
</bean>


The ResourceBundleViewResolver inspects the ResourceBundle identified by the basename, and for each view it is supposed to resolve, it uses the value of the property [viewname].(class) as the view class and the value of the property [viewname].url as the view url. We could always keep multiple resolvers (by setting it "order" property) and higher order runs first. Spring checks for each and resolver in the order to identify the view until finds, if it cant find then ServletException will be thrown.

Redirecting to Views:

As we saw earlier, controller returns a logical view name and resolvers resolves it to a specific view technology. Take for example, for JSPs that are processed through the Servlet or JSP engine, this resolution generally handled through the help of "InternalResourceViewResolver" and "InternalResourceView" which internally issues either forward/include using servlet API's methods. For other view technologies, such as Velocity, XSLT etc.. the view itself writes the content directly to the response stream. One way to force a redirect as a result of controller response is your controller itself create and return the instance of Spring's "RedirectView" then DS will not use normal view resolution but it simply instructs the view to do its work since it has got already the (redirect) view. This RedirectView issues an sendRedirect of (HttpServletResponse) the returns the client browser as an HTTP redirect. There is a special redirect/forward prefix you could use along with view name, once used view resolver will recognise this as a special indication that redirect is needed.This is same like using RedirectView but controller itself operate in terms of logical view names. Similarly :forward prefix will issue an forward() around the view name.


Previous Next

No comments:

Post a Comment