Application Configuration with Spring and Java Annotations
Posted by Helena Edelson on 25th July 2009
There is an ongoing debate about best practices for configuration management between XML with schema support versus Annotational meta data. I can make a valid argument either way, and I think the choice should be made on a case by case basis. But, is XML configuration so bad? I like using annotational configuration for things like Spring Web Services and Spring @MVC, where the configuration is specific to the method and arguments – where if you modify code you are likely to modify configuration. I also schema config with namespace support for cases where having a central area of configuration, easily maintainable, and keeping all code clean of meta data is preferred. One easy way to clean up necessary XML config is to use namespaces.
“Namespaces dramatically improve the Spring XML landscape
• More expressive, less verbose
• Just ask Spring Security where it’s 200+ lines of config went!”
- Chris Beams, Project Lead, Spring JavaConfig
Also with namespaces, in some cases the best practices configuration is already done for you and you can easily leverage convention over configuration to also reduce verbosity.
Item two in the list above is absolutely true. When you upgrade from Acegi Security to Spring Security, the configuration volume is night and day, and very cool. Perhaps they realized that if you required users to configure so many framework objects for one application layer such as security or messaging, you haven’t done a service to your users. They simply automatically register what amounts to best practices, letting the user override and add to the security context alternatively, as needed. Now that’s helpful.
So what about annotations? I am currently on large-scale enterprise migration project. Originally we made the decision to go with XML because the team was learning Java and Flex and we didn’t want to add Annotations to the mix for them. As the migrated application’s codebase is growing with domain logic, and in parallel its config files for business and framework services, I am moving toward Annotations at least in my own committed code. At first it was a, “Let’s not pollute and add dependencies in the code..” issue, and now it is a, “Component scanning, very cool.. automated registration … no config files to maintain and refactor” issue.
I am blogging not writing a thesis, so yes, there are more valid arguments which I leave for others, lets get to it and look at code. The main topics I’ll cover briefly are implementing JSR-250 Support, Spring’s @Autowired, Using Qualifiers, Component Scanning.
JSR-250 Support
Three of the total JSR-250 annotations defined in Java EE 5 which are out of the box in Java SE 6 are @Resource, @PostConstruct, and @PostDestroy. There are more in common-annotations.jar.
Enabling Spring’s JSR-250 support
This is as simple as implementing one of these two options in your application’s context.xml:
1. Old School
<bean class=“org.springframework.context.annotation.CommonAnnotationBeanPostProcessor”/>
2. Namespace, which offers greater functionalty than the above option:
<context:annotation-config/>
@Resource
You can implicitly are explicitly name your resource. This is implicit naming by property:
@Resource
private DataSource securityDataSource;
@Resource
public void setSecurityDataSource(DataSource sds) {
this.primaryDataSource = sds;
}
Alternatively, you can name by explicit meta data:
@Resource(name=“securityDataSource”)
private DataSource dataSource;
@Resource(name=“securityDataSource”)
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
And if needed, you can disable type-matching fallback as well:
<bean class=“org.springframework.context.annotation.CommonAnnotationBeanPostProcessor”>
<property name=“fallbackToDefaultTypeMatch” value=“false”/>
</bean>
Spring
Lifecycle Annotations: @PostConstruct and @PreDestroy
In some of my framework services I use 2 of Spring’s Interfaces: InitializingBean, DisposableBean and their respective methods: afterPropertiesSet(), destroy(). Another option to implementing these non-programmatically is this method which I think makes the code much cleaner and less verbose:
@PostConstruct public void initialize() { // on post setter injection }
@PreDestroy public void doShutdown() { // on close context }
Yet another option is if you have a need for creating custom initialize and destroy annotations you can configure them like so:
<bean class=“org.springframework.context.annotation.CommonAnnotationBeanPostProcessor”>
<property name=“initAnnotationType” value=“com.edelsonmedia.infrastructure.annotations.Initialize”/>
<property name=“destroyAnnotationType” value=“com.edelsonmedia.infrastructure.annotations..Destroy”/>
</bean>
which is cool because I like writing annotations for increased customization.
@Autowired
@Autowired is a dependency resolver acting on type which acts on fields, methods, and constructors. There are no method naming convention restrictions and multiple parameters are accepted.
In Spring 2.5, using the context namespace with <context:annotation-config/> automatically enables the @Autowired annotation.
Field injection:
@Autowired private DataSource dataSource;
Setter Injection:
@Autowired public void setDataSource(DataSource dataSource){...}
Constructor Injection:
@Autowired public ObjectRepository(DataSource dataSource){...}
Method Injection:
@Autowired public doSetup(DataSource dataSource, Company company) {...}
Attributes for further configuration:
This distinguishes an optional property but note that if there are more than one matches found this will fail:
@Autowired(required=false)
private SomeObject obj;
This configures a primary candidate from the other types:
<bean id=“dataSource” primary=“true” class=“org.apache.commons.dbcp.BasicDataSource” … />
<bean id=“backupDataSource” class=“org.apache.commons.dbcp.BasicDataSource” … />
@Qualifier
The @Qualifier annotation is scoped for field, constructor arg and method parameters. This annotation offers named matching functionality to the @Autowired annotation, finer granularity for autowire candidate resolution, and an extension point for custom autowiring qualifiers, which I will show below:
Field Injection
@Autowired
@Qualifier(“primaryDataSource”)
private DataSource dataSource;
Setter Injection
@Autowired
public void setDataSource(@Qualifier(“securityDataSource”)
DataSource dataSource) {
this.dataSource = dataSource;
}
Constructor Injection
@Autowired
public void setup(@Qualifier(“securityDataSource”)
DataSource dataSource, SomeObject obj) {
this.dataSource = dataSource;
this.obj = obj;
}
Multiple Parameter Method Injection
@Autowired
public AbstractedRepository(@Qualifier(“securityDataSource”)
DataSource dataSource) {
this.dataSource = dataSource;
}
@Qualifier As Meta-Annotation for Extended or Custom Qualifiers
Define it:
@Qualifier
public @interface VMLoaded { … }
Now use the annotation with @Autowired:
@Autowired
@VMLoaded /* If the annotation provides meaning as is, no value necessary */
private BootrapManager bootstrapManager;
If you want to register a custom annotation without using @Qualifier as meta-annotation:
<bean class=“org.springframework.beans.factory.annotation.CustomAutowireConfigurer”>
<property name=“customQualifierTypes”>
<set>
<value>org.example.Online</value>
<value>org.example.Offline</value>
</set>
</property>
</bean>
You can define attributes with custom qualifiers A value attribute can match against a bean name just like it does for @Qualifier
@Qualifier
public @interface CompanyCatalog {
String company();
int type();
...etc
}
Attributes can resolve against XML metadata or class-level annotation metadata
@Category(company=“someCompany”, type=“principle”)
public class CompanyCatalog implements Catalog {
//… etc
}
Stereotype Annotations
@Component – a generic component
@Repository – a repository (DAO)
@Service – a stateless service of idempotent operations
@Controller – an MVC controller
All auto-detected components are implicitly named from the non-qualified class name. This:
@Controller
public class AController { … }
is equivalent to:
<bean id=“aController” class=“org.foo.web.controller.AController”/>
You can set the generated name explicitly, where this:
@Controller(“aCatalog”)
public class AController { … }
is equivalent to:
<bean id=“aCatalog”
class=“org.foo.web.controller.AController”/>
So that whole services-config.xml file you may have that grows and grows now can be migrated to annotations in your classes, and you can delete the config file.
Component Scanning
In Spring 2.5, they included a new class, ClassPathBeanDefinitionScanner, which accepts packages passed in as arguments, and detects any class with declared stereotypes in the path while scanning the base package and its sub-packages. Using component scanning is easy. Simply add the context namespace to your main schema config and add:
<context:component-scan base-package=“org.foo”/>
Now you are set to customize the component scanner if needed. Using the @Component stereotype declared on a custom annotation, you can then decorate any class with that annotation simply by the above configuration addition and:
@Component
public @interface MyAnnotation{ ... }
@MyAnnotation
public class MyClass { ... }
But what if you need to filter the package scanning? Below demonstrates how to include components with custom filters, and the assignable, aspectj and regex filters:
<context:component-scan base-package=“org.foo”>
<!-- custom filter -->
<context:include-filter type=“annotation” expression=“foo.Bar”/>
<context:include-filter type=“assignable” expression=“foo.Baz”/>
<context:include-filter type=“aspectj” expression=“foo..*Service”/>
<context:include-filter type=“regex” expression=“foo\.B[a-z]+”/>
</context:component-scan>
For further customization, you can disable default filters or stereotypes and exclude filters
<context:component-scan base-package=“org.example.web” use-default-filters=“false”>
<context:include-filter type=“annotation” expression=“foo.Bar”/>
<context:include-filter type=“aspectj” expression=“foo..*Service”/>
<context:exclude-filter type=“assignable” expression=“foo.Bad”/>
</context:component-scan>
Scoping Components
As with bean definition in xml, the default scope is singleton. To provide any other scope, add the @Scope annotation:
@Controller
@Scope(“prototype”)
public class MyController { … }
@MyController
@Scope(“session”)
public class SomeWebComponent { … }
Best Practices
So in conclusion, you can you annotations and XML configuration together and leverage the best of both within one application context. Here are some thoughts from Juergen Hoeller, a Principle engineer at SpringSource, on the topic:
Annotation metadata is in the code
- Pro: facilitates refactoring
- Con: forces recompilation
XML externalizes the configuration
- Pro: configuration is not scattered
- Con: XML is verbose
Find out more: JSR_250 spec
Posted in Annotations, Application Cofiguration, Configuration Management, Flex, JMS, Java, Software Development, Spring | No Comments »
