devxlogo

Stereotype Annotations Cut Down XML Configuration in Spring

Stereotype Annotations Cut Down XML Configuration in Spring

nnotations have been part of the Spring Model-View-Controller (MVC) Framework since Spring 2.0. In Spring, annotations can greatly reduce the amount of XML bean configuration and wiring needed. Given the many components of the Spring MVC environment (handler mapping, controller, view resolver, and view), XML configuration can turn unwieldy in a hurry. So, Spring MVC configuration is certainly one area that can really benefit from reduced configuration.

As of Spring 2.5, the framework added new annotations to more easily configure and assemble the components of a multi-layered application, such as you might find in an MVC-designed system. In fact, an important type of annotation added in Spring 2.5, stereotype annotations, is for configuring the Spring MVC controller components.

Are these new annotations critical to your applications? Well, Rod Johnson (Spring founder, project lead, and CEO of SpringSource) has indicated that the future of Spring MVC lies in the new Spring 2.5 annotations. In an interview with InfoQ on January 22, 2009, Johnson said:

In Spring framework 3.0 there will be a number of deprecations, for example, the old MVC controller hierarchy. We view the Annotation-based Spring MVC usage model that we introduced in Spring 2.5 as being a far superior replacement.

That makes these new annotations, in particular those associated to Spring MVC, pretty important. Thus, the purpose of this article is to help you learn what looks to be the future of Spring development.

Stereotype Annotations

The component annotations introduced in Spring 2.5 are really just a continuation of the “stereotype” annotations introduced in Spring 2.0. (The stereotype annotation that Spring 2.0 introduced was @Repository.) Stereotype annotations are markers for any class that fulfills a role within an application. This helps remove, or at least greatly reduce, the Spring XML configuration required for these components. Specifically, the roles or stereotypes defined in Spring today include Repository, Service, and Controller.

Spring also defines these stereotypes as specializations of a more generic stereotype, Component. The Component annotation allows the Spring team to create new stereotypes in future versions of the framework. It also allows you to define your own stereotype components. So Spring actually defines four stereotype annotations. Table 1 lists the four types of stereotypes and their purposes.

Table 1. The Four Types of Spring Stereotype Components and Their Purposes
Stereotype DescriptionAnnotation
Component: Generic stereotype@Component
Repository: A class that serves in the persistence layer of the application as a data access object (DAO), otherwise known as a repository in some other technologies@Repository
Service: A class that is a business service façade, often providing some reusable business processing@Service
Controller: A controller component in the presentation layer of the application, as it relates to a MVC-designed application@Controller

Per the Spring documentation, in addition to simplifying configuration and wiring, stereotype annotations make your application components “more properly suited for processing by tools or associating with aspects.”

Using Stereotype Annotations: Setup and Configuration

To use the Spring stereotype annotations, you need a Spring 2.5 or later environment. That is, the Spring 2.5 or later JAR file (spring.jar) must be available on the classpath of your project. The first Spring stereotype, @Repository, was released with Spring 2.0. Therefore, if @Repository were the only annotation you used, a Spring 2.0 environment would suffice.

Spring MVC provides many additional annotations that support and augment the @Controller annotation in web applications, which means you also need the Spring MVC library JAR (spring-webmvc.jar) to use many of the controller-related annotations.

Again, the purpose of the stereotype annotation (or any annotation for that matter) is to reduce the amount of XML configuration. Therefore, annotated beans serving in a stereotype role do not need to be configured in the Spring XML configuration file. Instead, Spring provides a mechanism to automatically detect (something the Spring Framework documentation calls “autodect”) stereotype components.

To have the Spring Framework autodect the stereotype beans, include a component-scan element in the Spring XML configuration file (as shown below). Note the context schema that is required to use the component-scan element.

  ...               

The scan element forces the Spring container to search the package specified in the base-package attribute for all stereotype components. The base-package attribute specifies the parent package to look for stereotype components, but the Spring container scans all sub-packages as well. Use a comma-separated list of package names when you need to search multiple packages:

