1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

3-2. Instantiating the Spring IoC Container

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



46



5/5/08



4:50 PM



Page 46



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



The application context provides more advanced features than the bean factory while

keeping the basic features compatible. So, I strongly recommend using the application context for every application unless the resources of this application are restricted, such as when

running in an applet or a mobile device.

The interfaces for the bean factory and the application context are BeanFactory and

ApplicationContext, respectively. The interface ApplicationContext is a subinterface of

BeanFactory for maintaining compatibility.



How It Works

Instantiating a Bean Factory

To instantiate a bean factory, you have to load the bean configuration file into a Resource

object first. For example, the following statement loads your configuration file from the root of

the classpath:

Resource resource = new ClassPathResource("beans.xml");

Resource is only an interface, while ClassPathResource is one of its implementations for

loading a resource from the classpath. Other implementations of the Resource interface, such

as FileSystemResource, InputStreamResource, and UrlResource, are used to load a resource

from other locations. Figure 3-1 shows the common implementations of the Resource interface in Spring.



Figure 3-1. Common implementations of the Resource interface

Next, you can use the following statement to instantiate a bean factory by passing in a

Resource object with the configuration file loaded:

BeanFactory factory = new XmlBeanFactory(resource);

As mentioned, BeanFactory is only an interface for abstracting the operations of a bean

factory, while XmlBeanFactory is the implementation that builds a bean factory from an XML

configuration file.



9799ch03.qxd



5/5/08



4:50 PM



Page 47



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



Instantiating an Application Context

Like BeanFactory, ApplicationContext is an interface only. You have to instantiate an implementation of it. The ClassPathXmlApplicationContext implementation builds an application

context by loading an XML configuration file from the classpath. You can also specify multiple

configuration files for it.

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

Besides ClassPathXmlApplicationContext, there are several other ApplicationContext

implementations provided by Spring. FileSystemXmlApplicationContext is used to load

XML configuration files from the file system, while XmlWebApplicationContext and

XmlPortletApplicationContext can be used in web and portal applications only. Figure 3-2

shows the common implementations of the ApplicationContext interface in Spring.



Figure 3-2. Common implementations of the ApplicationContext interface



Getting Beans from the IoC Container

To get a declared bean from a bean factory or an application context, you just make a call to

the getBean() method and pass in the unique bean name. The return type of the getBean()

method is java.lang.Object, so you have to cast it to its actual type before using it.

SequenceGenerator generator =

(SequenceGenerator) context.getBean("sequenceGenerator");

Up to this step, you are free to use the bean just like any object you created using a constructor. The complete source code for running the sequence generator application is given in

the following Main class:

package com.apress.springrecipes.sequence;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;



47



9799ch03.qxd



48



5/5/08



4:50 PM



Page 48



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



public class Main {

public static void main(String[] args) {

ApplicationContext context =

new ClassPathXmlApplicationContext("beans.xml");

SequenceGenerator generator =

(SequenceGenerator) context.getBean("sequenceGenerator");

System.out.println(generator.getSequence());

System.out.println(generator.getSequence());

}

}

If everything is fine, you should see the following sequence numbers output, along with

some logging messages that you may not be interested in:

30100000A

30100001A



3-3. Resolving Constructor Ambiguity

Problem

When you specify one or more constructor arguments for a bean, Spring will attempt to find

an appropriate constructor in the bean class and pass in your arguments for bean instantiation. However, if your arguments can be applied to more than one constructor, it may cause

ambiguity in constructor matching. In this case, Spring may not be able to invoke your

expected constructor.



Solution

You can specify the attributes type and index for the element to assist

Spring in finding your expected constructor.



How It Works

Now let’s add a new constructor to the SequenceGenerator class with prefix and suffix as

arguments.

package com.apress.springrecipes.sequence;

public class SequenceGenerator {

...

public SequenceGenerator(String prefix, String suffix) {

this.prefix = prefix;

this.suffix = suffix;



9799ch03.qxd



5/5/08



4:50 PM



Page 49



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



}

}

In its bean declaration, you can specify one or more constructor arguments through the

elements. Spring will attempt to find an appropriate constructor for that

class and pass in your arguments for bean instantiation. Recall that there’s not a name attribute

in , as constructor arguments are position-based.


class="com.apress.springrecipes.sequence.SequenceGenerator">









It’s easy for Spring to find a constructor for these two arguments, as there is only one

constructor that requires two arguments. Suppose you have to add another constructor to

SequenceGenerator with prefix and initial as arguments.

package com.apress.springrecipes.sequence;

public class SequenceGenerator {

...

public SequenceGenerator(String prefix, String suffix) {

this.prefix = prefix;

this.suffix = suffix;

}

public SequenceGenerator(String prefix, int initial) {

this.prefix = prefix;

this.initial = initial;

}

}

To invoke this constructor, you make the following bean declaration to pass a prefix and

an initial value. The remaining suffix is injected through the setter method.


class="com.apress.springrecipes.sequence.SequenceGenerator">









However, if you run the application now, you will get the following result:

300A

301A



49



9799ch03.qxd



50



5/5/08



4:50 PM



Page 50



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



The cause of this unexpected result is that the first constructor, with prefix and suffix as

arguments, has been invoked, but not the second. This is because Spring resolved both of your

arguments as String type by default and considered that the first constructor was most suitable, as no type conversion was required. To specify the expected type of your arguments, you

have to set it in the type attribute in .


class="com.apress.springrecipes.sequence.SequenceGenerator">









Now add one more constructor to SequenceGenerator with initial and suffix as arguments, and modify your bean declaration for it accordingly.

package com.apress.springrecipes.sequence;

public class SequenceGenerator {

...

public SequenceGenerator(String prefix, String suffix) {

this.prefix = prefix;

this.suffix = suffix;

}

public SequenceGenerator(String prefix, int initial) {

this.prefix = prefix;

this.initial = initial;

}

public SequenceGenerator(int initial, String suffix) {

this.initial = initial;

this.suffix = suffix;

}

}


class="com.apress.springrecipes.sequence.SequenceGenerator">









If you run the application again, you may get the right result or the following unexpected

result:

30100000null

30100001null



9799ch03.qxd



5/5/08



4:50 PM



Page 51



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



The reason for this uncertainty is that Spring internally scores each constructor for

compatibility with your arguments. But during the scoring process, the order in which your

arguments appear in the XML is not considered. This means that from the view of Spring, the

second and the third constructors will get the same score. Which one to pick depends on

which one is matched first. According to the Java Reflection API, or more accurately the

Class.getDeclaredConstructors() method, the constructors returned will be in an arbitrary

order that may differ from the declaration order. All these factors, acting together, cause ambiguity in constructor matching.

To avoid this problem, you have to indicate the indexes of your arguments explicitly

through the index attribute of . With both the type and index attributes set,

Spring will be able to find the expected constructor for a bean accurately.


class="com.apress.springrecipes.sequence.SequenceGenerator">









However, if you are quite sure that your constructors won’t cause ambiguity, you can skip

the type and index attributes.



3-4. Specifying Bean References

Problem

The beans that make up your application often need to collaborate with each other to complete the application’s functions. For beans to access each other, you have to specify bean

references in the bean configuration file.



Solution

In the bean configuration file, you can specify a bean reference for a bean property or a constructor argument by the element. It’s as easy as specifying a simple value by the

element. You can also enclose a bean declaration in a property or a constructor argument

directly as an inner bean.



How It Works

Accepting a string value as the prefix of your sequence generator is not flexible enough to

adapt to future requirements. It would be better if the prefix generation could be customized

with some kind of programming logic. You can create the PrefixGenerator interface to define

the prefix generation operation.

package com.apress.springrecipes.sequence;

public interface PrefixGenerator {

public String getPrefix();

}



51



9799ch03.qxd



52



5/5/08



4:50 PM



Page 52



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



One prefix generation strategy is to use a particular pattern to format the current system

date. Let’s create the DatePrefixGenerator class that implements the PrefixGenerator

interface.

package com.apress.springrecipes.sequence;

...

public class DatePrefixGenerator implements PrefixGenerator {

private DateFormat formatter;

public void setPattern(String pattern) {

this.formatter = new SimpleDateFormat(pattern);

}

public String getPrefix() {

return formatter.format(new Date());

}

}

The pattern of this generator will be injected through the setter method setPattern() and

then used to create a java.text.DateFormat object to format the date. As the pattern string will

not be used any more once the DateFormat object is created, it’s not necessary to store it in a

private field.

Now you can declare a bean of type DatePrefixGenerator with an arbitrary pattern string

for date formatting.


class="com.apress.springrecipes.sequence.DatePrefixGenerator">





Specifying Bean References for Setter Methods

To apply this prefix generator approach, the SequenceGenerator class should accept an object

of type PrefixGenerator instead of a simple prefix string. You may choose setter injection to

accept this prefix generator. You have to delete the prefix property and its setter methods and

constructors that cause compile errors.

package com.apress.springrecipes.sequence;

public class SequenceGenerator {

...

private PrefixGenerator prefixGenerator;

public void setPrefixGenerator(PrefixGenerator prefixGenerator) {

this.prefixGenerator = prefixGenerator;

}

public synchronized String getSequence() {

StringBuffer buffer = new StringBuffer();



9799ch03.qxd



5/5/08



4:50 PM



Page 53



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



buffer.append(prefixGenerator.getPrefix());

buffer.append(initial + counter++);

buffer.append(suffix);

return buffer.toString();

}

}

Then a SequenceGenerator bean can refer to the datePrefixGenerator bean as its

prefixGenerator property by enclosing a element inside.


class="com.apress.springrecipes.sequence.SequenceGenerator">













The bean name in the element’s bean attribute can be a reference to any bean in the

IoC container, even if it’s not defined in the same XML configuration file. If you are referring to

a bean in the same XML file, you should use the local attribute, as it is an XML ID reference.

Your XML editor can help to validate whether a bean with that ID exists in the same XML file

(i.e., the reference integrity).


class="com.apress.springrecipes.sequence.SequenceGenerator">

...









There is also a convenient shortcut to specify a bean reference in the ref attribute of the

element.


class="com.apress.springrecipes.sequence.SequenceGenerator">

...





But in this way, your XML editor will not be able to validate the reference integrity. Actually, it has the same effect as specifying the element’s bean attribute.

Spring 2.x provides another convenient shortcut for you to specify bean references. It’s by

using the p schema to specify bean references as attributes of the element. This can

shorten the lines of XML configuration.


xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:p="http://www.springframework.org/schema/p"



53



9799ch03.qxd



54



5/5/08



4:50 PM



Page 54



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">


class="com.apress.springrecipes.sequence.SequenceGenerator"

p:suffix="A" p:initial="1000000"

p:prefixGenerator-ref="datePrefixGenerator" />



To distinguish a bean reference from a simple property value, you have to add the -ref

suffix to the property name.

Specifying Bean References for Constructor Arguments

Bean references can also be applied to constructor injection. For example, you can add a constructor that accepts a PrefixGenerator object as an argument.

package com.apress.springrecipes.sequence;

public class SequenceGenerator {

...

private PrefixGenerator prefixGenerator;

public SequenceGenerator(PrefixGenerator prefixGenerator) {

this.prefixGenerator = prefixGenerator;

}

}

In the element, you can enclose a bean reference by just like in

the element.


class="com.apress.springrecipes.sequence.SequenceGenerator">













The shortcut for specifying a bean reference also works for .


class="com.apress.springrecipes.sequence.SequenceGenerator">



...





9799ch03.qxd



5/5/08



4:50 PM



Page 55



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



Declaring Inner Beans

Whenever a bean instance is used for one particular property only, it can be declared as an

inner bean. An inner bean declaration is enclosed in or directly,

without any id or name attribute set. In this way, the bean will be anonymous so that you can’t

use it anywhere else. In fact, even if you define an id or a name attribute for an inner bean, it

will be ignored.


class="com.apress.springrecipes.sequence.SequenceGenerator">

















An inner bean can also be declared in a constructor argument.


class="com.apress.springrecipes.sequence.SequenceGenerator">



















3-5. Checking Properties with Dependency Checking

Problem

In a production-scale application, there may be hundreds or thousands of beans declared in

the IoC container, and the dependencies between them are often very complicated. One of the

shortcomings of setter injection is that you cannot make sure a property will be injected. It’s

very hard for you to check if all required properties have been set.



Solution

Spring’s dependency checking feature can help you to check if all properties of certain types

have been set on a bean. You simply have to specify the dependency checking mode in the

dependency-check attribute of . Note that the dependency checking feature can only

check if the properties have been set, but can’t check if their value is not null. Table 3-1 lists all

the dependency checking modes supported by Spring.



55



9799ch03.qxd



56



5/5/08



4:50 PM



Page 56



CHAPTER 3 ■ BEAN CONFIGURATION IN SPRING



Table 3-1. Dependency Checking Modes Supported by Spring



Mode



Description



none*



No dependency checking will be performed. Any properties can be left unset.



simple



If any properties of the simple types (the primitive and collection types) have not been

set, an UnsatisfiedDependencyException will be thrown.



objects



If any properties of the object types (other than the simple types) have not been set, an

UnsatisfiedDependencyException will be thrown.



all



If any properties of any type have not been set, an UnsatisfiedDependencyException

will be thrown.



* The default mode is none, but this can be changed by setting the default-dependency-check attribute of the

root element. This default mode will be overridden by a bean’s own mode if specified. You must set

this attribute with great care as it will alter the default dependency checking mode for all the beans in the

IoC container.



How It Works

Checking Properties of the Simple Types

Suppose the suffix property was not set for the sequence generator. Then the generator

would generate sequence numbers whose suffix was the string null. This kind of issue is often

very hard to debug, especially in a complicated bean. Fortunately, Spring is able to check if all

properties of certain types have been set. To ask Spring to check properties of the simple types

(i.e., the primitive and collection types), set the dependency-check attribute of to

simple.


class="com.apress.springrecipes.sequence.SequenceGenerator"

dependency-check="simple">







If any properties of such types have not been set, an UnsatisfiedDependencyException will

be thrown, indicating the unset property.

Exception in thread "main"

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating

bean with name 'sequenceGenerator' defined in class path resource [beans.xml]:

Unsatisfied dependency expressed through bean property 'suffix': Set this property

value or disable dependency checking for this bean.



Xem Thêm
Tải bản đầy đủ (.pdf) (753 trang)

×