The Spring framework comes with lots of bells and whistles. There are at times we wish to inject Java Collections such as Maps or Lists. There are also instances we wish to alter the bean configurations for our own customizations . Configuring such collections outside of the application in a properties file or customizing the beans to our own needs is easily done in Spring. While we learned the basics in the first three chapters, we discuss the more mature and advanced concepts in this chapter.
Did you wonder how many instances will be created when the Spring’s container is loads up the bean config file during the application startup? Do we get the same instance whenever you query the container for the same bean? If we have a case where one and only one bean (such as a service or a factory) is to be created irrespective of the number of times you call the container, how is this achieved? Or for every call, how can we fetch a brand new instance? How does Spring achieve this?
Well, it turns out to be a simple config tag that dictates these types—the scope
tag with values of singleton
and prototype
.
When you need one and only one instance of a bean (TrainFactory
in
this case), you should set the scope
tag to singleton
, as shown here:
<bean
name=
"trainFactory"
scope=
"singleton"
class=
"com.madhusudhan.jscore.fundamentals.scope.TrainFactory"
>
</bean>
However, the default scope
is
always singleton. Hence, you can ignore the setting of
as shown here:scope
to singleton
<bean
name=
"trainFactory"
class=
"com.madhusudhan.jscore.fundamentals.scope.TrainFactory"
>
</bean>
Every time a trainFactory
is
injected into another bean or queried using the getBean()
method, the same instance is
returned.
Note that there is a subtle difference between the instance obtained using the Java Singleton pattern and Spring Singleton. Spring Singleton is a singleton per context or container, whereas the Java Singleton is per process and per class loader.
Prototype scope creates a new instance every time a call is made
to fetch the bean. For example, we define a Train
object that gets created every time it
gets instantiated.
Because our requirement is to create a new Train
everytime, the scope
has to be set to prototype
in our config file. Spring would then create a new
Train
object for every invocation.
<bean
name=
"train"
scope="prototype" class="com.madhusudhan.jscore.fundamentals.scope.Train"><property
name=
"trainName"
value=
"London to Paris Special"
/>
</bean>
As you may have observed, nothing in the class definitions would indicate the scope of
the classes. There seems to be no difference in the code base, whether using
singleton
or prototype
. The scope is managed
declaratively, hence giving you the option of turning it on or off according to the
requirement.
For completeness, let’s execute a test class that prints out the
Train
and TrainFactory
’s
hashCode
values. The following is the test
client:
public
class
TrainClient
{
private
static
ApplicationContext
context
=
null
;
private
Train
train
=
null
;
private
TrainFactory
trainFactory
=
null
;
public
void
init
()
{
context
=
new
ClassPathXmlApplicationContext
(
"fundamentals-beans.xml"
);
}
private
void
checkTrainInstance
()
{
for
(
int
i
=
0
;
i
<
10
;
i
++)
{
train
=
context
.
getBean
(
"train"
,
Train
.
class
);
System
.
out
.
println
(
"Train instance: "
+
train
.
getInstance
());
}
}
private
void
checkTrainFactoryInstance
()
{
for
(
int
i
=
0
;
i
<
10
;
i
++)
{
trainFactory
=
context
.
getBean
(
"trainFactory"
,
TrainFactory
.
class
);
System
.
out
.
println
(
"TrainFactory instance: "
+
trainFactory
.
getInstance
());
}
}
public
static
void
main
(
String
[]
args
)
{
TrainClient
client
=
new
TrainClient
();
client
.
init
();
client
.
checkTrainInstance
();
client
.
checkTrainFactoryInstance
();
}
}
We loop through 10 times to check the instance’s hashCode value—obviously we expect the same hashCode for singleton and different hashCode for prototype beans. When the test is executed, the output is:
Train instance: 1443639316 Train instance: 975740206 Train instance: 1080513750 ... TrainFactory instance: 2074631480 TrainFactory instance: 2074631480 TrainFactory instance: 2074631480 ...
As you can see the Train
instance is different
everytime while the TrainFactory
is the same.
There are other types of scopes such as session
,
request
, and global-session
—they are used in the
Spring’s web applications.
When defining the FileReader
or FtpReader
, for example, we have set the hard-coded properties in
the XML file. The filename or the ftp hostname/credentials were all hard wired which is
definitely not a good thing.
If we need to change the properties from one environment to another, we will have to modify this XML file. This is definitely not a good practice. Spring gives us another way of injecting these properties.
What we need to do is to define a property file with name-value
pairs and let Spring’s PropertyPlaceholderConfigurer
read it. This class loads up a properties file specified by a location and
resolves the properties.
Let’s see how we can achieve this.
Create a JobSearchAgent class with two properties—location and type. The location attribute indicates the candidate’s preferred job location while the type attribute indicates the type of job to search (permanent, temporary, etc.).
public
class
JobSearchAgent
{
private
String
location
=
null
;
private
String
type
=
null
;
// Setters and Getters for these properties
// ...
}
We want these two properties to be loaded from an external properties file—the rational
being that each candidate may have an associaed agent with him/her. So, the next step is to
create a property file called fundamentals-beans.properties
in our resources area and add the following properties and their values:
job.location = /users/mkonda/dev/js; job.type = permanent;
Did you notice the convention that I’m following when naming the properties file? Usually, I prefer naming the properties file exactly as the bean definitions file except the extension (.properties in the former case).
The last bit of the puzzle is the config meta data of the JobSearchAgent class.
Edit the fundamentals-beans.xml file to add the framework’s class
named PropertyPlaceholderConfigurer
. This class has a
property called location, which should be pointing to our properties file:
<bean
class=
"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
>
<property
name=
"location"
value=
"classpath:fundamentals-beans.properties"
/>
</bean>
The PropertyPlaceholderConfigurer
is a central class in resolving the
properties. The responsibility of this PropertyPlaceholderConfigurer
is to search for the property files in the classpath and load them into the
application.
The last step is to parameterize the JobSearchAgent
class’s properties in the config file:
<bean
name=
"jobSearchAgent"
class=
"com.madhusudhan.jscore.fundamentals.properties.JobSearchAgent"
>
<property
name=
"type"
value=
"${job.type}"
/>
<property
name=
"location"
value=
"${job.location}"
/>
</bean>
The ${job.type}
and ${job.location}
properties resolves to the name-value pairs defined in the
fundamentals-beans.properties
. They are replaced
appropriately during the runtime of the application thus allowing us to create
environment-specific properties.
The format of the property attributes follow the famous Ant
style—${name}
. However, Framework provides a way of customizing the
prefix and suffixes.
For example, if we want to call the properties as #[name]
instead of
following the default ${} style, we need to set two properties
placeholderPrefix
and placeholderSuffix
with our
choice. See the following snippet for an example:
<bean
class=
"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
>
<property
name=
"location"
value=
"classpath:fundamentals-beans.properties"
/>
<property
name=
"placeholderPrefix"
value=
"#["
/>
<property
name=
"placeholderSuffix"
value=
"]"
/>
</bean>
Of course, the bean properties should reflect these changes—see the values in bold:
<bean
name=
"jobSearchAgent"
class=
"com.madhusudhan.jscore.fundamentals.properties.JobSearchAgent"
>
<property
name=
"type"
value=
"
#[job.type]"/><property
name=
"location"
value=
"
#[job.location]"/></bean>
Should you wish to read properties from multiple locations, set the
locations
property with the list of property files as shown here:
<bean
class=
"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
>
<property
name=
"locations"
>
<list>
<value>
classpath:fundamentals-beans.properties</value>
<value>
classpath:fundamentals-beans_1.properties</value>
</list>
</property>
</bean>
When we set the property values in the Spring config file, from the declaration, it sounds
like the properties were just Strings or Java primitives. However, there will be instances
when we need to pass in an externally initialized collection, object references, or other
values. Spring follows the Java Bean style property editor mechanism to facilitate such
requirements. We can inject any Java Collection such as List
,
Set
, Map
by declaring them in the config XML file. We
can also provide java.util.Properties
. We’ll see them in action
here.
If the property is another bean reference (a dependency), the actual type of the bean is resolved when injecting the dependency.
Injecting an initialized collection such as a List
, Set
,
or Map
couldn’t be any easier in
Spring. There is a specific syntax to follow in the config file so the
collection is initialized before injecting.
Lists are one of the frequent collections used heavily in Java programs. We may have instances of creating a pre-populated lists such as list of currencies, list of countries or list of week days, and so on.
The following CapitalCitiesManager
class defines a variable called
countriesList
which holds the list of Countries as Strings. Obviously
this is a static list that can be initialized outside the program in the config
XML.
public
class
CapitalCitiesManager
{
private
List
<
String
>
countriesList
=
null
;
// getters and setters
public
List
<
String
>
getCountriesList
()
{
return
countriesList
;
}
public
void
setCountriesList
(
List
<
String
>
countriesList
)
{
this
.
countriesList
=
countriesList
;
}
}
In order to use List
s,
declare the property countryList
(the property name
should match to the variable name) with a list
element. This list
element will then hold
individual value
tags enclosing the values.
The following config metadata shows the usage of
java.util.List
in our
CapitalCitiesManager
class.
<bean
name=
"capitalCitiesManager"
class=
"com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager"
>
<property
name=
"countriesList"
>
<list>
<value>
United Kingdom</value>
<value>
United States Of America</value>
<value>
India</value>
<value>
Japan</value>
</list>
</property>
</bean>
Similarly, if you have to create list of countries by using a Set
(remember, java.util.Set
will not accept duplicates)
values, swap the list
tag shown in the above snippet with this set
tag:
<bean
name=
"capitalCitiesManager"
class=
"com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager"
>
<property
name=
"countriesSet"
>
<set>
<value>
United Kingdom</value>
<value>
United Kingdom</value>
<value>
United States Of America</value>
<value>
United States Of America</value>
<value>
India</value>
<value>
Japan</value>
</set>
</property>
Purposely, I have created the duplicate values for the above Set
element. When you run the test client, you will see the Set
populated
with four unique countries!
Coming to Map
s, as expected,
we need to use the java.util.Map
implementation as shown in the following code snippet:
public
class
CapitalCitiesManager
{
private
Map
<
String
,
String
>
countriesCapitalsMap
=
null
;
//Getters and Setters
public
Map
<
String
,
String
>
getCountriesCapitalsMap
()
{
return
countriesCapitalsMap
;
}
public
void
setCountriesCapitalsMap
(
Map
<
String
,
String
>
countriesCapitalsMap
)
{
this
.
countriesCapitalsMap
=
countriesCapitalsMap
;
}
}
However, when configuring Map
s, there are certain subtle
differences. The element in a map is made of an Entry
node, which has name-value pairs defined as key
and
value
attributes:
<bean
name=
"capitalCitiesManager"
class=
"com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager"
>
<property
name=
"countriesCapitalsMap"
>
<map>
<entry
key=
"England"
value=
"London"
/>
<entry
key=
"USA"
value=
"Washington DC"
/>
</map>
</property>
</bean>
Values can also refer to any other object references—for example, the entry UK has a reference to UnitedKingdomUnion bean. See the snippet here here:
<bean
name=
"capitalCitiesManager"
class=
"com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager"
>
<property
name=
"countriesCapitalsRefMap"
>
<map>
<entry
key=
"UK"
value-ref="ukUnion"/></map>
</property>
</bean>
<bean
name=
"ukUnion"
class=
"com.madhusudhan.jscore.fundamentals.propertyeditor.UnitedKingdomUnion"
/>
Note that the ukUnion
bean is referenced
using the value-ref
tag instead of
value
tag as we did in the earlier snippet.
The java.util.Properties
object represents a
name-value pair. The following code snippet defines the
Properties
object in our
CapitalCitiesManager
class:
public
class
CapitalCitiesManager
{
private
Properties
countryProperties
=
null
;
public
Properties
getCountryProperties
()
{
return
countryProperties
;
}
public
void
setCountryProperties
(
Properties
countryProperties
)
{
this
.
countryProperties
=
countryProperties
;
}
}
The next step is wiring up the bean in the config.The
countriesProperties
variable should be declared as a
property to the bean.
See the XML config here:
<bean
name=
"capitalCitiesManager"
class=
"com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager"
>
<property
name=
"countryProperties"
>
<props>
<prop
key=
"UK"
>
London</prop>
<prop
key=
"USA"
>
Washington DC</prop>
</props>
</property>
</bean>
The Properties
object is represented by
props
element as you can see above. Each of the
element in the Properties
has a
prop
attribute that will have our key and
values.
Spring allows us to poke into the bean’s life cycle by implementing bean post processors.
In these post processors, we get a chance to alter the configuration, set a custom value, or
implement sophisticated init
processes.
The Framework provides a BeanPostProcessor
interface
which is used to exploit this functionality. This interface has two callback methods: postProcessBeforeInitialization
and postProcessAfterInitialization
. The BeanPostProcessor
is set per container only—all the beans in that container will
be processed by this post processor. We can have multiple post processors doing various bits
if we like.
As the names indicate, the postProcessBeforeInitialization
method is invoked just before calling your
init-method
or afterPropertiesSet
method on the bean. Our bean is just about to be implemented,
but we have been given a chance to do something before the final call! Similarly, the postProcessAfterInitialization
method is called just after the
initialization of the bean is completed.
Let’s take a simple case—any bean declared in a particular container should have a pre-set
variable componentName.
If the
componentName
is not set (is empty), then the bean should be set with a
default value, say, LONDON-DEFAULT-COMP
. The easiest way to achieve this
requirement is to create a class that implements the BeanPostProcessor
interface and modify the value of componentName
variable.
First, we’ll create a processor, in this case we call it DefaultComponentNamePostProcessor
:
public
class
DefaultComponentNamePostProcessor
implements
BeanPostProcessor
{
public
Object
postProcessBeforeInitialization
(
Object
o
,
String
string
)
throws
BeansException
{
System
.
out
.
println
(
"PostProcess BEFORE init: "
+
o
);
if
(
o
instanceof
PostProcessComponent
){
((
PostProcessComponent
)
o
).
setComponentName
(
"LONDON-DEFAULT-COMP"
);
}
return
o
;
}
public
Object
postProcessAfterInitialization
(
Object
o
,
String
string
)
throws
BeansException
{
System
.
out
.
println
(
"PostProcess AFTER init: "
+
o
);
if
(
o
instanceof
PostProcessComponent
){
System
.
out
.
println
(
"Default Property:
"
+((
PostProcessComponent
)
o
).
getComponentName
());
}
return
o
;
}
}
As you can see, the DefaultComponentNamePostProcessor
class implements
two superclass’s methods (highlighted in bold). Any bean that’s been created in this container
will be processed by this post processor. What we are doing here is setting a property
componentName
during the
postProcessBeforeInitialization
method invocation while simply printing
it out during the postProcessAfterInitialization
method (and returning the
unmodified instance).
To complete our discussion, we need to create a simple POJO PostProcessComponent
that has a variable named componentName
which will not be set yet.
Here’s the code snippet of PostProcessComponent
class:
public
class
PostProcessComponent
{
private
String
componentName
=
null
;
public
String
getComponentName
()
{
return
componentName
;
}
public
void
setComponentName
(
String
componentName
)
{
this
.
componentName
=
componentName
;
}
}
The last thing we need to do is to declare the post processor and the bean in the XML config file:
<bean
name=
"compNamePostProcessor"
class=
"com...containers.postprocessor.DefaultComponentNamePostProcessor"
/>
<bean
name=
"postProcessComponent"
class=
"com.madhusudhan.jscore.containers.postprocessor.PostProcessComponent"
/>
We do not have to define the bean as a post processor anywhere in the config, just declare them as normal beans. The container will automatically find all the post processors and load them for further work.
When you run the example, you can see the following output:
PostProcess BEFORE init: PostProcessComponent{componentName=null} PostProcess AFTER init: PostProcessComponent{componentName=LONDON-DEFAULT-COMP}
If you have several beans defined in the container config file with
componentName
not set, all of them will
then be pre-proceessed by the DefaultComponentNamePostProcessor
to set a
default componentName
.
We need to tell the framework that we’re using post processors. If
the container is BeanFactory
, we have
to invoke addBeanPostProcessor
(our
processor) on the BeanFactory
in order
to set the post processor instance.
However, if ApplicationContext
is
our container, defining the post processor in the config file is adequate.
The container automatically looks at the class to see if it’s a post
processor and then invokes the appropriate methods during the
instantiation of the bean.
Note that the BeanPostProcessor
s are applied per container while the
bean init
or destroy
methods are applied per
bean.
Spring allows us to declare abstract and implemented classes in the configuration. If
AbstractKlass
is an abstract class and Klass
is a
sub-class of it, they can be configured as shown here:
<bean
name=
"abstractKlass"
class=
"com.madhusudhan.jscore.fundamentals.inherit.AbstractKlass"
abstract="true"/><bean
name=
"klass"
class=
"com.madhusudhan.jscore.fundamentals.inherit.Klass"
parent="abstractKlass"/>
When we declare an abstract class, we need decorate it with abstract="true"
as shown in bold in the above snippet. The subclass should be defined with parent
attribute refering to this abstract class.
This chapter completes our journey into core Spring. It explains the fundamentals of containers and their usage. It then delves into using autowiring beans where you do not have to set properties on the beans. It then explains various ways of instantiating the beans, such as static methods or factory methods. We have also seen event handling supported by the framework.
One important aspect of Spring is its support for enterprise features such as Spring JMS and Database. The next chapter explains the simplification Spring has brought into the Java messaging world.
Get Just Spring now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.