By default, all components marked with the @Repository, @Service, @Controller, and @Component annotations are detected in the scan. However, you can configure the component-scan element with filters to include or exclude classes in the scan. In the example below, a regular expression include filter allows all classes that end in “Dao” to be included in the scan. Conversely, any component marked with a “@Service” annotation is excluded from the scan.

  

Five different types of filters are available for including or excluding classes in a scan. Table 2 describes each of them.

Table 2. Five Types of Filter for Including/Excluding Classes from a Stereotype Component Scan
Filter TypeDescriptionExpression Example
annotationInclude or exclude those classes with a stereotype annotationorg.springframework. stereotype.Repository
aspectjUse an AspectJ expression to specify classes to include or exclude (requires AspectJ libraries)com.intertech..*Controller
assignableInclude or exclude classes that extend or implement this class or interfaceorg.intertech.mvc.BaseController
customA custom implementation of the org.springframework.core.type.TypeFilter interfacecom.intertech.IntertechTypeFilterImpl
regexUse a regular expression to specify classes to include or exclude.*Controller

In essence, default annotation include filters scan the @Repository, @Service, @Controller, and @Component annotations, but you can disable this behavior. To turn off the automatic inclusion of components with the stereotype annotations, add the use-default-filters=”false” attribute to the component-scan element. The example component-scan configuration below turns off this default behavior (to include all components with a stereotype annotation) and includes only those assignable from com.intertech.mvc.IntertechBaseController.

use-default-filters="false"> 

Annotating Components

To create a stereotype component, apply the @Repository, @Service, @Controller, or @Component annotation on the class definition. Consider the typical service-to-DAO component relationship as set up in a standard Spring XML configuration file:

 
Figure 1. Relationship of Components in a Configuration to One Another: These are typical components that must be configured by XML in a Spring application.

Figure 1 depicts the relationship of the components in this configuration to one another. Using a stereotype annotation, you can simply mark the OrderDao bean as a DAO component using @Repository.

@Repositorypublic class OrderDaoImpl implements OrderDao { ...}

When Spring automatically detects the stereotype components, BeanNameGenerator assigns a default name to each one. BeanNameGenerator is an interface (org.springframework.beans.factory.support.BeanNameGenerator) that Spring provides by default. It works with the component scanner to provide bean names. By default, the Spring BeanNameGenerator gives each stereotype component a lower CamelCase of the class name as its name. So, in the example above, the repository’s name would be “orderDaoImpl.” If you don’t like the default name, you can implement your own generator and register it with the component scanner:

name-generator="com.intertech.utils.IntertechNameGenerator" />

Also, you can give an explicit name to any stereotype component by providing a component name with the annotation:

@Repository("myOrderDao")public class OrderDaoImpl implements OrderDao{ ...}

Given the stereotype annotation, the Spring XML configuration file no longer has to carry the DAO bean. Here is the reduced XML Spring configuration, given the repository annotation and repository’s default name:

 

In this scenario, you probably would make the OrderService a stereotype component (with an @Service annotation) also. Furthermore, because you are using annotations, you would likely use the @Autowired annotation with regard to the DAO:

@Servicepublic class OrderService {	 @Autowired private OrderDao orderDao;  ...}

Because Spring automatically includes some additional BeanPostProcessors when you use the component-scan element (), it automatically provides annotated autowiring with the component-scan as well. So now, with stereotype and @Autowired annotations in these two classes, absolutely no XML configuration is required for either the OrderService or OrderDao!

As with all Spring beans, stereotype components are singletons by default. However, you can set their scope explicitly with a @Scope annotation (org.springframework.context.annotation.Scope):

@Scope("prototype")@Repositorypublic class OrderDaoImpl implements OrderDao{ ...}
Author’s Note: Spring did not introduce the @Scope annotation until version 2.5. So even though repository components (@Repository) are available in Spring 2.0, prototype repositories are not.

Stereotype Controllers

Like other stereotype components, the @Controller annotation lets you designate any Spring MVC controller as such without XML configuration. For example, the following is a simple Spring MVC controller (extending AbstractController) annotated with the @Controller stereotype annotation:

