Spring in Practice

Willie Wheeler's Spring blog

Annotation-based Autowiring in Spring 2.5

| Comments

This article gives a quick summary of how to do autowiring with Spring 2.5 using annotations. We cover the data and services tiers here, but not the web tier. For information on web-tier configuration, please see my article Annotation-based MVC in Spring 2.5.

To set the proper context, our goal is to get rid of manual namings and wirings in the configuration files, and the basic strategy is convention-over-configuration: name the various dependencies according to the property names so that Spring can figure out how to autowire your app.

<aside>Incidentally, it is a separate and interesting discussion as to whether autowiring is something that should be done in the first place. One might argue that wiring is a configuration issue, and that adding annotations everywhere amounts to distributing the configuration across the various classes, thereby violating the principle of separation of concerns. Not to mention the fact that we don’t normally want to have to rebuild the app to make a config change. I can see that in some cases it makes sense to separate concerns (for example, I think modeling and persistence should generally be kept separate) and in other cases it doesn’t (for example, code documentation should not be separated out). Anyway I won’t argue here whether autowiring is good; I’ll just explain how to do it.</aside>

Here I assume that you’re using Spring MVC and Hibernate. I’m also assuming that you already have separate beans configuration files for the web, service, and data access tiers, that they’re currently not autowired (i.e. you want to migrate from a manual wiring to an autowired configuration). Nothing essential hinges upon these assumptions—they just reflect my own configuration—and the instructions should be useful even if your configuration is somewhat different.

Here we go.

Add annotations to name your DAO, service and controller classes

Chances are good that your DAO and service classes don’t have the names that name-based autowiring would require. For example, you might define interfaces that have the “right” names, such as a CategoryDao interface, or a ProductService interface, and then your concrete implementation classes would be HibernateCategoryDao or ProductServiceImpl (or whatever), which won’t autowire to the desired properties unless you have some strange and ill-advised property names. So our first order of business is to provide the requisite names. With manual wiring, you provide this on the <bean> element using the id or name attributes. But we’re trying to eliminate said elements from our config, so that option is unavailable. We use annotations instead.

For each DAO, add a class-level @Repository annotation.

These annotations go on the implementation class, not on the interface.

import org.springframework.stereotype.Repository;
 
@Repository("productDao")
public final class ProductDaoImpl extends AbstractHibernateDao
  implements ProductDao {

    ...
}

The @Repository annotation works in Spring 2.0 as well.

For each service component, add a class-level @Service annotation.

As before, these go on the implementation class, not on the interface.

import org.springframework.stereotype.Service;
 
@Service("productService")
public final class ProductServiceImpl implements ProductService {
    ...
}

Note that @Service is new with Spring 2.5. So it won’t work if you’re using Spring 2.0 or earlier.

For each Spring MVC controller, add a class-level @Controller annotation.

Don’t worry about providing a name; we won’t need it. (So this step is a little out of place in these instructions, but go ahead and do it anyway as you will need the @Controller annotation if you want to fully autowire the web tier.) Just do this for each controller:

import org.springframework.stereotype.Controller;
 
@Controller
public final class ProductController {
    ...
}

Like the @Service annotation, @Controller is new with Spring 2.5.

At this point we’ve haven’t really changed anything; you still have names defined in the manual configurations in addition to the annotation-based names you’ve just added. Test out your app to make sure we haven’t broken anything, and then move on to the next step.

Tell Spring about your annotation-based names and autowiring strategy

We have to tell Spring two things: where to find the names, and what strategy to use (viz., autowiring by name) for autowiring.

Tell Spring where to find the annotation-based names

You will need to use the <context:component-scan> element to do this. Since it’s in the context namespace, you must declare that namespace and schema location in your Spring config files. Here are examples from the data access, service, and web configurations:

<!-- Spring configuration for data access tier -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
    default-autowire="byName">
    
    <context:component-scan base-package="x.y.dao">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
    
    ...
</beans>
    
<!-- Spring configuration for service tier -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
    default-autowire="byName">
    
    <context:component-scan base-package="x.y.service">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Service"/>
    </context:component-scan>

    ...
</beans>
    
<!-- Spring configuration for web tier -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
    default-autowire="byName">
    
    <context:component-scan base-package="x.y.web">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    ...
</beans>

(You’ll of course need to provide the proper value for the base package.)

In the above, the pieces relevant to component names are the xmlns:context declaration, the xsi:schemaLocation declaration, and the <context:component-scan> configuration. I’m telling Spring to scan the x.y.dao package for components that have the @Repository annotation, to scan the x.y.service package for components that have the @Service annotation, and to scan x.y.web for components that have the @Controller annotation. Spring will do that, and in the case of the data access and service components, it will use the value we provided to the annotations in step 1 above as a basis for name-based autowiring. (We didn’t provide any names with the @Controller annotation though I would assume you can do that. It happens that in our case we won’t need it.)

You’ll notice however that I’ve snuck in some default-autowire=“byName” attributes in there. As you can guess, that’s for autowiring, and we’ll look at that in just a second.

Make sure you have public setters for each bean that you want autowired

It appears that default-autowire=“byName” autowiring is based on public setters. (With the @Autowired attribute applied to fields you can avoid the setters, but if you have unit tests, you’ll need setters to inject mocks, and so you may as well just keep the setters and make them public. Weaker visibility doesn’t appear to work.)

Tell Spring that you want to use name-based autowiring

The default-autowire=“byName” attribute tells Spring that you want to use component names to automatically wire up the app. In step 1 above we went through the process of providing correct names (“correct” here means names that match the injection property names) for DAO and service classes. That will allow us to remove the DAO and service components from our Spring config. In all likelihood there will be other components that you can’t remove from the config, such as a Hibernate SessionFactory in your data access configuration or a TransactionManager in your services configuration. In cases like that, just make sure that the names (or IDs) that you give to the components are the ones that would be expected when doing name-based injection. Here are some examples:

<!-- Hibernate session factory -->
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    ...
</bean>
 
<!-- Hibernate transaction manager -->
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

Note that using default-autowire=“byName” allows you to avoid having to use the @Autowired attribute on your various dependency injection fields or setters. You shouldn’t have to use that at all if you follow these instructions.

At this point you should be able to remove the following configurations:

  • The DAO component definitions from your Spring data access configuration file (but don’t remove the SessionFactory).
  • The service component definitions from your Spring service configuration file (but don’t remove the TransactionManager).
  • The service injections from your MVC controllers in your web configuration file. Don’t remove the controllers themselves for now; you still need those for defining URL mappings. Just remove the service injections since those will now be autowired. (Note that there’s a way to eliminate the MVC controller configurations as well; see Annotation-based MVC in Spring 2.5 for details.)
  • The sessionFactory injection from your TransactionManager definition since that will be autowired, the dataSource injection from your SessionFactory (assuming you have an appropriate ID on the DataSource config), etc. In general just look through your configs for elements that will now be autowired and either comment them out or remove them.

I would recommend commenting things out first and then testing the app to make sure it still works.

Congratulations!

Your app should now be autowired.

Post migrated from my Wheeler Software site.

Comments