A Spring application is composed of a set of beans that perform functionality specific to your application layers and are managed by the IoC container. You define your beans with configuration metadata in the form of XML, annotation, or JavaConfig.
The default scope of a Spring bean is singleton
. This means that a single instance is shared between clients anywhere in the application. Beware of keeping state (class level data) in singleton
classes, as a value set by one client will be visible to all others. The best use case for such singleton
classes are stateless services.
Beans are uniquely identified by an id
attribute, any of the values supplied to the (comma, semicolon, or space separated) name
attribute of the bean definition, or even as an alias
definition. You can refer to a bean anywhere in the application with id
or any of the names or aliases specified in the bean definition.
It's not necessary that you always provide an id
or name to the bean. If one isn't provided, Spring will generate a unique bean name for it; however, if you want to refer to it with a name or an id
, then you must provide one.
Spring will try to autowire beans by type if id
or name is not provided. This means that ApplicationContext
will try to match the bean with the same type or implementation in case it is an interface.
You can refer to a bean by type if it is either the only bean registered of that type or marked as @Primary
(primary="true"
for XML). Generally, for nested bean definitions and autowire collaborators, you don't need to define a name unless you refer to it outside the definition.
You can alias a bean outside the bean definition using the <alias/>
tag, as follows:
<alias name="fromName" alias="toName"/>
A bean definition object that you define to describe a bean has the following metadata:
Property |
Description |
---|---|
|
The fully qualified class name of the bean. |
|
The unique identifier of the bean. |
|
One or more unique names separated by commas, semicolons, or whitespace. Typically, |
|
The parent bean for inheriting configuration data from a parent bean definition. |
|
This decides the scope of the objects. The default scope of a Spring bean is |
|
Bean references or names for constructor-based DI. |
|
Values or references for setter-based DI. |
|
Instructs the bean whether or how to autowire relationships with collaborators. Autowiring will be discussed later. |
|
This indicates that the bean should be considered as the primary autowiring candidate in case of multiple matches being found. |
|
This forces instantiation of dependent beans prior to this bean. |
|
If true, this creates a bean instance when it is first requested. |
|
Initialization callback method. This has no |
|
Destruction callback method. This has no |
|
Static instance factory method on the bean itself, unless |
|
Another bean reference that is acting as an instance factory for this bean. Usually comes along with the |
Let's take a look at a sample bean definition in XML form:
<bean id="xmlTaskService" class="com...XmlDefinedTaskService" init-method="init" destroy-method="cleanup"> <constructor-arg ref="userService"/> <constructor-arg> <bean class="com...TaskInMemoryDAO"></bean> </constructor-arg> </bean>
In this sample application-context
file, the bean, xmlTaskService
, is autowired via a constructor, that is, dependencies are injected via a constructor. The first constructor argument refers to an existing bean definition, and the second one is an inline bean definition without an id
. The bean has init-method
and destroy-method
pointed to its own methods.
Now, let's take a look at an annotated bean with slightly different features:
@Service public class AnnotatedTaskService implements TaskService { ... @Autowired private UserService userService; @Autowired private TaskDAO taskDAO; @PostConstruct public void init() { logger.debug(this.getClass().getName() + " started!"); } @PreDestroy public void cleanup() { logger.debug(this.getClass().getName() + " is about to destroy!"); } public Task createTask(String name, int priority, int createdByuserId, int assigneeUserId) { Task task = new Task(name, priority, "Open", userService.findById(createdByuserId), null, userService.findById(assigneeUserId)); taskDAO.createTask(task); logger.info("Task created: " + task); return task; } ... }
This @Service
bean autowires its dependencies on its fields (properties) using an @Autowired
annotation. Note the @PostConstruct
and @PreDestroy
annotations, the equivalents of init-method
and destroy-method
in the previous XML bean definition example. These are not Spring specific but are JSR 250 annotations. They work pretty well with Spring.
Bean definitions are recipes for instantiating bean instances. Depending on metadata attributes such as scope
, lazy
, and depends-on
, Spring Framework decides when and how an instance is created. We will discuss it in detail later. Here, let's look at the "how" of instance creation.
Any bean definition with or without constructor arguments but without a factory-method
is instantiated via its own constructor, using the new
operator:
<bean id="greeter" class="com...GreetingBean"></bean>
Now let's see an annotated @Component
with a default constructor-based instantiation:
@Component("greeter") public class GreetingService { ... }
A static method within the same class, marked as factory-method
, will be invoked to create an instance in this case:
<bean id="Greeter" class="...GreetingBean" factory-method="newInstance"></bean>
With Java configuration, you can use an @Bean
annotation instead of factory methods:
@Configuration @ComponentScan(basePackages = "com.springessentialsbook") public class SpringJavaConfigurator { ... @Bean public BannerService createBanner() { return new BannerServiceImpl(); } ... }
In this case, bean definition does not need a class attribute, but you specify the factory-bean
attribute, which is another bean, with one of its non-static methods as factory-method
:
<bean id="greeter" factory-bean="serviceFactory" factory-method="createGreeter"/> <bean id="serviceFactory" class="...ServiceFactory"> <!— ... Dependencies ... --> </bean>
The main purpose of an IoC container is to resolve the dependencies of objects (beans) before they are returned to the clients who called for an instance (say, using the getBean
method). Spring does this job transparently based on the bean configuration. When the client receives the bean, all its dependencies are resolved unless specified as not required (@Autowired(required = false)
), and it is ready to use.
Spring supports two major variants of DI—constructor-based and setter-based DI—right out of the box.
In constructor-based DI, dependencies to a bean are injected as constructor arguments. Basically, the container calls the defined constructor, passing the resolved values of the arguments. It is best practice to resolve mandatory dependencies via a constructor. Let's look at an example of a simple POJO @Service
class, a ready candidate for constructor-based DI:
public class SimpleTaskService implements TaskService { ... private UserService userService; private TaskDAO taskDAO; public SimpleTaskService(UserService userService, TaskDAO taskDAO) { this.userService = userService; this.taskDAO = taskDAO; } ... }
Now, let's define this as a Spring bean in XML:
<bean id="taskService" class="com...SimpleTaskService""> <constructor-arg ref="userService" /> <constructor-arg ref="taskDAO"/> </bean>
The Spring container resolves dependencies via a constructor based on the argument's type. For the preceding example, you don't need to pass the index or type of the arguments, since they are of complex types.
However, if your constructor has simple types, such as primitives (int
, long
, and boolean
), primitive wrappers (java.lang.Integer
, Long
, and so on) or String
, ambiguities of type and index may arise. In this case, you can explicitly specify the type and index of each argument to help the container match the arguments, as follows:
<bean id="systemSettings" class="com...SystemSettings"> <constructor-arg index="0" type="int" value="5"/> <constructor-arg index="1" type="java.lang.String" value="dd/mm/yyyy"/> <constructor-arg index="2" type="java.lang.String" value="Taskify!"/> </bean>
Remember, index numbers start from zero. The same applies to setter-based injection as well.
The container calls the setter methods of your bean in the case of setter-based DI after the constructor (with or without args
) is invoked. Let's see how the bean definition for the previous SystemSettings
would look if the dependencies were injected via setter methods, assuming the SystemSettings
now has a no-args
constructor:
<bean id="systemSettings" class="com...SystemSettings"> <property name="openUserTasksMaxLimit" value="5"/> <property name="systemDateFormat" value="dd/mm/yyyy"/> <property name="appDisplayName" value="Taskify!"/> </bean>
Spring validates the bean definitions at the startup of the ApplicationContext
and fails with a proper message in case of a wrong configuration. The string values given to properties with built-in types such as int
, long
, String
, and boolean
are converted and injected automatically when the bean instances are created.
Which of these DI methods is better purely depends on your scenario and some requirements. The following best practices may provide a guideline:
In a typical Spring application, you can see dependencies injected using both approaches, but this depends on the scenario, considering the preceding guidelines.
You can make your bean definitions cleaner and more expressive using p:(property)
and c:(constructor)
namespaces, as shown here. While the p
namespace enables you to use the <bean/>
element's attributes instead of the nested <property/>
elements in order to describe your property values (or collaborating bean refs), the c
namespace allows you to declare the constructor args
as the attributes of the <bean/>
element:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="p-taskService" class="com...SimpleTaskService" c:userService-ref="userService" c:taskDAO-ref="taskDAO"/> <bean id="p-systemSettings" class="com...SystemSettings" p:openUserTasksMaxLimit="5" p:systemDateFormat"dd/mm/yyyy" p:appDisplayName="Taskify!"/> </beans>
The bean definitions in the preceding listing are cleaner but more expressive. Both c:
and p:
namespaces follow the same conventions. You need to declare both at the XML root element (<beans/>
) before using them with the <bean/>
elements. Note that you use the -ref
suffix for bean references.
On occasion, we will need to inject static collections of data as bean dependencies. Spring provides a natural method to wire lists. See this example:
<bean id="systemSettings" class="com...SystemSettings"> . . . <constructor-arg> <list> <value>[email protected]</value> <value>[email protected]</value> <value>[email protected]</value> </list> </constructor-arg> </bean>
The preceding example wires a java.util.List<String>
for simplicity. If your list contains a collection of beans, you can replace <value>
with <ref>
or <bean>
.
You can inject java.util.Map
instances too in a similar fashion. Look at this example:
<bean id="systemSettings" class="com...SystemSettings"> . . . <property name="emails"> <map> <entry key="admin" value="[email protected]"></entry> <entry key="it" value="[email protected]"></entry> <entry key="devops" value="[email protected]"></entry> </map> </property> </bean>
You can inject beans as values, replacing <value>
with <ref>
or <bean>
.
Spring can autowire dependencies of your beans automatically by inspecting the bean definitions present in the ApplicationContext
if you specify the autowire mode. In XML, you specify the autowire
attribute of the <bean/>
element. Alternatively, you can annotate a bean with @Autowired
to autowire dependencies. Spring supports four autowiring modes: no
, byName
, byType
, and constructor
.
The default autowiring of Spring beans is byType
. If you are autowiring an interface, Spring will try to find an implementation of that interface configured as a Spring bean. If there are multiple, Spring will look for the primary
attribute of the configuration to resolve; if not found, it will fail, complaining about an ambiguous bean definition.
Here is an example of autowiring constructor arguments:
@Service public class AnnotatedTaskService implements TaskService { ... @Autowired public AnnotatedTaskService(UserService userService, TaskDAO taskDAO) { this.userService = userService; this.taskDAO = taskDAO; } ... }
Alternatively, you can autowire at the field level, as follows:
@Service public class AnnotatedTaskService implements TaskService { ... @Autowired private UserService userService; @Autowired private TaskDAO taskDAO; ... }
Autowiring can be fine-tuned with an @Qualifier
annotation and required attribute:
@Autowired(required = true) @Qualifier("taskDAO") private UserService userService;
You can use @Qualifier
at the constructor level too:
@Autowired public AnnotatedTaskService(@Qualifier("userService") UserService userService, @Qualifier("taskDAO") TaskDAO taskDAO) { this.userService = userService; this.taskDAO = taskDAO; }
When defining a bean with its dependencies and other configuration values, you can optionally specify the scope of a bean in the bean definition. The scope determines the life span of the bean. Spring comes up with six built-in scopes out of the box and supports the creation of custom scopes too. If not explicitly specified, a bean will assume the singleton
scope, which is the default scope. The following table lists the built-in Spring scopes:
Scope |
Description |
---|---|
|
This ensures a single instance inside the container. This is the default scope. |
|
A new instance is created for every request for the bean. |
|
Scopes an instance with the life cycle of every new HTTP request. |
|
Scopes with the life cycle of every new HTTP session. |
|
Scopes with an HTTP session inside a portlet context. |
|
Scopes with the life cycle of a |
While singleton
and prototype
work in all environments, request, session, and application work only in web environments. The globalSession
scope is for portlet environments.
In an XML bean definition, the scope is set via the scope
attribute of the <bean/>
element:
<bean id="userPreferences" class="com...UserPreferences" scope="session">... </bean>
You can annotate the bean scope as a meta-annotation to @Component
or its derivations, such as @Service
and @Bean
, as shown in the following listing:
@Component @Scope("request") public class TaskSearch {...}
Generally, service classes and Spring data repositories are declared as singleton
, since they are built stateless according to best practice.
Beans of different scopes can be wired up as collaborators in your configuration metadata. For example, if you have a session-scoped bean as a dependency to singleton
and face an inconsistency problem, the first instance of the session-scoped bean will be shared between all users. This can be solved using a scoped proxy in place of the scoped bean:
<bean id="userPreferences" class="com...UserPreferences" scope="session"> <aop:scoped-proxy /> </bean> <bean id="taskService" class="com...TaskService"> <constructor-arg ref="userPreferences"/> </bean>
Every time the scoped bean is injected, Spring creates a new AOP proxy around the bean so that the instance is picked up from the exact scope. The annotated version of the preceding listing would look like this:
@Component @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public class UserPreferences { ... } public class AnnotatedTaskService implements TaskService { ... @Autowired private UserPreferences userPreferences; ... }
At times, the scopes supplied by Spring are not sufficient for your specific needs. Spring allows you to create your own custom scope for your scenario. For example, if you want to keep some business process level information throughout its life, you will want to create a new process scope. The following steps will enable you to achieve this:
org.springframework.beans.factory.config.Scope
.ApplicationContext
either programmatically or in XML with CustomScopeConfigurer
.