3.9 Annotation-based container configuration
Is annotation better than XML in configuring Spring?
The introduction of annotation-based configuration raises the question of whether this approach is better than XML-based configuration. The short answer depends on the situation. The longer answer is that each approach has its advantages and disadvantages, and it is usually up to the developer to decide which strategy is better for them. Because of the way annotations are defined, annotations provide many contexts in their declarations, resulting in shorter and simpler configurations. However, XML is good at connecting components without having to touch source code or recompile them. Some developers prefer to approach source code, while others believe that annotation-based classes are no longer POJOs, and that configuration becomes decentralized and more difficult to control.
Whatever the choice is, Spring can accommodate both styles and even mix them together. It is worth noting that through its Java configuration options, Spring allows annotations to be used in a non-intrusive manner, without touching the source code of the target component and those tools. All configuration styles are supported by the Spring Toolkit.
Annotation-based configuration provides an alternative to XML settings, relying on bytecode metadata to connect components rather than declaring them in angle brackets. Instead of using XML to describe bean connections, developers move configurations inside the component class itself by using annotations in related class, method, or field declarations. As mentioned in the section "Example: The Required Annotation Bean Post Processor", using BeanPost Processor in conjunction with annotations is a common way to extend Spring IoC containers. For example, Spring 2.0 introduces the possibility of @Required annotations to perform required properties. Spring 2.5 makes it possible to drive Spring's dependency injection in the same general way. Essentially, @Autowire provides the same capabilities as described in section 3.4.5. "Autowiring collaborators" have more fine-grained control and wider application. Spring 2.5 also adds support for JSR-250 annotations, such as @PostConstruct and @PreDestroy
. Spring 3.0 adds support for JSR-330, annotations included in the javax.inject package (Java dependency injection), such as @Inject and @Named. Details of these annotations can be found in the relevant sections.
Annotation injection takes place before XML injection, so the configuration of the latter for attributes assembled in two ways overrides the former.
As before, you can register them as separate bean definitions, but you can also implicitly register them by including the following tags in an XML-based Spring configuration (injection containing context namespace):
<?xml version="1.0" encoding="UTF-8"?> <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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
(Implicit registered post processors include Autowired Annotation Bean Post Processor, Common Annotation Bean Post Processor, Persistence Annotation Bean Post Processor and the previously mentioned Reed Annotation Bean Post Processor.)
<context: annotation-config/> only beans that look for annotations in the same application context that defines it. This means that if you place <context: annotation-config/> in a Web Application Context serving Dispatcher Servlet, it can only find @Autowired annotated beans in your controller, not in your service layer. For more information, see section 18.2, "The Dispatcher Servlet".
3.9.1 @Required
@ The Required annotation is applied to the setter method of bean attributes as follows:
public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
This annotation simply indicates that the affected bean properties must be populated by explicit bean definitions or automatic assembly at configuration time. If the affected bean attribute is not populated, the container throws an exception, which allows early and explicit failure to avoid NullPointerExceptions or similar situations later. It is still recommended that you add assertions to the bean class itself, for example, to initialization methods. Doing so forces these required references and values, even when you use the class outside the container.
3.9.2 @Autowired
In the following example, JSR 330's @Inject annotation can be used to replace Spring's @Autowire annotation.
You can apply the @Autowire annotation to constructors.
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
Starting with Spring Framework 4.3, if the target bena defines only one constructor, then the constructor for the @Autowire annotation is no longer necessary. If some constructors are available, at least one must be annotated to tell the container which one to use.
As expected, you can also apply the @Autowire annotation to the "traditional" setter method:
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
You can also apply annotations to methods with any name and/or parameters:
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
You can also apply @Autowire to fields, or even mix it with constructors:
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired private MovieCatalog movieCatalog; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
By adding @Autowired annotations to fields or methods with arrays, you can also provide a set of specific types of bean s from the Application Context:
public class MovieRecommender { @Autowired private MovieCatalog[] movieCatalogs; // ... }
It can also be applied to collections of the same type:
public class MovieRecommender { private Set<MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... }
If you want the items in an array or list to be sorted in the specified order, your bean can implement the org. spring framework. core. Ordered interface, or use the @Order or standard @Priority annotation.
As long as the expected key is String, typed Maps can be assembled automatically. The value of the Map will contain all expected types of beans, and the key will contain the corresponding bean name:
public class MovieRecommender { private Map<String, MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... }
By default, automatic assembly fails when no candidate beans are available; by default, annotation methods, constructors, and fields are considered to indicate the required dependencies. This behavior can also be changed in the following ways.
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required=false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
Each class has only one constructor that can be marked as necessary, but can annotate multiple non-essential constructors. In this case, each of these candidates is considered. Spring uses the greediest constructor, which relies on the most satisfying constructor, with the largest number of parameters.
It is recommended that the required feature of @Autowire be used on the @Required annotation. The required feature indicates that automatic assembly of this property is unnecessary, and if this property cannot be automatically assembled, it will be ignored. On the other hand, @Required is more powerful in that it forces this property to be set by bean s supported by any container. If there is no value injection, the corresponding exception is thrown.
You can also use @Autowired: BeanFactory, Application Context, Environment, Resource Loader, Application Event Publisher and Message Source for known interfaces with resolvable dependencies. These interfaces and their extended interfaces, such as Configurable Application Context or Resource PatternResolver, can be parsed automatically without special settings.
public class MovieRecommender { @Autowired private ApplicationContext context; public MovieRecommender() { } // ... }
@ Autowired, @Inject, @Resource and @Value annotations are processed through Spring BeanPostProcessor, which in turn means that you cannot apply these annotations (if any) to your own BeanPostProcessor or BeanFactoryPostProcessor. These types must be wired up explicitly through XML or using Spring's @Bean method.
3.9.3 Use @Primary to fine-tune annotation-based automatic assembly
Because automatic assembly according to type may lead to multiple candidate targets, it is often necessary to have more control in the selection process. One way is through Spring's @Primary annotation. When there are multiple candidate beans to assemble into a single-valued dependency, @Primary indicates that the specified beans should have a higher priority. If you determine that a'primary'bean is in the middle of the candidate target, it will be the value of the automatic assembly.
Assuming we have the following configuration, first Movie Catalog is defined as the main Movie Catalog.
@Configuration public class MovieConfiguration { @Bean @Primary public MovieCatalog firstMovieCatalog() { ... } @Bean public MovieCatalog secondMovieCatalog() { ... } // ... }
Based on this configuration, the following Movie Recommender will automatically assemble with first Movie Catalog.
public class MovieRecommender { @Autowired private MovieCatalog movieCatalog; // ... }
The corresponding bean s are defined as follows:
<?xml version="1.0" encoding="UTF-8"?> <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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog" primary="true"> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <!-- inject any dependencies required by this bean --> </bean> <bean id="movieRecommender" class="example.MovieRecommender"/> </beans>
3.9.4 Fine Tuning Annotation-based Automatic Assembly with Qualifiers
Primary is an effective way to assemble automatically by type when multiple instances need to identify a major candidate. Spring's @Qualifier annotation can be used when more control is needed in the selection process. To select a specific bean for each, you can associate the value of the qualifier with a specific parameter, reducing the set of type matches. In the simplest case, this is a purely descriptive value:
public class MovieRecommender { @Autowired @Qualifier("main") private MovieCatalog movieCatalog; // ... }
@ The Qualifier annotation can also specify a single constructor parameter or method parameter:
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("main")MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
The corresponding bean is defined as follows. Beans with a qualifier value of "main" are assembled into constructor parameters with the same value.
<?xml version="1.0" encoding="UTF-8"?> <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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier value="main"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier value="action"/> <!-- inject any dependencies required by this bean --> </bean> <bean id="movieRecommender" class="example.MovieRecommender"/> </beans>
For fallback matches, the bean name is considered the default qualifier value. So you can define a bean with an ID of main instead of an embedded qualifier element, which will have the same matching result. However, although you can use this Convention to refer to specific beans by name, @Autowire essentially uses optional semantic qualifiers for type-driven injection. This means that qualifier values, even if returned to bean names, always narrow the set of semantic type matches; they do not semantically express a reference as a unique bean ID. A good qualifier value is "main" or "EMEA" or "persistent", which expresses the nature of a particular component that is independent of the bean id, even if an anonymous bean like this bean in the previous example generates an ID automatically.
As discussed earlier, qualifiers can also be applied to type combinations, such as Set < MovieCatalog>. In this example, all beans matched by declared qualifiers are injected as a collection. This means that qualifiers don't have to be unique; they just constitute filtering criteria. For example, you can define multiple Movie Catalogs with the same qualifier value "action", all of which will be injected into Set < Movie Catalog > with the annotation @Qualifier("action").
If you want to express annotation-driven injection by name, don't use @Autowired primarily, although technically you can refer to a bean name by @Qualifier value. As an alternative product, you can use the JSR-250@Resource annotation, which is semantically defined as identifying a specific target component by its unique name, and the type of declaration is independent of the matching process. @ Autowire has different semantics: by selecting candidate beans by type, the value of a particular String qualifier is considered to be only in the candidate target of type selection, for example, to match an account qualifier in beans marked with the same qualifier label.
For beans that are themselves defined in a collection/mapping or array type, @Resource is a good solution for specific collections or array beans that are distinguished by a unique name. That is to say, since Spring 4.3, set/mapping and array types can also be matched by Spring's @Autowire type matching algorithm, as long as element type information is retained in @bean, return type signature or set inheritance system. In this case, the qualifier value can be used to select from the same type of set, as summarized in the previous paragraph.
Since Spring 4.3, @Autowire has also considered self-reference injection, for example, the reference returns the beans currently injected. Note that self-injection is standby; general dependencies on other components always take precedence. In this sense, self-references do not participate in the general selection of candidates, so they are not particularly important; on the contrary, they are always the lowest priority in the end. In practice, self-reference is only used as a last resort, for example, to invoke other methods of the same instance through a bean's transaction broker: to separate scenarios of proxy beans in consideration of extracting affected methods. Or, using @Resource with its unique name, you might get a proxy that returns the current bean.
@ Autowired can be applied to fields, constructors and multi-parameter methods, allowing candidate targets to be reduced at the parameter level through qualifier annotations. In contrast, @Resource only supports setter methods with a single parameter for field and bean attributes. Therefore, if your injection goal is a constructor or a multi-parameter method, stick to the qualifier.
You can create your own custom qualifier annotations. Simply define an annotation and provide @Qualifier annotation in your own definition:
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Genre { String value(); }
Then you can provide custom qualifiers on fields and parameters of automatic assembly:
public class MovieRecommender { @Autowired @Genre("Action") private MovieCatalog actionCatalog; private MovieCatalog comedyCatalog; @Autowired public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) { this.comedyCatalog = comedyCatalog; } // ... }
Next, provide information about the definition of candidate beans. You can add the <qualifier/> tag as a child of the <bean/> tag, and then specify the type and value that matches your custom qualifier annotations. Type is used to match the fully qualified class name of the annotation. Or, if there is no risk of name conflict, you can use abbreviated class names for convenience. The following examples illustrate these methods.
<?xml version="1.0" encoding="UTF-8"?> <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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="Genre" value="Action"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="example.Genre" value="Comedy"/> <!-- inject any dependencies required by this bean --> </bean> <bean id="movieRecommender" class="example.MovieRecommender"/> </beans>
In section 3.10, "Class Path Scan and Management Components", you will see an annotation-based alternative to providing qualifier metadata in XML. In particular, see section 3.10.8, "Providing qualifier metadata with annotations".
In some cases, annotations without values are sufficient. This is very useful when annotations are for general purposes and can be applied across several different types of dependencies. For example, when the network is unavailable, you can provide an offline directory to search for. First, define a simple annotation:
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Offline { }
Then add annotations to fields or attributes to be automatically assembled:
public class MovieRecommender { @Autowired @Offline private MovieCatalog offlineCatalog; // ... }
Now the bean definition requires only one qualifier type:
<bean class="example.SimpleMovieCatalog"> <qualifier type="Offline"/> <!-- inject any dependencies required by this bean --> </bean>
You can also define custom qualifier annotations that receive additional named attributes or replace simple value attributes. If the field or parameter to be injected specifies multiple attribute values, the bean definition must match all attribute values before it can be considered as a candidate for automatic assembly. As an example, consider the following annotated definitions:
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface MovieQualifier { String genre(); Format format(); }
In this case, Format is an enumeration type:
public enum Format { VHS, DVD, BLURAY }
The fields to be assembled automatically are annotated with custom qualifiers and contain two attribute values: genre and format.
public class MovieRecommender { @Autowired @MovieQualifier(format=Format.VHS, genre="Action") private MovieCatalog actionVhsCatalog; @Autowired @MovieQualifier(format=Format.VHS, genre="Comedy") private MovieCatalog comedyVhsCatalog; @Autowired @MovieQualifier(format=Format.DVD, genre="Action") private MovieCatalog actionDvdCatalog; @Autowired @MovieQualifier(format=Format.BLURAY, genre="Comedy") private MovieCatalog comedyBluRayCatalog; // ... }
Finally, the bean definition should contain matching qualifier values. This example also demonstrates that bean meta-attributes can be used instead of < qualifier/> sub-elements. If <qualifier/> is available, it and its attributes have higher priority. If there is no qualifier at present, the automatic assembly mechanism will use the values in <meta/> as backup, as defined in the last two beans in the following example.
<?xml version="1.0" encoding="UTF-8"?> <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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute key="format" value="VHS"/> <attribute key="genre" value="Action"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute key="format" value="VHS"/> <attribute key="genre" value="Comedy"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <meta key="format" value="DVD"/> <meta key="genre" value="Action"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <meta key="format" value="BLURAY"/> <meta key="genre" value="Comedy"/> <!-- inject any dependencies required by this bean --> </bean> </beans>
3.9.5 Use generics as automatic assembly qualifiers
In addition to the @Qualifier annotation, you can also use Java generic types as a hint for qualifiers. For example, suppose you have the following configuration:
@Configuration public class MyConfiguration { @Bean public StringStore stringStore() { return new StringStore(); } @Bean public IntegerStore integerStore() { return new IntegerStore(); } }
Assuming that the beans above implement a generic interface, such as Store < String > and Store < Integer >, you can use the @Autowire Store interface, which will be used as a qualifier:
@Autowired private Store<String> s1; // <String> qualifier, injects the stringStore bean @Autowired private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
Generic qualifiers are also applied when automatically assembling Lists, Maps, and Arrays:
// Inject all Store beans as long as they have an <Integer> generic // Store<String> beans will not appear in this list @Autowired private List<Store<Integer>> s;
3.9.6 CustomAutowireConfigurer
Custom Autowire Configurer is a BeanFactoryPostProcessor that allows you to register your own custom qualifier annotation types, even if they do not use Spring's @Qualifier annotation.
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"> <property name="customQualifierTypes"> <set> <value>example.CustomQualifier</value> </set> </property> </bean>
Autowire Candidate Resolver determines candidate targets for automatic assembly in the following ways:
autowire-candidate defined by each bean
Any default-autowire-candidates mode available in the <beans/> element
There are @Qualifier annotations and any custom annotations registered in Custom Autowire Configurer
When multiple beans qualify as candidates for automatic assembly, the decision of the "primary" bean is as follows: if the primary feature in a given bean in the candidate target is set to true, it will be selected as the target bean.
3.9.7 @Resource
Spring also supports injection of field or bean attribute setter methods using JSR-250@Resource. This is a common pattern in Java EE 5 and 6, such as JSF 1.2 managed beans or JAX-WS 2.0 endpoints. Spring also supports this pattern for objects it manages.
@ Resource takes the name attribute, and Spring takes the name value as the name of the bean to be injected by default. In other words, it follows by-name semantics, as demonstrated by the following examples:
public class SimpleMovieLister { private MovieFinder movieFinder; @Resource(name="myMovieFinder") public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
If there is no explicitly specified name, the default name is obtained from the field name or setter method. In the case of a field, it takes the field name; in the case of the setter method, it takes the attribute name of the bean. So the following example injects a bean named movieFinder into its setter method:
public class SimpleMovieLister { private MovieFinder movieFinder; @Resource public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
The name provided by the annotation is resolved to the bean name by the Application Context perceived by the Common Annotation Bean PostProcessor. If you explicitly configure Spring's Simple JndiBeanFactory, the name will be resolved through JNDI. However, it is recommended that you rely on default behavior and simply use Spring's JNDI lookup function to protect the indirect lookup level.
In @Resource-specific cases where no explicit name is specified, similar to @Autowired, @Resource performs major matching types to replace the named bean and resolves known resolvable dependencies: BeanFactory, Application Context, ResourceLoader, Application Event Publisher and MessageSource interfaces.
So in the following example, the customerPreferenceDao field first looks for a bean named customerPreferenceDao, and then falls back to the type matching of the main type CustomerPreferenceDao. The context field is injected based on the known resolvable dependency type ApplicationContext.
public class MovieRecommender { @Resource private CustomerPreferenceDao customerPreferenceDao; @Resource private ApplicationContext context; public MovieRecommender() { } // ... }
3.9.8 @PostConstruct and @PreDestroy
Common Annotation Bean Post Processor recognizes not only @Resource annotations, but also JSR-250 life cycle annotations. Support for these annotations was introduced in Spring 2.5, and an alternative way to initialize and destroy the annotations described in the callback function was provided. Assuming that Common Annotation Bean Post Processor is registered in Spring's Application Context, the method executing these annotations is called at the same point in the life cycle as the corresponding Spring lifecycle interface method or explicitly declared callback method. In the following example, the cache is pre-placed close to initialization and cleaned up before destruction.
public class CachingMovieLister { @PostConstruct public void populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache() { // clears the movie cache upon destruction... } }