Working with Spring Tag Libraries

In this article by Amuthan G, the author of the book Spring MVC Beginners Guide - Second Edition, you are going to learn more about the various tags that are available as part of the Spring tag libraries.

(For more resources related to this topic, see here.)

After reading this article, you will have a good idea about the following topics:

  • JavaServer Pages Standard Tag Library (JSTL)
  • Serving and processing web forms
  • Form-binding and whitelisting
  • Spring tag libraries

JavaServer Pages Standard Tag Library

JavaServer Pages (JSP) is a technology that lets you embed Java code inside HTML pages. This code can be inserted by means of <% %> blocks or by means of JSTL tags. To insert Java code into JSP, the JSTL tags are generally preferred, since tags adapt better to their own tag representation of HTML, so your JSP pages will look more readable.

JSP even lets you  define your own tags; you must write the code that actually implements the logic of your own tags in Java.

JSTL is just a standard tag library provided by Oracle. We can add a reference to the JSTL tag library in our JSP pages as follows:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

Similarly, Spring MVC also provides its own tag library to develop Spring JSP views easily and effectively. These tags provide a lot of useful common functionality such as form binding, evaluating errors and outputting messages, and more when we work with Spring MVC.

In order to use these, Spring MVC has provided tags in our JSP pages. We must add a reference to that tag library in our JSP pages as follows:

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

These taglib directives declare that our JSP page uses a set of custom tags related to Spring and identify the location of the library. It also provides a means to identify the custom tags in our JSP page. In the taglib directive, the uri attribute value resolves to a location that the servlet container understands and the prefix attribute informs which bits of markup are custom actions.

Serving and processing forms

In Spring MVC, the process of putting a HTML form element's values into model data is called form binding.

The following line is a typical example of how we put data into the Model from the Controller:

model.addAttribute(greeting,"Welcome")

Similarly, the next line shows how we retrieve that data in the View using a JSTL expression:

<p> ${greeting} </p>

But what if we want to put data into the Model from the View? How do we retrieve that data in the Controller? For example, consider a scenario where an admin of our store wants to add new product information to our store by filling out and submitting a HTML form. How can we collect the values filled out in the HTML form elements and process them in the Controller? This is where the Spring tag library tags help us to bind the HTML tag element's values to a form backing bean in the Model. Later, the Controller can retrieve the formbacking bean from the Model using the @ModelAttribute (org.springframework.web.bind.annotation.ModelAttribute) annotation.

The form backing bean (sometimes called the form bean) is used to store form data. We can even use our domain objects as form beans; this works well when there's a close match between the fields in the form and the properties in our domain object. Another approach is creating separate classes for form beans, which is sometimes called Data Transfer Objects (DTO).

Time for action – serving and processing forms

The Spring tag library provides some special <form> and <input> tags, which are more or less similar to HTML form and input tags, but have some special attributes to bind form elements’ data with the form backed bean. Let's create a Spring web form in our application to add new products to our product list:

  1. Open our ProductRepository interface and add one more method declaration to it as follows:
    void addProduct(Product product);
  2. Add an implementation for this method in the InMemoryProductRepository class as follows:
    @Override
    public void addProduct(Product product) {
          String SQL = "INSERT INTO PRODUCTS (ID, "
                + "NAME,"
                + "DESCRIPTION,"
                + "UNIT_PRICE,"
                + "MANUFACTURER,"
                + "CATEGORY,"
                + "CONDITION,"
                + "UNITS_IN_STOCK,"
                + "UNITS_IN_ORDER,"
                + "DISCONTINUED) "
                + "VALUES (:id, :name, :desc, :price, :manufacturer, :category, :condition, :inStock, :inOrder, :discontinued)"; 
    
          Map<String, Object> params = new HashMap<>();
          params.put("id", product.getProductId()); 
          params.put("name", product.getName()); 
          params.put("desc", product.getDescription()); 
          params.put("price", product.getUnitPrice()); 
          params.put("manufacturer", product.getManufacturer()); 
          params.put("category", product.getCategory()); 
          params.put("condition", product.getCondition()); 
          params.put("inStock", product.getUnitsInStock()); 
          params.put("inOrder", product.getUnitsInOrder()); 
          params.put("discontinued", product.isDiscontinued()); 
    
          jdbcTempleate.update(SQL, params);    
       }
  3. Open our ProductService interface and add one more method declaration to it as follows:
    void addProduct(Product product);
  4. And add an implementation for this method in the ProductServiceImpl class as follows:
    @Override
    public void addProduct(Product product) {
       productRepository.addProduct(product);
    }
  5. Open our ProductController class and add two more request mapping methods as follows:
    @RequestMapping(value = "/products/add", method = RequestMethod.GET)
    public String getAddNewProductForm(Model model) {
       Product newProduct = new Product();
       model.addAttribute("newProduct", newProduct);
       return "addProduct";
    }
    
    @RequestMapping(value = "/products/add", method = RequestMethod.POST)
    public String processAddNewProductForm(@ModelAttribute("newProduct") Product newProduct) {
       productService.addProduct(newProduct);
       return "redirect:/market/products";
    }
  6. Finally, add one more JSP View file called addProduct.jsp under the  src/main/webapp/WEB-INF/views/ directory and add the following tag reference declaration as the very first line in it:
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"  %>
  7. Now add the following code snippet under the tag declaration line and save addProduct.jsp. Note that I skipped some <form:input> binding tags for some of the fields of the product domain object, but I strongly encourage you to add binding tags for the skipped fields while you are trying out this exercise:
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <link rel="stylesheet"   href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
    <title>Products</title>
    </head>
    <body>
       <section>
          <div class="jumbotron">
             <div class="container">
                <h1>Products</h1>
                <p>Add products</p>
             </div>
          </div>
       </section>
       <section class="container">
          <form:form  method="POST" modelAttribute="newProduct" class="form-horizontal">
             <fieldset>
                <legend>Add new product</legend>
    
                <div class="form-group">
                   <label class="control-label col-lg-2 col-lg-2" for="productId">Product Id</label>
                   <div class="col-lg-10">
                      <form:input id="productId" path="productId" type="text" class="form:input-large"/>
                   </div>
                </div>
    
                <!-- Similarly bind <form:input> tag for name,unitPrice,manufacturer,category,unitsInStock and unitsInOrder fields-->
    
                <div class="form-group">
                   <label class="control-label col-lg-2" for="description">Description</label>
                   <div class="col-lg-10">
                      <form:textarea id="description" path="description" rows = "2"/>
                   </div>
                </div>
    
                <div class="form-group">
                   <label class="control-label col-lg-2" for="discontinued">Discontinued</label>
                   <div class="col-lg-10">
                      <form:checkbox  id="discontinued" path="discontinued"/>
                   </div>
                </div>
    
                <div class="form-group">
                   <label class="control-label col-lg-2" for="condition">Condition</label>
                   <div class="col-lg-10">
                      <form:radiobutton path="condition" value="New" />New
                      <form:radiobutton path="condition" value="Old" />Old
                      <form:radiobutton path="condition" value="Refurbished" />Refurbished
                   </div>
                </div>
    
                <div class="form-group">
                   <div class="col-lg-offset-2 col-lg-10">
                      <input type="submit" id="btnAdd" class="btn btn-primary" value ="Add"/>
                   </div>
                </div>
             </fieldset>
          </form:form>
       </section>
    </body>
    </html>
  8. Now run our application and enter the URL: http://localhost:8080/webstore/market/products/add. You will be able to see a web page showing a web form to add product information as shown in the following screenshot:Add a products web form
  9. Now enter all the information related to the new product that you want to add and click on the Add button. You will see the new product added in the product listing page under the URL http://localhost:8080/webstore/market/products.

What just happened?

In the whole sequence, steps 5 and 6 are very important steps that need to be observed carefully. Whatever was mentioned prior to step 5 was very familiar to you I guess. Anyhow, I will give you a brief note on what we did in steps 1 to 4.

In step 1, we just created an addProduct method declaration in our ProductRepository interface to add new products. And in step 2, we just implemented the addProduct method in our InMemoryProductRepository class. Steps 3 and 4 are just a Service layer extension for ProductRepository. In step 3, we declared a similar method addProduct in our ProductService and implemented it in step 4 to add products to the repository via the productRepository reference.

Okay, coming back to the important step; what we did in step 5 was nothing but adding two request mapping methods, namely getAddNewProductForm and processAddNewProductForm:

   @RequestMapping(value = "/products/add", method = RequestMethod.GET)
   public String getAddNewProductForm(Model model) {
      Product newProduct = new Product();
      model.addAttribute("newProduct", newProduct);
      return "addProduct";
   }

   @RequestMapping(value = "/products/add", method = RequestMethod.POST)
   public String processAddNewProductForm(@ModelAttribute("newProduct") Product productToBeAdded) {
      productService.addProduct(productToBeAdded);
      return "redirect:/market/products";
   }

If you observe those methods carefully, you will notice a peculiar thing, that is, both the methods have the same URL mapping value in their @RequestMapping annotations (value = "/products/add"). So if we enter the URL http://localhost:8080/webstore/market/products/add in the browser, which method will Spring MVC  map that request to?

The answer lies in the second attribute of the @RequestMapping annotation (method = RequestMethod.GET and method = RequestMethod.POST). Yes if you look again, even though both methods have the same URL mapping, they differ in the request method.

So what is happening behind the screen is when we enter the URL http://localhost:8080/webstore/market/products/add in the browser, it is considered as a GET request, so Spring MVC will map that request to the getAddNewProductForm method. Within that method, we simply attach a new empty Product domain object with the model, under the attribute name newProduct. So in the  addproduct.jsp View, we can access that newProduct Model object:

Product newProduct = new Product();
model.addAttribute("newProduct", newProduct);

Before jumping into the processAddNewProductForm method, let's review the addproduct.jsp View file for some time, so that you understand the form processing flow without confusion. In addproduct.jsp, we just added a <form:form> tag from Spring's tag library:

<form:form modelAttribute="newProduct" class="form-horizontal">

Since this special <form:form> tag is coming from a Spring tag library, we need to add a reference to that tag library in our JSP file; that's why we added the following line at the top of the addProducts.jsp file in step 6:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"  %>

In the Spring <form:form> tag, one of the important attributes is modelAttribute. In our case, we assigned the value newProduct as the value of the modelAttribute in the <form:form> tag. If you remember correctly, you can see that this value of the modelAttribute and the attribute name we used to store the newProduct object in the Model from our getAddNewProductForm method are the same. So the newProduct object that we attached to the model from the Controller method (getAddNewProductForm) is now bound to the form. This object is called the form backing bean in Spring MVC.

Okay now you should look at every <form:input> tag inside the <form:form>tag. You can observe a common attribute in every tag. That attribute is path:

<form:input id="productId" path="productId" type="text" class="form:input-large"/>

The path attribute just indicates the field name that is relative to form backing bean. So the value that is entered in this input box at runtime will be bound to the corresponding field of the form bean.

Okay, now it’s time to come back and review our processAddNewProductForm method. When will this method be invoked? This method will be invoked once we press the submit button on our form. Yes, since every form submission is considered a POST request, this time the browser will send a POST request to the same URL http://localhost:8080/webstore/products/add.

So this time the processAddNewProductForm method will get invoked since it is a POST request. Inside the processAddNewProductForm method, we simply are calling the addProduct service method to add the new product to the repository:

   productService.addProduct(productToBeAdded);

But the interesting question here is how come the productToBeAdded object is populated with the data that we entered in the form? The answer lies in the @ModelAttribute (org.springframework.web.bind.annotation.ModelAttribute) annotation. Notice the method signature of the processAddNewProductForm method:

public String processAddNewProductForm(@ModelAttribute("newProduct") Product productToBeAdded)

Here if you look at the value attribute of the @ModelAttribute annotation, you can observe a pattern. Yes, the @ModelAttribute annotation's value and the value of the modelAttribute from the <form:form> tag are the same. So Spring MVC knows that it should assign the form bounded newProduct object to the processAddNewProductForm method's parameter productToBeAdded.

The @ModelAttribute annotation is not only used to retrieve a object from the Model, but if we want we can even use the @ModelAttribute annotation to add objects to the Model. For instance, we can even rewrite our getAddNewProductForm method to something like the following with using the @ModelAttribute annotation:

@RequestMapping(value = "/products/add", method = RequestMethod.GET)
   public String getAddNewProductForm(@ModelAttribute("newProduct") Product newProduct) {
      return "addProduct";
}

You can see that we haven't created a new empty Product domain object and attached it to the model. All we did was added a parameter of the type Product and annotated it with the @ModelAttribute annotation, so Spring MVC will know that it should create an object of Product and attach it to the model under the name newProduct.

One more thing that needs to be observed in the processAddNewProductForm method is the logical View name it is returning: redirect:/market/products. So what we are trying to tell Spring MVC by returning the string redirect:/market/products? To get the answer, observe the logical View name string carefully; if we split this string with the ":" (colon) symbol, we will get two parts. The first part is the prefix redirect and the second part is something that looks like a request path: /market/products. So, instead of returning a View name, we are simply instructing Spring to issue a redirect request to the request path /market/products, which is the request path for the list method of our ProductController. So after submitting the form, we list the products using the list method of ProductController.

As a matter of fact, when we return any request path with the redirect: prefix from a request mapping method, Spring will use a special View object called RedirectView (org.springframework.web.servlet.view.RedirectView) to issue the redirect command behind the screen.

Instead of landing on a web page after the successful submission of a web form, we are spawning a new request to the request path /market/products with the help of RedirectView. This pattern is called redirect-after-post, which is a common pattern to use with web-based forms. We are using this pattern to avoid double submission of the same form.

Sometimes after submitting the form, if we press the browser's refresh button or back button, there are chances to resubmit the same form. This behavior is called double submission.

Have a go hero – customer registration form

It is great that we created a web form to add new products to our web application under the URL http://localhost:8080/webstore/market/products/add. Why don't you create a customer registration form in our application to register a new customer in our application? Try to create a customer registration form under the URL http://localhost:8080/webstore/customers/add.

Customizing data binding

In the last section, you saw how to bind data submitted by a HTML form to a form backing bean. In order to do the binding, Spring MVC internally uses a special binding object called WebDataBinder (org.springframework.web.bind.WebDataBinder).

WebDataBinder extracts the data out of the HttpServletRequest object and converts it to a proper data format, loads it into a form backing bean, and validates it. To customize the behavior of data binding, we can initialize and configure the WebDataBinder object in our Controller. The @InitBinder (org.springframework.web.bind.annotation.InitBinder) annotation helps us to do that. The @InitBinder annotation designates a method to initialize WebDataBinder.

Let's look at a practical use of customizing WebDataBinder. Since we are using the actual domain object itself as form backing bean, during the form submission there is a chance for security vulnerabilities. Because Spring automatically binds HTTP parameters to form bean properties, an attacker could bind a suitably-named HTTP parameter with form properties that weren't intended for binding. To address this problem, we can explicitly tell Spring which fields are allowed for form binding. Technically speaking, the process of explicitly telling which fields are allowed for binding is called whitelisting binding in Spring MVC; we can do whitelisting binding using WebDataBinder.

Time for action – whitelisting form fields for binding

In the previous exercise while adding a new product, we bound every field of the Product domain in the form, but it is meaningless to specify unitsInOrder and discontinued values during the addition of a new product because nobody can make an order before adding the product to the store and similarly discontinued products need not be added in our product list. So we should not allow these fields to be bounded with the form bean while adding a new product to our store. However all the other fields of the Product domain object to be bound. Let's see how to this with the following steps:

  1. Open our ProductController class and add a method as follows:
    @InitBinder
    public void initialiseBinder(WebDataBinder binder) {
       binder.setAllowedFields("productId",
                "name",
                "unitPrice",
                "description",
                "manufacturer",
                "category",
                "unitsInStock",
                "condition");
    }
  2. Add an extra parameter of the type BindingResult (org.springframework.validation.BindingResult) to the processAddNewProductForm method as follows:
    public String processAddNewProductForm(@ModelAttribute("newProduct") Product productToBeAdded, BindingResult result)
  3. In the same processAddNewProductForm method, add the following condition just before the line saving the productToBeAdded object:
    String[] suppressedFields = result.getSuppressedFields();
       if (suppressedFields.length > 0) {
          throw new RuntimeException("Attempting to bind disallowed fields: " + StringUtils.arrayToCommaDelimitedString(suppressedFields));
       }
  4. Now run our application and enter the URL http://localhost:8080/webstore/market/products/add. You will be able to see a web page showing a web form to add new product information. Fill out all the fields, particularly Units in order and discontinued.
  5. Now press the Add button and you will see a HTTP status 500 error on the web page as shown in the following image:

    The add product page showing an error for disallowed fields

  6. Now open addProduct.jsp from /Webshop/src/main/webapp/WEB-INF/views/ in your project and remove the input tags that are related to the Units in order and discontinued fields. Basically, you need to remove the following block of code:
    <div class="form-group">
       <label class="control-label col-lg-2" for="unitsInOrder">Units In
          Order</label>
       <div class="col-lg-10">
          <form:input id="unitsInOrder" path="unitsInOrder"  type="text" class="form:input-large"/>
       </div>
    </div>
    
    <div class="form-group">
       <label class="control-label col-lg-2" for="discontinued">Discontinued</label>
       <div class="col-lg-10">
          <form:checkbox  id="discontinued" path="discontinued"/>
       </div>
    </div>
  7. Now run our application again and enter the URL http://localhost:8080/webstore/market/products/add. You will be able to see a web page showing a web form to add a new product, but this time without the Units in order and Discontinued fields.
  8. Now enter all information related to the new product and click on the Add button. You will see the new product added in the product listing page under the URL http://localhost:8080/webstore/market/products.

What just happened?

Our intention was to put some restrictions on binding HTTP parameters with the form baking bean. As we already discussed, the automatic binding feature of Spring could lead to a potential security vulnerability if we used a domain object itself as form bean. So we have to explicitly tell Spring MVC which are fields are allowed. That's what we are doing in step 1.

The @InitBinder annotation designates a Controller method as a hook method to do some custom configuration regarding data binding on the WebDataBinder. And WebDataBinder is the thing that is doing the data binding at runtime, so we need to tell which fields are allowed to bind to WebDataBinder. If you observe our initialiseBinder method from ProductController, it has a parameter called binder, which is of the type WebDataBinder. We are simply calling the setAllowedFields method on the binder object and passing the field names that are allowed for binding. Spring MVC will call this method to initialize WebDataBinder before doing the binding since it has the @InitBinder annotation.

WebDataBinder also has a method called setDisallowedFields to strictly specify which fields are disallowed for binding . If you use this method, Spring MVC allows any HTTP request parameters to be bound except those fields names specified in the setDisallowedFields method. This is called blacklisting binding.

Okay, we configured which the allowed fields are for binding, but we need to verify whether any fields other than those allowed are bound with the form baking bean. That's what we are doing in steps 2 and 3.

We changed processAddNewProductForm by adding one extra parameter called result, which is of the type BindingResult. Spring MVC will fill this object with the result of the binding. If any attempt is made to bind any fields other than the allowed fields, the BindingResult object will have a getSuppressedFields count greater than zero. That's why we were checking the suppressed field count and throwing a RuntimeException exception:

if (suppressedFields.length > 0) {
throw new RuntimeException("Attempting to bind disallowed fields: " + StringUtils.arrayToCommaDelimitedString(suppressedFields));
}

Here the static class StringUtils comes from org.springframework.util.StringUtils.

We want to ensure that our binding configuration is working—that's why we run our application without changing the View file addProduct.jsp in step 4. And as expected, we got the HTTP status 500 error saying Attempting to bind disallowed fields when we submit the Add products form with the unitsInOrder and discontinued fields filled out. Now we know our binder configuration is working, we could change our View file so not to bind the disallowed fields—that's what we were doing in step 6; just removing the input field elements that are related to the disallowed fields from the addProduct.jsp file.

After that, our added new products page just works fine, as expected. If any of the outside attackers try to tamper with the POST request and attach a HTTP parameter with the same field name as the form baking bean, they will get a RuntimeException.

The whitelisting is just an example of how can we customize the binding with the help of WebDataBinder. But by using WebDataBinder, we can perform many more types of binding customization as well. For example, WebDataBinder internally uses many PropertyEditor (java.beans.PropertyEditor) implementations to convert the HTTP request parameters to the target field of the form backing bean. We can even register custom PropertyEditor objects with WebDataBinder to convert more complex data types. For instance, look at the following code snippet that shows how to register the custom PropertyEditor to convert a Date class:

@InitBinder
public void initialiseBinder (WebDataBinder binder) {
  DateFormat dateFormat = new SimpleDateFormat("MMM d, YYYY");
  CustomDateEditor orderDateEditor = new CustomDateEditor(dateFormat, true);
  binder.registerCustomEditor(Date.class, orderDateEditor);
}

There are many advanced configurations we can make with WebDataBinder in terms of data binding, but for a beginner level, we don’t need to go that deep.

Pop quiz – data binding

Considering the following data binding customization and identify the possible matching field bindings:

@InitBinder
public void initialiseBinder(WebDataBinder binder) {
   binder.setAllowedFields("unit*");
}
  1. NoOfUnit
  2. unitPrice
  3. priceUnit
  4. united

Externalizing text messages

So far in all our View files, we hardcoded text values for all the labels; for instance, take our addProduct.jsp file—for the productId input tag, we have a label tag with the hardcoded text value as Product id:

<label class="control-label col-lg-2 col-lg-2" for="productId">Product Id</label>

Externalizing these texts from a View file into a properties file will help us to have a single centralized control for all label messages. Moreover, it will help us to make our web pages ready for internationalization. But in order to perform internalization, we need to externalize the label messages first. So now you are going to see how to externalize locale-sensitive text messages from a web page to a property file.

Time for action – externalizing messages

Let's externalize the labels texts in our addProduct.jsp:

  1. Open our addProduct.jsp file and add the following tag lib reference at the top:
    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
  2. Change the product ID <label> tag's value ID to <spring:message code="addProdcut.form.productId.label"/>. After changing your product ID <label> tag's value, it should look as follows:
    <label class="control-label col-lg-2 col-lg-2" for="productId"> <spring:message code="addProduct.form.productId.label"/> </label>
  3. Create a file called messages.properties under /src/main/resources in your project and add the following line to it:
    addProduct.form.productId.label = New Product ID
  4. Now open our web application context configuration file WebApplicationContextConfig.java and add the following bean definition to it:
    @Bean
    public MessageSource messageSource() {
       ResourceBundleMessageSource resource = new ResourceBundleMessageSource();
       resource.setBasename("messages");
       return resource;   
    }
  5. Now run our application again and enter the URL http://localhost:8080/webstore/market/products/add. You will be able to see the added product page with the product ID label showing as New Product ID.

What just happened?

Spring MVC has a special a tag called <spring:message> to externalize texts from JSP files. In order to use this tag, we need to add a reference to a Spring tag library—that's what we did in step 1. We just added a reference to the Spring tag library in our addProduct.jsp file:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

In step 2, we just used that tag to externalize the label text of the product ID input tag:

<label class="control-label col-lg-2 col-lg-2" for="productId"> <spring:message code="addProduct.form.productId.label"/> </label>

Here, an important thing you need to remember is the code attribute of <spring:message> tag, we have assigned the value addProduct.form.productId.label as the code for this <spring:message> tag. This code attribute is a kind of key; at runtime Spring will try to read the corresponding value for the given key (code) from a message source property file.

We said that Spring will read the message’s value from a message source property file, so we need to create that file property file. That's what we did in step 3. We just created a property file with the name messages.properties under the resource directory. Inside that file, we just assigned the label text value to the message tag code:

addProduct.form.productId.label = New Product ID

Remember for demonstration purposes I just externalized a single label, but a typical web application will have externalized messages  for almost all tags; in that case messages messages.properties file will have many code-value pair entries.

Okay, we created a message source property file and added the <spring:message> tag in our JSP file, but to connect these two, we need to create one more Spring bean in our web application context for the org.springframework.context.support.ResourceBundleMessageSource class with the name messageSource—we did that in step 4:

@Bean
public MessageSource messageSource() {
   ResourceBundleMessageSource resource = new ResourceBundleMessageSource();
   resource.setBasename("messages");
   return resource;   
}

One important property you need to notice here is the basename property; we assigned the value messages for that property. If you remember, this is the name of the property file that we created in step 3.

That is all we did to enable the externalizing of messages in a JSP file. Now if we run the application and open up the Add products page, you can see that the product ID label will have the same text as we assigned to the  addProdcut.form.productId.label code in the messages.properties file.

Have a go hero – externalize all the labels from all the pages

I just showed you how to externalize the message for a single label; you can now do that for every single label available in all the pages.

Summary

At the start of this article, you saw how to serve and process forms, and you learned how to bind form data with a form backing bean. You also learned how to read a bean in the Controller. After that, we went a little deeper into the form bean binding and configured the binder in our Controller to whitelist some of the POST parameters from being bound to the form bean. Finally, you saw how to use one more Spring special tag <spring:message> to externalize the messages in a JSP file.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Spring MVC: Beginner's Guide - Second Edition

Explore Title
comments powered by Disqus