@Controllerpublic class HelloWorldController extends AbstractController {  protected ModelAndView handleRequestInternal(HttpServletRequest       request, HttpServletResponse response) throws Exception {    String name = request.getParameter("name");    return new ModelAndView("greet", "name", name);  }}
Figure 2. Three MVC Components That Must Be Configured: The three major components that must be constructed and configured (usually via XML) to make a Spring MVC application work include the handler mapping, controller, and view resolver.

Controllers are one of three MVC components that usually have to be configured in XML in order to handle any web request. The other two are handler mappings and view resolvers (see Figure 2).

By itself, the @Controller annotation removes one of the three big MVC components from the Spring XML configuration (as shown by the following sample configuration). However, as you will see, additional Spring 2.5 controller annotations can remove the need to configure one of the other MVC components as well.

            /greeting.request=greetingController          

To help explain the important annotations associated with the controller component, the code download for this article contains two small Java Eclipse projects:

  • HelloWorldOriginal.zip is a Spring MVC Hello World application pre-annotations.
  • HelloWorldAnnotated.zip is a Spring MVC application with the new stereotype annotations.

The sample configuration above shows the simple HelloWorldController annotated with the new @Controller stereotype annotation. By the way, as with any other stereotype component, you can explicitly name and scope the controller by annotation as shown here:

@Controller("greetingController")@Scope("prototype")public class HelloWorldController extends AbstractController { ...}

As mentioned previously, many additional annotations are provided in the Spring MVC library to work with the @Controller annotation. Also note that the MVC annotations are available for either the web (a.k.a. Servlet) or Portlet MVC environments.

RequestMapping by Annotation

When building Spring beans or components, those beans should not have to extend any Spring class or implement a Spring interface. In rare cases when Spring provides an interface or class that your bean implements or extends, the documentation is less than coy in its warnings. Take, for example, the documentation for the InitializingBean interface, which allows initialization work to occur after all properties on a bean have been set:

Generally, the use of the InitializingBean interface can be avoided and is actually discouraged since it unnecessarily couples the code to Spring.

Wait a minute! Have you examined your Spring MVC components lately? Almost all of the Spring MVC components, from handler mappings to views, require the extension or implementation of a Spring MVC class or interface! @Controller alone doesn’t fix this (check out HelloWorldController again). Some, including members of the Spring development team, have recognized this as intrusive. The @RequestMapping annotation helps remove this coupling, at least as far as controllers are concerned.

The @RequestMapping annotation, working with the @Controller annotation, maps request URLs to a controller. You can use this annotation on the controller type itself or on the controller methods. When the @RequestMapping annotation is used on the controller class (type-level annotation), it directs specific requests to the controller. For example, the @RequestMapping annotation here directs all requests for http://…/greeting.request to the HelloWorldController.

@Controller@RequestMapping("/greeting.request")public class HelloWorldController extends AbstractController {  protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) 
throws Exception { String name = request.getParameter("name"); return new ModelAndView("greet", "name", name); }}

The @RequestMapping annotation, along with the @Controller stereotype annotation, simplifies MVC development in a couple of ways:

  1. The @RequestMapping does the job of the Spring MVC handler mapping! Therefore, the handler mapping configuration is not required. The XML configuration just got smaller again.
  2. The @RequestMapping also took away the need to name the controller, which is usually used by handler mappings. Note the @Controller annotation is without a name parameter.

So now, because of the @Controller and @RequestMapping annotations, two of the three major Spring MVC components (the handler mapping and controller) do not need to be configured in XML. You need only the view resolver to configure an MVC set of components to handle a request:

    

When you use @RequestMapping on methods of the controller (called method-level annotation), it eliminates the need to couple the controller to any type. Any class can serve in the role of controller, and the @RequestMapping on the method guides request traffic to the specific methods defined in the class. No longer do controllers have to extend or implement a Spring MVC controller class or interface! In fact, the controller classes don’t even have to have an attachment to the Servlet API:

@Controllerpublic class HelloWorldController {  @RequestMapping("/greeting.request")  protected String sayHello (){    return "greet";  }}

Note that the request-mapping annotation can use a ‘*’ so that the multiple-request URLs are mapped to a single controller or controller method (see below).

