Implementing Custom Views

Although Spring provides view classes and view resolvers for a variety of view technologies, you might run into a situation where you need to implement a custom view. One of the authors once came across a requirement where he needed to implement an ultra-high performance application, largely based on XML. Instead of implementing an XSLTView using a style sheet, he chose to implement a new view, based on SAXHandlers that rendered the (simple) views. Because the SAXHandlers could stream the HTML back to the client, it greatly improved the performance of the application.

View and AbstractView

Implementing a custom view begins with implementing the org.springframework.web.servlet.View interface. The view interface specified just one method:

void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception;

The Map is the model returned from the controller. Because Spring Web MVC is a web-oriented framework, and clients will always access your controllers with an HTTP client (such as a browser), rendering the view will be a bit difficult if we're not passing along the HttpServletRequest and the HttpServletResponse.

For convenience purposes, Spring implements an AbstractView class, offering some common functionality shared by all view implementations. This includes the following:

  • requestContextAttribute property, when there is a need to expose the Spring Request Context object. By default this is null, which will result in no RequestContext being bound to the request.

  • staticAttributesMap property and staticAttributesCSV setter (parsing static attributes into the staticAttributesMap), allowing users to specify attributes that will be bound to the request independently of the model.

  • contentType property, to specify the content type of the view. In many cases this will be text/ html, but in the case of the AbstractPDFView, it's set to application/pdf. View implementations typically set this property in a hard-coded fashion (still allowing developers to override it if needed).

  • Overridden render() method, taking care of the creation of the RequestContext, the binding of the static attributes to the request, and the calling of the Template Method renderMerged OutputModel().

  • Extension of WebApplicationObjectSupport, offering access to the WebApplicationContext.

When implementing a custom view, you can either extend AbstractView or AbstractUrlView, where the latter adds a url property to consistently model URL-based views (such as RedirectView or the InternalResourceView).

Implementing a View Generating XML from a Data Object

The following example shows how to implement a view that outputs Hibernate XML generated by the Hibernate Databinder ( We're using the XMLDatabinder in this case, which unfortunately requires a SessionFactory.

 public class HibernateDataView extends AbstractView {       /** needed by Hibernate Databinder */   private SessionFactory sessionFactory;   /** transformer */   private Transformer transformer;   /** model attribute to use (if null, all objects will be bound and output) */   private String modelAttribute;       public void setSessionFactory(SessionFactory sessionFactory() {     this.sessionFactory = sessionFactory;   }       public void setModelAttribute(String modelAttribute) {     this.modelAttribute = modelAttribute;   }       protected void initApplicationContext()    throws Exception {     TransformerFactory factory = TransformerFactory.newInstance();     this.transformer = factory.newTransformer();   }       protected void renderMergedOutputModel(Map model,      HttpServletRequest request, HttpServletResponse response)   throws Exception {         XMLDatabinder dataBinder = new XMLDatabinder(this.sessionFactory);     dataBinder.initializeLazy(false);     if (modelAttribute != null) {       dataBinder.bind(model.get(modelAttribute));     }     else {       Iterator it = model.values().iterator();       while (it.hasNext()) {         dataBinder.bind(;       }     }         PrintWriter writer = response.getWriter();     writer.write(dataBinder.toGenericXML());     writer.close();   } }

Using a ResourceBundleViewResolver (or any other view resolver), we can now wire up and use our newly created HibernateDataView to render Hibernate-persisted objects to our users:

 <bean     >   <property name="modelAttribute"><value>show</value></property> </bean> 

If, in a controller, we're returning a ModelAndView, as shown in the following code, the user will be presented with an XML representation of the Show loaded by our data-access object.

private ShowDao showDao;     protected ModelAndView handleRequestInternal(   HttpServletRequest request, HttpServletResponse response) throws Exception {   Long id = WebUtils.getRequiredLongParameter("id");   Show show = showDao.loadShow(id);   return new ModelAndView("hibernateShowView", "show", show); } 


Hibernate's Databinder infrastructure can be used to generate XML for any object registering with the Hibernate SessionFactory. In combination with an XSL style sheet, you can use this to generate PDFs, for example, using Apache FOP.

Considerations When Implementing Custom Views

Many of Spring's view resolvers include a caching facility. Spring maintains an internal cache of logical view names mapped to View instances. If possible, design your view implementation to be threadsafe in order to make it reusable across more than just one request. If this is not possible, you can disable caching by setting the cache property of the view resolver to false (caching of views is available only on the AbstractCachingViewResolver and its subclasses).

Before embarking on the implementation of your own custom view, check for already existing implementations. It's very likely that the team that created the view technology can implement a Spring view class in no time, if they haven't done this already.

Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: