Home > Software Development > Practical Spring MVC Part 3: The Presentation Layer

Practical Spring MVC Part 3: The Presentation Layer

In this quickly-paced, demo-driven series, I will take you on an exciting tour of Spring MVC. Unlike “pet clinic” style demonstrations, I will make use of practical solutions to actual-world issues in order to demonstrate the breadth of functionality provided by Spring MVC. This post is a excellent fit for any person looking for a fast overview of Spring MVC and its capabilities. Moreover, full code is incorporated for those who wish to a follow along closely and discover the ideas presented.

In this third part we will explore Spring MVC’s integration with various presentation layer technologies including Freemarker, Velocity, and JSP.  We will then go a step additional and show how to help PDF, Excel, Word, XML, and JSON output.

Introduction

Spring MVC controllers are completely decoupled from the presentation layer.  So, how does Spring know how to render a response?  The answer is the ViewResolver interface.  Implementations of ViewResolver simply locate a View appropriate for the current request.  The View is then rendered by Spring in the context of the model.

ViewResolvers

Spring MVC offers a wealth of view resolver implementations out-of-the-box.  The most flexible and useful group are the URL-based the resolvers which interpret the return value of a controller method as a URL pointing to a template file.  Consider the following Freemarker view resolver:

1
2
3
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
    p:viewClass='me.tedyoung.blog.support.FixedFreeMarkerView'
    p:prefix= "/views/" p:suffix=".ftl"/>

If a controller method returns “articles/list”, the view resolver will prepend “/WEB-INF/views/” and append “.ftl”.  The result is the following URL: “/WEB-INF/views/articles/list.ftl”.  The view resolver locates the corresponding template file and wraps it in a View, which is subsequently rendered by Spring.  There are identical implementations for JSP and Velocity.

Generally, for most applications, one of these the resolvers is sufficient.  But, the decoupling of controllers from the presentation layer offers us incredible flexibility.  In this article we will see how far we can stretch that flexibility.

ContentNegotiatingViewResolver

Spring offers yet another implementation of ViewResolver referred to as the ContentNegotiatingViewResolver that enables us to pick the sort of response we want to produce primarily based on the sort of content material requested.  For example, if the client requests HTML, we can send an HTML representation of our web page.  However, if the client requests PDF, we can send a PDF representation as an alternative.

How does a client requests a specific kind of content?  Generally, this is controlled through the Accepts header.  Sadly, most of your clientele will be web browsers and really few internet browsers permit the user to offer custom Accepts headers for every request.  Fortunately, ContentNegotiatingViewResolver will also think about the file extension of the URL.  As such, “/articles/list.html” will be treated as an HTML request although “/write-up/list.pdf” will be treated as a PDF request.  By the way, this is why we did not include the file extension in our @RequestMappings.

As soon as the specific request kind has been determined, ContentNegotiatingViewResolver will forward the request to the first view resolver that supports the particular kind of content material requested.  In the end, this permits us to help exporting our blog’s pages in a selection of different formats.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<bean id='viewResolver' class='org.springframework.web.servlet.view.ContentNegotiatingViewResolver'>
<property name="mediaTypes">
    <map>
        <entry key="html" value="text/html;charset=UTF-8"/>
        <entry key="xls" value="application/vnd.ms-excel"/>
        <entry key="xml" value="application/xml"/>
        <entry key="json" value="application/json"/>
        <entry key="pdf" value="application/pdf"/>
    </map>
</property>
<property name="viewResolvers">
    <list>
        <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
            p:viewClass='me.tedyoung.blog.support.FixedFreeMarkerView'
            p:contentType="text/html;charset=UTF-8"
            p:exposeSpringMacroHelpers='true'
            p:prefix= "/views/" p:suffix=".ftl"/>
        <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
            p:viewClass='me.tedyoung.blog.support.FixedFreeMarkerView'
            p:contentType="application/vnd.ms-excel"
            p:exposeSpringMacroHelpers='true'
            p:prefix= "/views/" p:suffix=".ftl"/>
        <bean class="me.tedyoung.blog.support.HTML2PDFViewResolver"
            p:template='/WEB-INF/xsl/html.xsl'>
            <property name="viewResolver">
                <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
                    p:viewClass='me.tedyoung.blog.support.FixedFreeMarkerView'
                    p:exposeSpringMacroHelpers='true'
                    p:prefix= "/views/" p:suffix=".ftl"/>
            </property>
        </bean>
    </list>
</property>
<property name="defaultViews">
    <list>
        <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
        <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
            <property name='marshaller'>
                <bean class='org.springframework.oxm.jaxb.Jaxb2Marshaller'
                    p:classesToBeBound='me.tedyoung.blog.Article'/>
            </property>
        </bean>
    </list>
</property>
</bean>

Configuration

The mediaTypes property permits us to map file extensions to content material kinds (i.e. MIME varieties).  As soon as the ContentNegotiatingViewResolver identifies the content sort of the request it will iterate more than all of the viewResolvers to locate a view resolver that supports the appropriate content kind.  If it can’t discover 1, it will iterate over the defaultViews until I can find a view capable of rendering the appropriate content sort.

Results

If you have not done so already, you may wish to download the source code and run it so you can follow along with the rest of this article.  After all, seeing is believing and the best way to appreciate the power of Spring MVC.

HTML

When you make a request for “/articles/list.html”, you will be presented with the classic HTML view of the list of articles.  However, we might want to allow the export of this list to Excel for off-line reporting and bulk manipulation.

Excel and Word

There are two ways of generating Microsoft Word and Excel documents: through native APIs (such as POI) or by exploiting the fact that both of these applications can import HTML.  Spring supports both.  In this example I chose the second option as it allows us to use templates rather than procedural code.

Look at the first two resolvers configured:

1
2
3
4
5
6
7
8
9
10
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
    p:viewClass='me.tedyoung.blog.support.FixedFreeMarkerView'
    p:contentType="text/html;charset=UTF-8"
    p:exposeSpringMacroHelpers='true'
    p:prefix= "/views/" p:suffix=".ftl"/>
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
    p:viewClass='me.tedyoung.blog.support.FixedFreeMarkerView'
    p:contentType="application/vnd.ms-excel"
    p:exposeSpringMacroHelpers='true'
    p:prefix= "/views/" p:suffix=".ftl"/>

The initial 1 is explicitly configured (by means of the content material type house) to handle HTML requests (text/html) whilst the second one is configured to manage Excel requests (application/vnd.ms-excel).  Other than that, the view resolvers are configured identically.  Fundamentally, the exact same template file will be utilized for both types of requests.  The only difference will be the content kind generated for the response.  When the browser receives a content material kind of application/vnd.ms-excel, it will forward the response to Microsoft Excel, which will just import the HTML table.

So, when you make a request for “/articles/list.xls”, you will be presented with an Excel spreadsheet containing the list of articles.  But, you will also see a bunch of other content material such as the query box.  Theoretically, 1 can reuse the exact same template among HTML and Excel.  Realistically, there are generally subtle differences in between the two.  As such, you can simply modify the configuration of the Excel the resolver so that it appears for a distinct template.  For example, you could alter the suffix to be “-excel.ftl”.  As such, the above request would be forwarded to the template “list-excel.ftl” instead of just “list.ftl”.

The same exact technique can be employed for generating Microsoft Word documents.  You simply create an extra view resolver and set the content sort to “application/vnd.ms-word”.

PDF

Sadly, converting from HTML to PDF is not really as simple.  One choice would be to use native APIs, and there are several accessible.  However, I will use this chance to demonstrate the composition of several technologies so that we can continue to use templates to create our PDFs by means of the use of a custom ViewResolver.

If you make a request for “/articles/1.pdf” you will get a PDF rendered version of the article view web page.
The rewards of the solution consist of that pages in our weblog can be exported to PDF with out any further coding and with out the use of low level binary APIs.  An additional advantage is that the rendering of HTML to PDF is controlled completely through a single XSL stylesheet.  For instance, the stylesheet can be altered to hide elements such as navigation and menus, which are seldom useful in a printed copy of a webpage.

The a single drawback is that overall performance is not wonderful.  While this method will function just fine for exporting individual webpages to PDF, it may not be sufficiently performant to handle hundreds of requests per second.  But, it undoubtedly shows the capability of Spring MVC’s view resolver interface.

XML

XML conversion functions a small bit differently.  Rather of making use of a template file, contents of the model are just converted to XML.  As such, there is no want for a complete-blown ViewResolver.  Rather, Spring’s support for XML rendering is implemented as a simple View.  This is why it is configured in a slightly various way from the earlier technologies.

By just adding this additional View, you can now make a request for “/articles/1.xml” and get an XML representation of the article.  The conversion from article to XML is done through JAXB, a standard Java framework for converting POJOs to and from XML.  Handle over the conversion procedure is obtained through the use of annotations in the Post class.

Once more, without having any modification of our controller we are now in a position to expose the blog’s information for consumption by external solutions.

JSON

Supporting JSON is just as straightforward as supporting XML.  The configuration and the conversion method are practically identical.  In this case, Jackson is utilized to convert the article instance to JSON.  Once again, handle over the conversion method is obtained by means of annotations in the Post class.

A request for “/articles/1.json” will return a JSON representation of the article.  Whilst this output can also be consumed by external solutions, it is especially beneficial for retrieving info via AJAX.  Now, with a single line of configuration, our website’s JavaScript can retrieve data from our server.

That becoming stated, neither of the solutions for JSON nor XML is perfect if you want to offer full-fledged internet service interfaces.  A net service interface is a distinct user interface from the browser/HTML interface and, as such, ought to be implemented with a separate set of controllers, which I will do in the subsequent element.

 

  1. No comments yet.