@RequestMapping(value="/*.request",method=RequestMethod.POST)protected String sayHello (){ return "greet";}

Given the method-level @RequestMapping annotations, multi-action controllers are a breeze to develop. In fact, multi-action controllers get implemented no differently than other controllers. A multi-action controller using controller annotations might look like this:

@Controllerpublic class CustomerController {  @RequestMapping("/addCustomer.request")  public String addCustomer() {    //do the work to add a customer  }  @RequestMapping("/deleteCustomer.request")  public void deleteCustomer() {    //do the work to delete a customer  }  @RequestMapping("/displayCustomer.request")  public String displayCustomer() {    //do the work to retrieve a customer  }}

You can use the @RequestMapping to further specify which method to call for a request. That is, @RequestMapping focuses specific HTTP method (GET, POST, etc.) requests to specific handler methods within the controller. For example, the type-level annotation here routs the request URLs to the controller.

@Controller@RequestMapping("/greeting.request")public class HelloWorldController {  @RequestMapping(method=RequestMethod.POST)  protected String sayHello (){    return "greet";  }  @RequestMapping(method=RequestMethod.GET)  protected String showForm (){    return "getname";  }}

You can accomplish the type-level and method-level annotations above entirely at the method-level as shown here:

@Controllerpublic class HelloWorldController {  @RequestMapping(value="/greeting.request",method=RequestMethod.POST)  protected String sayHello (){    return "greet";  }  @RequestMapping(value="/greeting.request",method=RequestMethod.GET)  protected String showForm (){    return "getname";  }}

The ability to use @RequestMapping to route requests based on URLs as well as HTTP methods allows any class to serve in a behavior similar to that of form controllers.

Handler Method Parameters

You can pass arguments to controller handler methods (methods with the @RequestMapping annotation). Specifically, you can pass any argument of the type listed in Table 3 to a handler method without further annotation in the controller. The Spring container automatically locates the correct object and passes it as the proper argument.

Table 3. Types of Arguments You Can Pass to Handler Methods
Parameter TypeDescription
BindingResultSpring MVC BindingResult object (org.springframework.validation.BindingResult)
Command ObjectSpring MVC-defined Command objects
ErrorsSpring MVC Errors object (org.springframework.validation.Errors)
InputStream/ReaderServlet API provided raw InputStream/Reader (java.io.InputStream or Reader)
LocaleRequest locale (java.util.Locale)
MapMap provided as Spring model to the view (java.util.Map)
ModelMap provided as Spring model to the view (org.springframework.ui.Model)
ModelMapMap provided as Spring model to the view (org.springframework.ui.ModelMap)
OutputStream/WriterServlet API provided raw OutputStream/Writer (java.io.OutputStream or Writer)
RequestServlet API Request object (use either HttpServletRequest or ServletRequest)
ResponseServlet API Response object (use either HttpServletRequest or ServletRequest)
SessionServlet API Session object
SessionStatusSpring provided handle for marking when form processing is finished (org.springframework.web.bind.support.SessionStatus)
WebRequestSpring-provided objects for parameter and attribute access without the Servlet API (use org.springframework.web.context.request.WebRequest or NativeWebRequest)

The Spring container automatically detects the parameter need and passes the appropriate object to the handler method. For example, the sayHello method here automatically gets passed the HttpServletRequest object.

@Controllerpublic class HelloWorldController {  @RequestMapping("/greeting.request")  protected String sayHello (HttpServletRequest req){    String name = req.getParameter("name");    return "greet";  }}
Author’s Note: When creating the handler methods, consider some of the ramifications of using certain parameter types. Using parameters such as Model or HttpServletRequest couples the controller to Spring, the Servlet API, or both. Part of the rationale for moving to the annotated controllers may be to remove or reduce such coupling.

You can use the @RequestParam annotation, another of the Spring 2.5 MVC annotations, on a handler method to bind a request parameter to a method parameter. In the code example above, the “name” parameter is programmatically fetched out of the HttpServletRequest object. In the following example, the @RequestParam annotation accomplishes the same task more directly and automatically.

@Controllerpublic class HelloWorldController {  @RequestMapping("/greeting.request")  protected String sayHello (@RequestParam("name") String name){    return "greet";  }}

By default, parameters annotated with @RequestParam are required method parameters. You can make a parameter optional by setting the required attribute of @RequestParam to false.

Similar to how @RequestParam binds a request parameter to a method parameter, the @ModelAttribute annotation can be used on a handler method parameter to bind a model object’s attribute to a method parameter.

 @RequestMapping("/displaycustomer.request") public String show(@ModelAttribute("customer") Customer cust) {  ...  return "displaycustomer"; }

The @ModelAttribute annotation also has a second use. You can use it to put information, such as reference data, into the model. When you place the @ModelAttribute annotation on a method, the method it annotates will get executed before the appropriate handler method. This has the effect of pre-populating the model.

Consider the HTML for a simple form entry page in Listing 1. The @ModelAttribute annotated method in the controller populates the “titles” options in the getname.jsp before the page displays.

@Controllerpublic class HelloWorldController {  @ModelAttribute("titles")  public List populateTitles() {  String[] titles = { "Mr.", "Mrs.", "Miss", "Ms.", "Dr." };    return Arrays.asList(titles);   }    @RequestMapping(value="/greeting.request",method=RequestMethod.GET)  protected String showForm (){    return "getname";  }  @RequestMapping(value="/greeting.request",method=RequestMethod.POST)  protected String sayHello (@RequestParam(value="name") String name, @RequestParam(value="title") String title){    System.out.println("Yep name is: " + title + " " + name);    return "greet";  }}

Handler Method Return Types

Handler methods can return almost any type of data. You can use the data returned as the model, view, or other such Spring MVC component depending on its type. Table 4 describes the types of data that a handler method can return and how the Spring container uses that data. Again, using a Spring type (like ModelAndView) as the return type of a handler method couples the controller to Spring. So use some return objects with caution.

Table 4. Types and Uses of Data That a Handler Method Can Return
Return Data TypeHow the Return Data Is Used
ModelAndViewUsed as any ModelAndView object would be used, as if returned by a handleRequest method of a non-annotated controller (org.springframework.web.servlet.ModelAndView)
ModelThe Model object (of the ModelAndView) containing key-value pairs of model data (org.springframework.ui.Model)
MapKey-value pair model data (java.util.Map)
ViewThe View object (of the ModelAndView) containing information on the logical view name (org.springframework.web.servlet.View)
StringInterpreted as the logical view name, as would be represented in a View object
voidIndicates that the handler method handles the response rendering itself, just as any handleRequest type method of a non-annotated controller
otherAny other return type gets treated as a single model attribute value accessed by the name specified in @ModelAttribute (if provided) or based on the return type class name (when @ModelAttribute is not used)

Annotation Pros and Cons

Stereotype annotations can greatly reduce Spring XML configuration. XML documents are not the easiest to read or maintain, and XML itself is very verbose. Annotations are not only a simpler alternative, but they also are attached to the program artifacts they describe, giving the metadata information relevance that is not always there or clear when viewed in an XML file. In Spring, this sentiment is expressed by the “don’t repeat yourself” (DRY) principle. Annotations provide one unambiguous and authoritative representation for any piece of knowledge.

However, because annotations are part of the source code, they are compiled right into the class files. This has both advantages and disadvantages. On one hand, an incorrect or stale XML file cannot be accidentally used to configure the application. On the other hand, annotations require the code to be recompiled if a configuration change is necessary; some request mapping was moved to XML configuration files in some MVC environments precisely to avoid this. An XML configuration file allows for application changes without the source.

Figure 3. Application Changes Between Dev and Production Environments: Critics of annotations claim XML configuration allows changes between development and production environments more easily.

Consider what changes as you move your application from dev to test to stage to production platforms (see Figure 3). Do these changes also impact cross-cutting concerns?

In any case, Spring Stereotype annotations can greatly reduce the amount of XML configuration required to build and maintain a Spring application. In some cases, they allow the application components to be more loosely coupled to Spring or other APIs. Ceratainly, given comments by Rod Johnson and others, the Spring communicaty can look forward to annotations playing an even bigger part of Spring applications in the future.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist