Pivotal Knowledge Base

Follow

Using Annotated MVC Controllers and IoC XML Defined Controllers Together

Environment

 Product  Version
 Spring  All

Overview

Spring Framework uses two styles for defining Spring MVC controllers: Annotations and XML configuration files. In an application migration scenario, you should typically use both styles at the same time. This article explains how that can be achieved.

Description

The typical case for using both types of defining controllers is a migration period from 2.0.x Spring-based web application (for example, the classic style of definitions), to a 3.x Spring one, or to the annotation style.

You can keep an existent handler mapping strategy and add those HandlerMapping and HandlerAdapter implementations that are specific to an annotation-based approach: DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter.

Consider the code of the Petclinic sample application that comes with Spring 2.0.x releases:

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
       <property name="mappings">
           <props>
               <prop key="/welcome.htm">clinicController</prop>
               <prop key="/vets.htm">clinicController</prop>
               <prop key="/findOwners.htm">findOwnersForm</prop>
               <prop key="/owner.htm">clinicController</prop>
               <prop key="/addOwner.htm">addOwnerForm</prop>
               <prop key="/editOwner.htm">editOwnerForm</prop>
               <prop key="/addPet.htm">addPetForm</prop>
               <prop key="/editPet.htm">editPetForm</prop>
               <prop key="/addVisit.htm">addVisitForm</prop>
           </props>
       </property>
   </bean>
  .........

  <!-- This bean is a MultiActionController that manages general View rendering.

       It uses the "clinicControllerResolver" bean below for method name resolution. -->

<bean id="clinicController" class="org.springframework.samples.petclinic.web.ClinicController">
       <property name="methodNameResolver" ref="clinicControllerResolver" />
       <property name="clinic" ref="clinic" />
  </bean>

 <!-- This bean is a MethodNameResolver definition for a MultiActionController.</p>

 It maps URLs to methods for the "clinicController" bean. -->

<bean id="clinicControllerResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
       <property name="mappings">
           <props>
               <prop key="/welcome.htm">welcomeHandler</prop>
               <prop key="/vets.htm">vetsHandler</prop>
               <prop key="/owner.htm">ownerHandler</prop>
           </props>
       </property>
  </bean>

Using Annotations

This is how a Spring annotation can be added to the web application as a way of declaring controllers. Consider a new controller class annotated with the @Controller annotation:

package org.springframework.samples.petclinic.web.annotation;
// imports
@Controller public class MedsController { @RequestMapping("/meds") public ModelMap medsHandler() { List meds = new ArrayList(); // code omitted for clarity return new ModelMap("medsList", meds); }
}

To add the class above as a controller to the sample application, these changes must be implemented in the XML configuration file:

 <context:component-scan base-package="org.springframework.samples.petclinic.web.annotation" />

This line tells the Spring bean factory to scan the mentioned package for classes that are considered to be "components". By default, classes annotated with @Component, @Repository, @Service, or @Controller (or classes annotated with a custom annotation that itself is annotated with @Component) are the only detected candidate components.

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" /> 

By defining the handler mapping above, you enable the mapping of handlers based on HTTP paths expressed through the RequestMapping annotation at the type or method level. An example is the medsHandler() method in the MedsController defined above.

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

This is the HandlerAdapter responsible for calling the correct handlers for those HTTP paths expressed through the RequestMapping annotation. The mapping definitions between the HTTP paths and methods are taken care of by the DefaultAnnotationHandlerMapping class.

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

Whenever a handler adapter or handler mapping is explicitly defined in the BeanFactory, the default adapters are not registered automatically (the same rule applies for HandlerMappings). SimpleControllerHandlerAdapter, being one of the default handler adapters to be registered automatically, is no longer registered. This handler adapter is dealing with classes implementing Controller interface. To allow us to have both styles of defining a controller in the same application (XML and using annotations), this handler adapter needs to be explicitly defined. This matters in the following case:

<context:component-scan base-package="org.springframework.samples.petclinic.web.annotation" />
 <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
 <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="order" value="0" />
 </bean>  

 <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
 <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="order" value="1" />
   <property name="mappings">
     <props>
       <prop key="/welcome.htm">clinicController</prop>
       <prop key="/vets.htm">clinicController</prop>
       <prop key="/meds.htm">clinicController</prop>
       <prop key="/findOwners.htm">findOwnersForm</prop>
       <prop key="/owner.htm">clinicController</prop>
       <prop key="/addOwner.htm">addOwnerForm</prop>
       <prop key="/editOwner.htm">editOwnerForm</prop>
       <prop key="/addPet.htm">addPetForm</prop>
       <prop key="/editPet.htm">editPetForm</prop>
       <prop key="/addVisit.htm">addVisitForm</prop>
     </props>
   </property>
 </bean>

 <bean id="clinicControllerResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
   <property name="mappings">
     <props>
       <prop key="/welcome.htm">welcomeHandler</prop>
       <prop key="/vets.htm">vetsHandler</prop>
       <prop key="/meds.htm">medsHandler</prop>
       <prop key="/owner.htm">ownerHandler</prop>
     </props>
   </property>
 </bean>

MVC Namespace

Another approach is to use an element instead of explicitly defining DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter instances. The former approach is more appropriate for the use case of migrating a 2.0.x Spring application to a 3.0.x Spring application because it offers the possibility of defining an ordered list of handler adapters.

Summary

The sample above contains the two styles of defining controllers in an application, and a way of mapping the same HTTP path to two different controllers: SimpleUrlHandlerMapping maps "/meds.htm" to clinicController and DefaultAnnotationHandlerMapping and context:component-scan, which take MedsController and map its medsHandler() method to the same HTTP path ("/meds").

Using the order property for SimpleUrlHandlerMapping and DefaultAnnotationHandlerMapping, you can define a priority: the smaller the order number is, the higher the priority. In the example above, the DefaultAnnotationHandlerMapping instance takes precedence and, therefore, the annotation style of defining controllers.

Comments

Powered by Zendesk