Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.49 MB, 753 trang )
9799ch03.qxd
5/5/08
4:50 PM
Page 65
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
How It Works
To ask Spring to auto-wire the bean properties with @Autowired or @Resource, you have to register an AutowiredAnnotationBeanPostProcessor instance in the IoC container. If you are using
a bean factory, you have to register this bean post processor through the API. Otherwise, you
can just declare an instance of it in your application context.
Or you can simply include the
registered.
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">
...
Auto-Wiring a Single Bean of Compatible Type
The @Autowired annotation can be applied to a particular property for Spring to auto-wire it.
As an example, you can annotate the setter method of the prefixGenerator property with
@Autowired. Then Spring will attempt to wire a bean whose type is compatible with
PrefixGenerator.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired
public void setPrefixGenerator(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
If you have a bean whose type is compatible with PrefixGenerator defined in the IoC container, it will be set to the prefixGenerator property automatically.
65
9799ch03.qxd
66
5/5/08
4:50 PM
Page 66
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
...
class="com.apress.springrecipes.sequence.SequenceGenerator">
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
By default, all the properties with @Autowired are required. When Spring can’t find a
matching bean to wire, it will throw an exception. If you would like a certain property to be
optional, set the required attribute of @Autowired to false. Then when Spring can’t find a
matching bean, it will leave this property unset.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired(required = false)
public void setPrefixGenerator(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
In addition to the setter method, the @Autowired annotation can also be applied to a constructor. Then Spring will attempt to find a bean with the compatible type for each of the
constructor arguments.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired
public SequenceGenerator(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
The @Autowired annotation can also be applied to a field, even if it is not declared as
public. In this way, you can omit the need of declaring a setter method or a constructor for
9799ch03.qxd
5/5/08
4:50 PM
Page 67
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
this field. Spring will inject the matched bean into this field via reflection. However, annotating a non-public field with @Autowired will reduce code testability since the code will be
difficult to unit test.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
private PrefixGenerator prefixGenerator;
...
}
You may even apply the @Autowired annotation to a method with an arbitrary name and
an arbitrary number of arguments. Then Spring will attempt to wire a bean with the compatible type for each of the method arguments.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired
public void inject(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
Auto-Wiring All Beans of Compatible Type
The @Autowired annotation can also be applied to a property of array type to have Spring autowire all the matching beans. For example, you can annotate a PrefixGenerator[] property
with @Autowired. Then Spring will auto-wire all the beans whose type is compatible with
PrefixGenerator at one time.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
private PrefixGenerator[] prefixGenerators;
...
}
67
9799ch03.qxd
68
5/5/08
4:50 PM
Page 68
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
If you have multiple beans whose type is compatible with the PrefixGenerator defined in
the IoC container, they will be added to the prefixGenerators array automatically.
...
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
In a similar way, you can apply the @Autowired annotation to a type-safe collection. Spring
is able to read the type information of this collection and auto-wire all the beans whose type is
compatible.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
private List
...
}
If Spring notices that the @Autowired annotation is applied to a type-safe java.util.Map
with strings as the keys, it will add all the beans of the compatible type, with the bean names
as the keys, to this map.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
private Map
...
}
9799ch03.qxd
5/5/08
4:50 PM
Page 69
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
Auto-Wiring by Type with Qualifiers
By default, auto-wiring by type will not work when there is more than one bean with the compatible type in the IoC container. However, Spring allows you to specify a candidate bean by
providing its name in the @Qualifier annotation.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class SequenceGenerator {
@Autowired
@Qualifier("datePrefixGenerator")
private PrefixGenerator prefixGenerator;
...
}
Then Spring will attempt to find a bean with that name in the IoC container and wire it
into the property.
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
The @Qualifier annotation can also be applied to a method argument for auto-wiring.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class SequenceGenerator {
...
@Autowired
public void inject(
@Qualifier("datePrefixGenerator") PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
You can further create a custom qualifier annotation type for the auto-wiring purpose.
This annotation type must be annotated with @Qualifier itself.
package com.apress.springrecipes.sequence;
...
import org.springframework.beans.factory.annotation.Qualifier;
69
9799ch03.qxd
70
5/5/08
4:50 PM
Page 70
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER })
@Qualifier
public @interface Generator {
String value();
}
Then you can apply this annotation to an @Autowired bean property. It will ask Spring to
auto-wire the bean with this qualifier annotation and the specified value.
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
@Generator("prefix")
private PrefixGenerator prefixGenerator;
...
}
You have to provide this qualifier to the target bean that you want to be auto-wired into
the preceding property. The qualifier is added by the
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
Auto-Wiring by Name
If you would like to auto-wire bean properties by name, you can annotate a setter method, a
constructor, or a field with the JSR-250 @Resource annotation. By default, Spring will attempt
to find a bean with the same name as this property. But you can specify the bean name
explicitly in its name attribute.
■Note To use the JSR-250 annotations, you have to include common-annotations.jar (located in the
lib/j2ee directory of the Spring installation) in your classpath. However, if your application is running on
Java SE 6 or Java EE 5, you needn’t include this JAR file.
9799ch03.qxd
5/5/08
4:50 PM
Page 71
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
package com.apress.springrecipes.sequence;
import javax.annotation.Resource;
public class SequenceGenerator {
@Resource(name = "datePrefixGenerator")
private PrefixGenerator prefixGenerator;
...
}
3-9. Inheriting Bean Configuration
Problem
When configuring beans in the Spring IoC container, you may have more than one bean
sharing some common configurations, such as bean properties and attributes in the
element. You often have to repeat these configurations for multiple beans.
Solution
Spring allows you to extract the common bean configurations to form a parent bean. The
beans that inherit from this parent bean are called child beans. The child beans will inherit the
bean configurations, including bean properties and attributes in the
parent bean to avoid duplicate configurations. The child beans can also override the inherited
configurations when necessary.
The parent bean can act as a configuration template and also as a bean instance at the
same time. However, if you want the parent bean to act only as a template that cannot be
retrieved, you must set the abstract attribute to true, asking Spring not to instantiate this
bean.
You must note that not all attributes defined in the parent
parent. To find out more about which attributes will be inherited from the parent and which
won’t, please refer to the Spring documentation about bean inheritance.
How It Works
Suppose you need to add a new sequence generator instance whose initial value and suffix are
the same as the existing ones.
class="com.apress.springrecipes.sequence.SequenceGenerator">
71
9799ch03.qxd
72
5/5/08
4:50 PM
Page 72
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
class="com.apress.springrecipes.sequence.SequenceGenerator">
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
To avoid duplicating the same properties, you can declare a base sequence generator
bean with those properties set. Then the two sequence generators can inherit this base generator so that they also have those properties set automatically. You needn’t specify the class
attributes of the child beans if they are the same as the parent’s.
class="com.apress.springrecipes.sequence.SequenceGenerator">
...
The inherited properties can be overridden by the child beans. For example, you can add
a child sequence generator with a different initial value.
class="com.apress.springrecipes.sequence.SequenceGenerator">
...
9799ch03.qxd
5/5/08
4:50 PM
Page 73
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
The base sequence generator bean can now be retrieved as a bean instance to use. If you
want it to act as a template only, you have to set the abstract attribute to true. Then Spring
will not instantiate this bean.
class="com.apress.springrecipes.sequence.SequenceGenerator">
...
You can also omit the class of the parent bean and let the child beans specify their own,
especially when the parent bean and child beans are not in the same class hierarchy, but
share some properties of the same name. In this case, the parent bean’s abstract attribute
must be set to true, as the parent bean can’t be instantiated. For example, let’s add another
ReverseGenerator class that has an initial property also.
package com.apress.springrecipes.sequence;
public class ReverseGenerator {
private int initial;
public void setInitial(int initial) {
this.initial = initial;
}
}
Now SequenceGenerator and ReverseGenerator don’t extend the same base class—that is,
they’re not in the same class hierarchy, but they have a property of the same name: initial. To
extract this common initial property, you need a baseGenerator parent bean with no class
attribute defined.
class="com.apress.springrecipes.sequence.SequenceGenerator">
class="com.apress.springrecipes.sequence.ReverseGenerator" />
73
9799ch03.qxd
74
5/5/08
4:50 PM
Page 74
CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING
...
...
Figure 3-3 shows the object diagram for this generator bean hierarchy.
Figure 3-3. Object diagram for the generator bean hierarchy
3-10. Defining Collections for Bean Properties
Problem
Sometimes your bean properties may be of a collection type that contains multiple elements.
You would like to configure these collection properties in Spring’s bean configuration file
rather than by coding.
Solution
The Java Collections framework defines a set of interfaces, implementations, and algorithms
for different types of collections, such as lists, sets, and maps. Figure 3-4 shows a simplified
UML class diagram that may help you to understand the Java Collections framework better.
List, Set, and Map are the core interfaces representing three main types of collections. For
each collection type, Java provides several implementations with different functions and characteristics from which you can choose. In Spring, these types of collections can be easily
configured with a group of built-in XML tags, such as ,