0% found this document useful (0 votes)
31 views74 pages

Spring Framework 141 280 1 70

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
31 views74 pages

Spring Framework 141 280 1 70

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 74

Java

import jakarta.inject.Inject; import jakarta.inject.Named;


public class SimpleMovieLister { private MovieFinder movieFinder; @Inject
import jakarta.inject.Inject import jakarta.inject.Named
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
class SimpleMovieLister {
}
private lateinit var movieFinder: MovieFinder @Inject
fun setMovieFinder(@Named("main") movieFinder: MovieFinder) { this.movieFinder = movieFinder
// ...
}
}
// ...
}

Kotlin

As with @Autowired, @Inject can also be used with java.util.Optional or @Nullable. This
is even more applicable here, since @Inject does not have a required attribute. The
following pair of examples show how to use @Inject and @Nullable:

public class SimpleMovieLister {

@Inject
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
// ...
}
}

1
Java
public class SimpleMovieLister {

@Inject
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
// ...
}
}

Kotlin
class SimpleMovieLister {

@Inject
var movieFinder: MovieFinder? = null
}

@Named and @ManagedBean: Standard Equivalents to the @Component Annotation

Instead of @Component, you can use @jakarta.inject.Named or


jakarta.annotation.ManagedBean, as the following example shows:

Java
import jakarta.inject.Inject; import jakarta.inject.Named;

@Named("movieListener")// @ManagedBean("movieListener") could be used as well public class Si


private MovieFinder movieFinder; @Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...
}

2
import jakarta.inject.Inject; import jakarta.inject.Named;
import jakarta.inject.Inject import jakarta.inject.Named
@Named
public class SimpleMovieLister @ManagedBean("movieListener")
@Named("movieListener")// { could be used as well class SimpleM
private MovieFinder movieFinder; @Inject
public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder;
@Inject
} lateinit var movieFinder: MovieFinder

// ...
// ...
} }

Kotlin

It is very common to use @Component without specifying a name for the component.
@Named can be used in a similar fashion, as the following example shows:

Jav

Kotlin
import jakarta.inject.Inject import jakarta.inject.Named

@Named
class SimpleMovieLister {

@Inject
lateinit var movieFinder: MovieFinder

// ...
}

3
When you use @Named or @ManagedBean, you can use component scanning in the
exact same way as when you use Spring annotations, as the following example shows:

Java
@Configuration
@ComponentScan(basePackages = "org.example") public class AppConfig{
// ...
}

Kotlin
@Configuration
@ComponentScan(basePackages = ["org.example"]) class AppConfig{
// ...
}

In contrast to @Component, the JSR-330 @Named and the JSR-250


@ManagedBean annotations are not composable. You should use Spring’s
stereotype model for building custom component annotations.

Limitations of JSR-330 Standard Annotations

When you work with standard annotations, you should know that some significant
features are not available, as the following table shows:

Table 6. Spring component model elements versus JSR-330 variants

Spring jakarta.inject.* jakarta.inject


restrictions / comments

@Autowired @Inject @Inject has no 'required'


attribute. Can be used with
Java 8’s Optional instead.
@Component @Named / @ManagedBean JSR-330 does not provide a
composable model, only a
way to identify named
components.

4
Spring jakarta.inject.* jakarta.inject
restrictions / comments

@Scope("singleton") @Singleton The JSR-330 default scope is


like Spring’s prototype.
However, in order to keep it
consistent with Spring’s
general defaults, a JSR- 330
bean declared in the Spring
container is a singleton by
default. In order to use a
scope other than singleton,
you should use Spring’s
@Scope annotation.
jakarta.inject also provides a
jakarta.inject.Scope
annotation: however, this
one is only intended to be
used for creating custom
annotations.
@Qualifier @Qualifier / @Named jakarta.inject.Qualifier is just
a meta-annotation for
building custom qualifiers.
Concrete String qualifiers
(like Spring’s @Qualifier
with a value) can be
associated through
jakarta.inject.Named.
@Value - no equivalent

@Lazy - no equivalent

ObjectFactory Provider jakarta.inject.Provider is a


direct alternative to
Spring’s ObjectFactory, only
with a shorter get() method
name. It can also be used in
combination with Spring’s
@Autowired or with non-
annotated constructors and
setter methods.

2.1.12. Java-based Container Configuration


This section covers how to use annotations in your Java code to configure the Spring
container. It includes the following topics:

• Basic Concepts: @Bean and @Configuration


• Instantiating the Spring Container by Using AnnotationConfigApplicationContext
• Using the @Bean Annotation
• Using the @Configuration annotation
5
• Composing Java-based Configurations

6
• Bean Definition Profiles

• PropertySource Abstraction
• Using @PropertySource

• Placeholder Resolution in Statements

Basic Concepts: @Bean and @Configuration

The central artifacts in Spring’s new Java-configuration support are @Configuration-


annotated classes and @Bean-annotated methods.

The @Bean annotation is used to indicate that a method instantiates, configures, and
initializes a new object to be managed by the Spring IoC container. For those familiar
with Spring’s <beans/> XML configuration, the @Bean annotation plays the same role
as the <bean/> element. You can use @Bean
-annotated methods with any Spring @Component. However, they are most
often used with
@Configuration beans.

Annotating a class with @Configuration indicates that its primary purpose is as a source
of bean definitions. Furthermore, @Configuration classes let inter-bean dependencies be
defined by calling other @Bean methods in the same class. The simplest possible
@Configuration class reads as follows:

Java
@Configuration
public class AppConfig {

@Bean
public MyService myService() { return new MyServiceImpl();
}
}

Kotlin
@Configuration class AppConfig {

@Bean
fun myService(): MyService { return MyServiceImpl()
}
}

The preceding AppConfig class is equivalent to the following Spring <beans/> XML:

<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

7
Full @Configuration vs “lite”
@Bean mode?
When @Bean methods are declared within classes that are not annotated with
@Configuration, they are referred to as being processed in a “lite” mode. Bean
methods declared in a @Component or even in a plain old class are considered to
be “lite”, with a different primary purpose of the containing class and a @Bean
method being a sort of bonus there. For example, service components may
expose management views to the container through an additional @Bean
method on each applicable component class. In such scenarios, @Bean methods
are a general-purpose factory method mechanism.

Unlike full @Configuration, lite @Bean methods cannot declare inter-bean


dependencies. Instead, they operate on their containing component’s internal
state and, optionally, on arguments that they may declare. Such a @Bean method
should therefore not invoke other @Bean methods. Each such method is literally
only a factory method for a particular bean reference, without any special
runtime semantics. The positive side-effect here is that no CGLIB subclassing
has to be applied at runtime, so there are no limitations in terms of class design
(that is, the containing class may be final and so forth).

In common scenarios, @Bean methods are to be declared within


@Configuration classes, ensuring that “full” mode is always used and that
cross-method references therefore get redirected to the container’s lifecycle
management. This prevents the same @Bean method from accidentally being
invoked through a regular Java call, which helps to reduce subtle bugs that can
be hard to track down when operating in “lite” mode.

The @Bean and @Configuration annotations are discussed in depth in the following
sections. First, however, we cover the various ways of creating a spring container by
using Java-based configuration.

Instantiating the Spring Container by Using AnnotationConfigApplicationContext

The following sections document Spring’s AnnotationConfigApplicationContext,


introduced in Spring
3.0. This versatile ApplicationContext implementation is capable of accepting not only
@Configuration classes as input but also plain @Component classes and classes annotated
with JSR-330 metadata.

When @Configuration classes are provided as input, the @Configuration class itself is
registered as a bean definition and all declared @Bean methods within the class are
also registered as bean definitions.

When @Component and JSR-330 classes are provided, they are registered as bean
definitions, and it is assumed that DI metadata such as @Autowired or @Inject are used
within those classes where necessary.

Simple Construction

In much the same way that Spring XML files are used as input when instantiating a
ClassPathXmlApplicationContext, you can use @Configuration classes as input when

8
instantiating an AnnotationConfigApplicationContext. This allows for completely XML-
free usage of the Spring

9
container, as the following example shows:

Java
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService my
myService.doStuff();
}

Kotlin
import org.springframework.beans.factory.getBean

fun main() {
val ctx = AnnotationConfigApplicationContext(AppConfig::class.java) val myService = ctx.getBean<
myService.doStuff()
}

As mentioned earlier, AnnotationConfigApplicationContext is not limited to working only


with @Configuration classes. Any @Component or JSR-330 annotated class may be supplied
as input to the constructor, as the following example shows:

Java
public static void main(String[] args) { ApplicationContext ctx = new
AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class)
MyService myService = ctx.getBean(MyService.class); myService.doStuff();
}

Kotlin
import org.springframework.beans.factory.getBean

fun main() {
val ctx = AnnotationConfigApplicationContext(MyServiceImpl::class.java, Dependency1::class.java,
val myService = ctx.getBean<MyService>() myService.doStuff()
}

The preceding example assumes that MyServiceImpl, Dependency1, and Dependency2


use Spring dependency injection annotations such as @Autowired.

10
Building the Container Programmatically by Using register(Class<?>…)

You can instantiate an AnnotationConfigApplicationContext by using a no-arg constructor and


then configure it by using the register() method. This approach is particularly useful
when programmatically building an AnnotationConfigApplicationContext. The following
example shows how to do so:

Java
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(
ctx.refresh();
MyService myService = ctx.getBean(MyService.class); myService.doStuff();
}

Kotlin
import org.springframework.beans.factory.getBean

fun main() {
val ctx = AnnotationConfigApplicationContext() ctx.register(AppConfig::class.java, OtherConfig::cla
ctx.refresh()
val myService = ctx.getBean<MyService>() myService.doStuff()
}

Enabling Component Scanning with scan(String…)

To enable component scanning, you can annotate your @Configuration class as follows:

Java
@Configuration
@ComponentScan(basePackages = "com.acme") ①
public class AppConfig{
// ...
}

① This annotation enables component scanning.

11
Kotlin
@Configuration
@ComponentScan(basePackages = ["com.acme"]) ①
class AppConfig{
// ...
}

① This annotation enables component scanning.

Experienced Spring users may be familiar with the XML declaration


equivalent from Spring’s context: namespace, shown in the following
example:

<beans>
<context:component-scan base-package="com.acme"/>
</beans>

In the preceding example, the com.acme package is scanned to look for any
@Component-annotated classes, and those classes are registered as Spring bean
definitions within the container. AnnotationConfigApplicationContext exposes the
scan(String…) method to allow for the same component-scanning functionality, as the
following example shows:

Java
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("co
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}

Kotlin
fun main() {
val ctx = AnnotationConfigApplicationContext() ctx.scan("com.acme")
ctx.refresh()
val myService = ctx.getBean<MyService>()
}

Remember that @Configuration classes are meta-annotated with


@Component, so they are candidates for component-scanning. In the
preceding example, assuming that AppConfig is declared within the
com.acme package (or any package underneath), it is picked up during
the call to scan(). Upon refresh(), all its @Bean methods are processed
and registered as bean definitions within the container.

12
Support for Web Applications with AnnotationConfigWebApplicationContext

A WebApplicationContext variant of AnnotationConfigApplicationContext is available with


AnnotationConfigWebApplicationContext. You can use this implementation when
configuring the Spring ContextLoaderListener servlet listener, Spring MVC
DispatcherServlet, and so forth. The following web.xml snippet configures a typical
Spring MVC web application (note the use of the contextClass context-param and init-
param):

<web-app>
<!-- Configure ContextLoaderListener to use
AnnotationConfigWebApplicationContext instead of the default
XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>

org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>

<!-- Configuration locations must consist of one or more comma- or space-


delimited fully-qualified @Configuration classes. Fully-qualified packages
may also be specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.AppConfig</param-value>
</context-param>

<!-- Bootstrap the root application context as usual using ContextLoaderListener


--
> <listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Declare a Spring MVC DispatcherServlet as usual -->


<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-
class
> <!-- Configure DispatcherServlet to use
AnnotationConfigWebApplicationContext instead of the default
XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>

org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- Again, config locations must consist of one or more comma- or
space- delimited
and fully-qualified @Configuration classes -->

13
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.web.MvcConfig</param-value>
</init-param>
</servlet>

<!-- map all requests for /app/* to the dispatcher servlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
For programmatic use cases, a GenericWebApplicationContext can be used as an alternativetoAnnota
</web-app>

Using the @Bean Annotation

@Bean is a method-level annotation and a direct analog of the XML <bean/> element.
The annotation supports some of the attributes offered by <bean/>, such as:

• init-method

• destroy-method

• autowiring

• name.

You can use the @Bean annotation in a @Configuration-annotated or in a @Component-


annotated class.

Declaring a Bean

To declare a bean, you can annotate a method with the @Bean annotation. You use this
method to register a bean definition within an ApplicationContext of the type specified as
the method’s return value. By default, the bean name is the same as the method
name. The following example shows a @Bean method declaration:

Java
@Configuration
public class AppConfig {

@Bean
public TransferServiceImpl transferService() { return new TransferServiceImpl();
}
}

14
Kotlin
@Configuration class AppConfig {

@Bean
fun transferService() = TransferServiceImpl()
}

The preceding configuration is exactly equivalent to the following Spring XML:

<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

Both declarations make a bean named transferService available in the


ApplicationContext, bound to an object instance of type TransferServiceImpl, as the
following text image shows:

transferService -> com.acme.TransferServiceImpl

You can also use default methods to define beans. This allows composition of bean
configurations by implementing interfaces with bean definitions on default methods.

Java
public interface BaseConfig {

@Bean
default TransferServiceImpl transferService() { return new TransferServiceImpl();
}
}

@Configuration
public class AppConfig implements BaseConfig {

You can also declare your @Bean method with an interface (or base class) return
type, as the following example shows:

15
Java
@Configuration
public class AppConfig {

@Bean
public TransferService transferService() { return new TransferServiceImpl();
}
}

Kotlin
@Configuration class AppConfig {

@Bean
fun transferService(): TransferService { return TransferServiceImpl()
}
}

However, this limits the visibility for advance type prediction to the specified interface
type (TransferService). Then, with the full type (TransferServiceImpl) known to the
container only once the affected singleton bean has been instantiated. Non-lazy
singleton beans get instantiated according to their declaration order, so you may see
different type matching results depending on when another component tries to match
by a non-declared type (such as @Autowired TransferServiceImpl, which resolves only
once the transferService bean has been instantiated).

If you consistently refer to your types by a declared service interface,


your @Bean return types may safely join that design decision.
However, for components that implement several interfaces or for
components potentially referred to by their implementation type, it is
safer to declare the most specific return type possible (at least as
specific as required by the injection points that refer to your bean).

Bean Dependencies

A @Bean-annotated method can have an arbitrary number of parameters that describe


the dependencies required to build that bean. For instance, if our TransferService
requires an AccountRepository, we can materialize that dependency with a method
parameter, as the following example shows:

16
Java
@Configuration
public class AppConfig {

@Bean
public TransferService transferService(AccountRepository accountRepository) { return new Transfe
}
}

Kotlin
@Configuration class AppConfig {

@Bean
fun transferService(accountRepository: AccountRepository): TransferService { return TransferServi
}
}

The resolution mechanism is pretty much identical to constructor-based dependency


injection. See the relevant section for more details.

Receiving Lifecycle Callbacks

Any classes defined with the @Bean annotation support the regular lifecycle
callbacks and can use the @PostConstruct and @PreDestroy annotations from JSR-250. See
JSR-250 annotations for further details.

The regular Spring lifecycle callbacks are fully supported as well. If a bean
implements
InitializingBean, DisposableBean, or Lifecycle, their respective methods are called by the
container.

The standard set of *Aware interfaces (such as BeanFactoryAware, BeanNameAware,


MessageSourceAware, ApplicationContextAware, and so on) are also fully supported.

The @Bean annotation supports specifying arbitrary initialization and destruction


callback methods, much like Spring XML’s init-method and destroy-method attributes on
the bean element, as the following example shows:

17
Java
public class BeanOne {

public void init() {


// initialization logic
}
}
public class BeanTwo { public void cleanup() {
// destruction logic
}
}

@Configuration
public class AppConfig {

@Bean(initMethod = "init") public BeanOne beanOne() {


return new BeanOne();
}

@Bean(destroyMethod = "cleanup") public BeanTwo beanTwo() {


return new BeanTwo();
}
}

18
Kotlin
class BeanOne {

fun init() {
// initialization logic
}
}

class BeanTwo {

fun cleanup() {
// destruction logic
}
}

@Configuration class AppConfig {

@Bean(initMethod = "init") fun beanOne() = BeanOne()

@Bean(destroyMethod = "cleanup") fun beanTwo() = BeanTwo()


}

19
By default, beans defined with Java configuration that have a public close
or shutdown method are automatically enlisted with a destruction
callback. If you have a public close or shutdown method and you do
not wish for it to be called when the container shuts down, you can add
@Bean(destroyMethod="") to your bean definition to disable the default
(inferred) mode.

You may want to do that by default for a resource that you acquire with
JNDI, as its lifecycle is managed outside the application. In particular,
make sure to always do it for a DataSource, as it is known to be
problematic on Jakarta EE application servers.

The following example shows how to prevent an automatic


destruction callback for a DataSource:

Java
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException { return (DataSource) jndiTe
}

Kotlin
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
return jndiTemplate.lookup("MyDS") as DataSource
}

Also, with @Bean methods, you typically use programmatic JNDI lookups,
either by using Spring’s JndiTemplate or JndiLocatorDelegate helpers or
straight JNDI InitialContext usage but not the JndiObjectFactoryBean variant
(which would force you to declare the return type as the FactoryBean type
instead of the actual target type, making it harder to use for cross-
reference calls in other @Bean methods that intend to refer to the
provided resource here).

In the case of BeanOne from the example above the preceding note, it would be
equally valid to call the init() method directly during construction, as the following
example shows:

20
Java
@Configuration
public class AppConfig {

@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne(); beanOne.init();
return beanOne;
}

// ...
}

Kotlin
@Configuration class AppConfig {

@Bean
fun beanOne() = BeanOne().apply { init()
}

// ...
}

When you work directly in Java, you can do anything you like with your
objects and do not always need to rely on the container lifecycle.

Specifying Bean Scope

Spring includes the @Scope annotation so that you can specify the scope of a bean.

Using the @Scope Annotation

You can specify that your beans defined with the @Bean annotation should have a
specific scope. You can use any of the standard scopes specified in the Bean Scopes
section.

The default scope is singleton, but you can override this with the @Scope
annotation, as the following example shows:

21
Java
@Configuration
public class MyConfiguration {

@Bean @Scope("prototype")
public Encryptor encryptor() {
// ...
}
}

Kotlin
@Configuration
class MyConfiguration {

@Bean @Scope("prototype")
fun encryptor(): Encryptor {
// ...
}
}

@Scope and scoped-proxy

Spring offers a convenient way of working with scoped dependencies through


scoped proxies. The easiest way to create such a proxy when using the XML
configuration is the <aop:scoped-proxy/> element. Configuring your beans in Java
with a @Scope annotation offers equivalent support with the proxyMode attribute. The
default is ScopedProxyMode.DEFAULT, which typically indicates that no scoped proxy
should be created unless a different default has been configured at the component-
scan instruction level. You can specify ScopedProxyMode.TARGET_CLASS,
ScopedProxyMode.INTERFACES or ScopedProxyMode.NO.

If you port the scoped proxy example from the XML reference documentation (see
scoped proxies) to our @Bean using Java, it resembles the following:

22
Java
// an HTTP Session-scoped bean exposed as a proxy @Bean
@SessionScope
public UserPreferences userPreferences() { return new UserPreferences();
}

@Bean
public Service userService() {
UserService service = new SimpleUserService();
// a reference to the proxied userPreferences bean service.setUserPreferences(userPreferences());
}

Kotlin
// an HTTP Session-scoped bean exposed as a proxy @Bean
@SessionScope
fun userPreferences() = UserPreferences()

@Bean
fun userService(): Service {
return SimpleUserService().apply {
// a reference to the proxied userPreferences bean setUserPreferences(userPreferences())
}
}

Customizing Bean Naming

By default, configuration classes use a @Bean method’s name as the name of the
resulting bean. This functionality can be overridden, however, with the name attribute,
as the following example shows:

Java
@Configuration
public class AppConfig {

@Bean("myThing") public Thing thing() {


return new Thing();
}
}

23
Kotlin
@Configuration class AppConfig {

@Bean("myThing")
fun thing() = Thing()
}

Bean Aliasing

As discussed in Naming Beans, it is sometimes desirable to give a single bean


multiple names, otherwise known as bean aliasing. The name attribute of the @Bean
annotation accepts a String array for this purpose. The following example shows how
to set a number of aliases for a bean:

Java
@Configuration
public class AppConfig {

@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"}) public DataSource d


// instantiate, configure and return DataSource bean...
}
}

Kotlin
@Configuration class AppConfig {

@Bean("dataSource", "subsystemA-dataSource", "subsystemB-dataSource") fun dataSource(): Data


// instantiate, configure and return DataSource bean...
}
}

Bean Description

Sometimes, it is helpful to provide a more detailed textual description of a bean.


This can be particularly useful when beans are exposed (perhaps through JMX) for
monitoring purposes.

To add a description to a @Bean, you can use the @Description annotation, as the
following example shows:

24
Java
@Configuration
public class AppConfig {

@Bean
@Description("Provides a basic example of a bean") public Thing thing() {
return new Thing();
}
}

Kotlin
@Configuration class AppConfig {

@Bean
@Description("Provides a basic example of a bean") fun thing() = Thing()
}

Using the @Configuration annotation

@Configuration is a class-level annotation indicating that an object is a source of bean


definitions. @Configuration classes declare beans through @Bean-annotated methods.
Calls to @Bean methods on @Configuration classes can also be used to define inter-bean
dependencies. See Basic Concepts: @Bean and @Configuration for a general
introduction.

Injecting Inter-bean Dependencies

When beans have dependencies on one another, expressing that dependency is as


simple as having one bean method call another, as the following example shows:

Java
@Configuration
public class AppConfig {

@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}

@Bean
public BeanTwo beanTwo() { return new BeanTwo();
}
}

25
Kotlin
@Configuration class AppConfig {

@Bean
fun beanOne() = BeanOne(beanTwo())

@Bean
fun beanTwo() = BeanTwo()
}

In the preceding example, beanOne receives a reference to beanTwo through


constructor injection.

This method of declaring inter-bean dependencies works only when


the @Bean method is declared within a @Configuration class. You cannot
declare inter-bean dependencies by using plain @Component classes.

Lookup Method Injection

As noted earlier, lookup method injection is an advanced feature that you should use
rarely. It is useful in cases where a singleton-scoped bean has a dependency on a
prototype-scoped bean. Using Java for this type of configuration provides a natural
means for implementing this pattern. The following example shows how to use lookup
method injection:

Java
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface Command command = createComma
// set the state on the (hopefully brand new) Command instance command.setState(commandState
return command.execute();
}

// okay... but where is the implementation of this method? protected abstract Command createCom
}

26
Kotlin
abstract class CommandManager {
fun process(commandState: Any): Any {
// grab a new instance of the appropriate Command interface val command = createCommand()
// set the state on the (hopefully brand new) Command instance command.setState(commandState
return command.execute()
}

// okay... but where is the implementation of this method? protected abstract fun createCommand(
}

By using Java configuration, you can create a subclass of CommandManager where


the abstract createCommand() method is overridden in such a way that it looks up a
new (prototype) command object. The following example shows how to do so:

Java
@Bean @Scope("prototype")
public AsyncCommand asyncCommand() { AsyncCommand command = new AsyncCommand();
// inject dependencies here as required return command;
}

@Bean
public CommandManager commandManager() {
// return new anonymous implementation of CommandManager with createCommand()
// overridden to return a new prototype Command object return new CommandManager() {
protected Command createCommand() { return asyncCommand();
}
}
}

27
Kotlin
@Bean @Scope("prototype")
fun asyncCommand(): AsyncCommand { val command = AsyncCommand()
// inject dependencies here as required return command
}

@Bean
fun commandManager(): CommandManager {
// return new anonymous implementation of CommandManager with createCommand()
// overridden to return a new prototype Command object return object : CommandManager() {
override fun createCommand(): Command { return asyncCommand()
}
}
}

Further Information About How Java-based Configuration Works Internally

Consider the following example, which shows a @Bean annotated method being called
twice:

Java
@Configuration
public class AppConfig {

@Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl(); clientService.setClientDao(clientDao());
return clientService;
}

@Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl(); clientService.setClientDao(clientDao());
return clientService;
}

@Bean
public ClientDao clientDao() { return new ClientDaoImpl();
}
}

28
Kotlin
@Configuration class AppConfig {

@Bean
fun clientService1(): ClientService { return ClientServiceImpl().apply {
clientDao = clientDao()
}
}

@Bean
fun clientService2(): ClientService { return ClientServiceImpl().apply {
clientDao = clientDao()
}
}

@Bean
fun clientDao(): ClientDao { return ClientDaoImpl()
}
}

clientDao() has been called once in clientService1() and once in clientService2(). Since
this method creates a new instance of ClientDaoImpl and returns it, you would normally
expect to have two instances (one for each service). That definitely would be
problematic: In Spring, instantiated beans have a singleton scope by default. This is
where the magic comes in: All @Configuration classes are subclassed at startup-time
with CGLIB. In the subclass, the child method checks the container first for any cached
(scoped) beans before it calls the parent method and creates a new instance.

The behavior could be different according to the scope of your bean.


We are talking about singletons here.

As of Spring 3.2, it is no longer necessary to add CGLIB to your classpath


because CGLIB classes have been repackaged under
org.springframework.cglib and included directly within the spring-core
JAR.

29
There are a few restrictions due to the fact that CGLIB dynamically
adds features at startup-time. In particular, configuration classes
must not be final. However, as of 4.3, any constructors are allowed on
configuration classes, including the use of @Autowired or a single non-
default constructor declaration for default injection.

If you prefer to avoid any CGLIB-imposed limitations, consider

declaring your
@Bean methods on non-@Configuration classes (for example, on plain
@Component classes instead). Cross-method calls between @Bean
methods are not then intercepted, so you have to exclusively rely on
dependency injection at the constructor or method level there.

Composing Java-based Configurations

Spring’s Java-based configuration feature lets you compose annotations, which can
reduce the complexity of your configuration.

Using the @Import Annotation

Much as the <import/> element is used within Spring XML files to aid in
modularizing configurations, the @Import annotation allows for loading @Bean
definitions from another configuration class, as the following example shows:

Java
@Configuration
public class ConfigA {

@Bean
public A a() { return new A();
}
}

@Configuration @Import(ConfigA.class) public class ConfigB {

@Bean
public B b() { return new B();
}
}

30
Kotlin
@Configuration class ConfigA {

@Bean
fun a() = A()
}

@Configuration @Import(ConfigA::class) class ConfigB {

@Bean
fun b() = B()
}

Now, rather than needing to specify both ConfigA.class and ConfigB.class when
instantiating the context, only ConfigB needs to be supplied explicitly, as the following
example shows:

Java
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

// now both beans A and B will be available... A a = ctx.getBean(A.class);


B b = ctx.getBean(B.class);
}

Kotlin
import org.springframework.beans.factory.getBean

fun main() {
val ctx = AnnotationConfigApplicationContext(ConfigB::class.java)

// now both beans A and B will be available... val a = ctx.getBean<A>()


val b = ctx.getBean<B>()
}

This approach simplifies container instantiation, as only one class needs to be dealt
with, rather than requiring you to remember a potentially large number of
@Configuration classes during construction.

31
As of Spring Framework 4.2, @Import also supports references to
regular component classes, analogous to the
AnnotationConfigApplicationContext.register method. This is particularly
useful if you want to avoid component scanning, by using a few
configuration classes as entry points to explicitly define all your
components.

Injecting Dependencies on Imported @Bean Definitions

The preceding example works but is simplistic. In most practical scenarios, beans
have dependencies on one another across configuration classes. When using XML,
this is not an issue, because no compiler is involved, and you can declare
ref="someBean" and trust Spring to work it out during container initialization. When
using @Configuration classes, the Java compiler places constraints on the
configuration model, in that references to other beans must be valid Java syntax.

Fortunately, solving this problem is simple. As we already discussed, a @Bean method


can have an arbitrary number of parameters that describe the bean dependencies.
Consider the following more real-world scenario with several @Configuration classes,
each depending on beans declared in the others:

32
Java

@Configuration
public class ServiceConfig {

@Bean
public TransferService transferService(AccountRepository
accountRepository) { return new
TransferServiceImpl(accountRepository);
}
}

@Configuration
public class RepositoryConfig {

@Bean
public AccountRepository accountRepository(DataSource
dataSource) { return new
JdbcAccountRepository(dataSource);
}
}

@Configuration
@Import({ServiceConfig.class,
RepositoryConfig.class}) public class
SystemTestConfig {

@Bean
public DataSource dataSource() {
// return new DataSource
}
}

public static void main(String[] args)


{ ApplicationContext ctx = new
AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService =
ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}

33
Kotlin

import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

@Bean
fun transferService(accountRepository: AccountRepository):
TransferService { return TransferServiceImpl(accountRepository)
}
}

@Configuration
class RepositoryConfig {

@Bean
fun accountRepository(dataSource: DataSource):
AccountRepository { return
JdbcAccountRepository(dataSource)
}
}

@Configuration
@Import(ServiceConfig::class,
RepositoryConfig::class) class SystemTestConfig {

@Bean
fun dataSource(): DataSource {
// return new DataSource
}
}

fun main() {
val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
// everything wires up across configuration classes...
val transferService =
ctx.getBean<TransferService>()
transferService.transfer(100.00, "A123", "C456")
}

There is another way to achieve the same result. Remember that @Configuration classes
are ultimately only another bean in the container: This means that they can take
advantage of @Autowired and @Value injection and other features the same as any
other bean.

34
Make sure that the dependencies you inject that way are of the
simplest kind only. @Configuration classes are processed quite early
during the initialization of the context, and forcing a dependency to be
injected this way may lead to unexpected early initialization.
Whenever possible, resort to parameter-based injection, as in the
preceding example.

Also, be particularly careful with BeanPostProcessor and


BeanFactoryPostProcessor definitions through @Bean. Those should
usually be declared as static @Bean methods, not triggering the
instantiation of their containing configuration class.
Otherwise, @Autowired and @Value may not work on the configuration
class itself, since it is possible to create it as a bean instance earlier than
AutowiredAnnotationBeanPostProcessor.

The following example shows how one bean can be autowired to another bean:

35
Java

@Configuration
public class ServiceConfig {

@Autowired
private AccountRepository accountRepository;

@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}

@Configuration
public class RepositoryConfig {

private final DataSource dataSource;

public RepositoryConfig(DataSource
dataSource) { this.dataSource =
dataSource;
}

@Bean
public AccountRepository accountRepository() {
return new
JdbcAccountRepository(dataSource);
}
}

@Configuration
@Import({ServiceConfig.class,
RepositoryConfig.class}) public class
SystemTestConfig {

@Bean
public DataSource dataSource() {
// return new DataSource
}
}

public static void main(String[] args) {


ApplicationContext ctx = new
AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService =
ctx.getBean(TransferService.class); transferService.transfer(100.00,
"A123", "C456");
}

36
Kotlin

import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

@Autowired
lateinit var accountRepository: AccountRepository

@Bean
fun transferService(): TransferService {
return TransferServiceImpl(accountRepository)
}
}

@Configuration
class RepositoryConfig(private val dataSource: DataSource) {

@Bean
fun accountRepository():
AccountRepository { return
JdbcAccountRepository(dataSource)
}
}

@Configuration
@Import(ServiceConfig::class,
RepositoryConfig::class) class SystemTestConfig {

@Bean
fun dataSource(): DataSource {
// return new DataSource
}
}

fun main() {
val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
// everything wires up across configuration classes...
val transferService =
ctx.getBean<TransferService>()
transferService.transfer(100.00, "A123", "C456")
}

Constructor injection in @Configuration classes is only supported as of


Spring Framework 4.3. Note also that there is no need to specify
@Autowired if the target bean defines only one constructor.

Fully-qualifying imported beans for ease of navigation


In the preceding scenario, using @Autowired works well and provides the desired
modularity, but determining exactly where the autowired bean definitions are
declared is still somewhat

37
ambiguous. For example, as a developer looking at ServiceConfig, how do you know
exactly where the @Autowired AccountRepository bean is declared? It is not explicit in
the code, and this may be just fine. Remember that the Spring Tools for Eclipse
provides tooling that can render graphs showing how everything is wired, which may
be all you need. Also, your Java IDE can easily find all declarations and uses of the
AccountRepository type and quickly show you the location of @Bean methods that return
that type.

In cases where this ambiguity is not acceptable and you wish to have direct
navigation from within your IDE from one @Configuration class to another, consider
autowiring the configuration classes themselves. The following example shows how to
do so:

Java
@Configuration
public class ServiceConfig {

@Autowired
private RepositoryConfig repositoryConfig;

@Bean
public TransferService transferService() {
// navigate 'through' the config class to the @Bean method!
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}

Kotlin
@Configuration
class ServiceConfig {

@Autowired
private lateinit var repositoryConfig: RepositoryConfig

@Bean
fun transferService(): TransferService {
// navigate 'through' the config class to the @Bean method! return TransferServiceImpl(repositoryC
}
}

In the preceding situation, where AccountRepository is defined is completely explicit.


However, ServiceConfig is now tightly coupled to RepositoryConfig. That is the tradeoff.
This tight coupling can be somewhat mitigated by using interface-based or abstract
class-based @Configuration classes. Consider the following example:

38
Java

@Configuration
public class ServiceConfig {

@Autowired
private RepositoryConfig repositoryConfig;

@Bean
public TransferService transferService() {
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}

@Configuration
public interface RepositoryConfig {

@Bean
AccountRepository accountRepository();
}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

@Bean
public AccountRepository
accountRepository() { return new
JdbcAccountRepository(...);
}
}

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the
concrete config!
public class SystemTestConfig {

@Bean
public DataSource dataSource() {
// return DataSource
}

public static void main(String[] args) {


ApplicationContext ctx = new
AnnotationConfigApplicationContext(SystemTestConfig.class);
TransferService transferService =
ctx.getBean(TransferService.class); transferService.transfer(100.00,
"A123", "C456");
}

39
Kotlin

import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

@Autowired
private lateinit var repositoryConfig: RepositoryConfig

@Bean
fun transferService(): TransferService {
return TransferServiceImpl(repositoryConfig.accountRepository())
}
}

@Configuration
interface RepositoryConfig {

@Bean
fun accountRepository(): AccountRepository
}

@Configuration
class DefaultRepositoryConfig : RepositoryConfig {

@Bean
fun accountRepository():
AccountRepository { return
JdbcAccountRepository(...)
}
}

@Configuration
@Import(ServiceConfig::class, DefaultRepositoryConfig::class) // import the concrete
config!
class SystemTestConfig {

@Bean
fun dataSource(): DataSource {
// return DataSource
}

fun main() {
val ctx =
AnnotationConfigApplicationContext(SystemTestConfig::class.java) val
transferService = ctx.getBean<TransferService>()
transferService.transfer(100.00, "A123", "C456")
}

40
Now ServiceConfig is loosely coupled with respect to the concrete
DefaultRepositoryConfig, and built-in IDE tooling is still useful: You can easily get a type
hierarchy of RepositoryConfig implementations. In this way, navigating @Configuration
classes and their dependencies becomes no different than the usual process of
navigating interface-based code.

If you want to influence the startup creation order of certain beans,


consider declaring some of them as @Lazy (for creation on first access
instead of on startup) or as @DependsOn certain other beans (making
sure that specific other beans are created before the current bean,
beyond what the latter’s direct dependencies imply).

Conditionally Include @Configuration Classes or @Bean Methods

It is often useful to conditionally enable or disable a complete @Configuration class


or even individual @Bean methods, based on some arbitrary system state. One common
example of this is to use the @Profile annotation to activate beans only when a specific
profile has been enabled in the Spring Environment (see Bean Definition Profiles for
details).

The @Profile annotation is actually implemented by using a much more flexible


annotation called @Conditional. The
@Conditional annotation indicates
specific org.springframework.context.annotation.Condition
implementations that should be consulted before a @Bean is registered.

Implementations of the Condition interface provide a matches(…) method that returns


true or false. For example, the following listing shows the actual Condition
implementation used for @Profile:

Java
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// Read the @Profile annotation attributes MultiValueMap<String, Object> attrs =
metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) { return true;
}
}
return false;
}
return true;
}

41
Kotlin
override fun matches(context: ConditionContext, metadata: AnnotatedTypeMetadata): Boolean {
// Read the @Profile annotation attributes
val attrs = metadata.getAllAnnotationAttributes(Profile::class.java.name) if (attrs != null) {
for (value in attrs["value"]!!) {
if (context.environment.acceptsProfiles(Profiles.of(*value as Array<String>))) {
return true
}
}
return false
}
return true
}

See the @Conditional javadoc for more detail.

Combining Java and XML Configuration

Spring’s @Configuration class support does not aim to be a 100% complete replacement
for Spring XML. Some facilities, such as Spring XML namespaces, remain an ideal way
to configure the container. In cases where XML is convenient or necessary, you have a
choice: either instantiate the container in an “XML-centric” way by using, for example,
ClassPathXmlApplicationContext, or instantiate it in a “Java-centric” way by using
AnnotationConfigApplicationContext and the @ImportResource annotation to import XML
as needed.

XML-centric Use of @Configuration Classes

It may be preferable to bootstrap the Spring container from XML and include
@Configuration classes in an ad-hoc fashion. For example, in a large existing
codebase that uses Spring XML, it is easier to create @Configuration classes on an as-
needed basis and include them from the existing XML files. Later in this section, we
cover the options for using @Configuration classes in this kind of “XML- centric”
situation.

Declaring @Configuration classes as plain Spring <bean/> elements


Remember that @Configuration classes are ultimately bean definitions in the container.
In this series examples, we create a @Configuration class named AppConfig and include
it within system- test-config.xml as a <bean/> definition. Because <context:annotation-
config/> is switched on, the container recognizes the @Configuration annotation and
processes the @Bean methods declared in AppConfig properly.

The following example shows an ordinary configuration class in Java:

42
Java
@Configuration
public class AppConfig {

@Autowired
private DataSource dataSource;

@Bean
public AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource);
}

@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}

Kotlin
@Configuration class AppConfig {

@Autowired
private lateinit var dataSource: DataSource

@Bean
fun accountRepository(): AccountRepository { return JdbcAccountRepository(dataSource)
}

@Bean
fun transferService() = TransferService(accountRepository())
}

The following example shows part of a sample system-test-config.xml file:

43
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

<bean class="com.acme.AppConfig"/>

<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>

The following example shows a possible jdbc.properties file:

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa
jdbc.password=

Java
public static void main(String[] args) { ApplicationContext ctx = new
ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml"); TransferService t
// ...
}

Kotlin
fun main() {
val ctx = ClassPathXmlApplicationContext("classpath:/com/acme/system-test- config.xml")
val transferService = ctx.getBean<TransferService>()
// ...
}

In system-test-config.xml file, the AppConfig <bean/> does not declare an


id element. While it would be acceptable to do so, it is unnecessary,
given that no other bean ever refers to it, and it is unlikely to be
explicitly fetched from the container by name. Similarly, the DataSource
bean is only ever autowired by type, so an explicit bean id is not strictly
required.

Using <context:component-scan/> to pick up @Configuration classes


Because @Configuration is meta-annotated with @Component, @Configuration-
annotated classes are

44
automatically candidates for component scanning. Using the same scenario as described
in the previous example, we can redefine system-test-config.xml to take advantage of
component- scanning. Note that, in this case, we need not explicitly declare
<context:annotation-config/>, because <context:component-scan/> enables the same
functionality.

The following example shows the modified system-test-config.xml file:

<beans>
<!-- picks up and registers AppConfig as a bean definition -->
<context:component-scan base-package="com.acme"/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>

@Configuration Class-centric Use of XML with @ImportResource

In applications where @Configuration classes are the primary mechanism for


configuring the container, it is still likely necessary to use at least some XML. In
these scenarios, you can use @ImportResource and define only as much XML as you
need. Doing so achieves a “Java-centric” approach to configuring the container and
keeps XML to a bare minimum. The following example (which includes a
configuration class, an XML file that defines a bean, a properties file, and the main
class) shows how to use the @ImportResource annotation to achieve “Java-centric”
configuration that uses XML as needed:

Java
@Configuration @ImportResource("classpath:/com/acme/properties-config.xml") public class AppCo

@Value("${jdbc.url}") private String url;

@Value("${jdbc.username}") private String username;

@Value("${jdbc.password}") private String password;

@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}

45
Kotlin
@Configuration @ImportResource("classpath:/com/acme/properties-config.xml") class AppConfig {

@Value("\${jdbc.url}")
private lateinit var url: String

@Value("\${jdbc.username}")
private lateinit var username: String

@Value("\${jdbc.password}")
private lateinit var password: String

@Bean
fun dataSource(): DataSource {
return DriverManagerDataSource(url, username, password)
}
}

properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>

jdbc.properties jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa


jdbc.password=

Java
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); TransferServic
// ...
}

46
Kotlin
import org.springframework.beans.factory.getBean

fun main() {
val ctx = AnnotationConfigApplicationContext(AppConfig::class.java) val transferService = ctx.getB
// ...
}

2.1.13. Environment Abstraction


The Environment interface is an abstraction integrated in the container that models
two key aspects of the application environment: profiles and properties.

A profile is a named, logical group of bean definitions to be registered with the


container only if the given profile is active. Beans may be assigned to a profile
whether defined in XML or with annotations. The role of the Environment object with
relation to profiles is in determining which profiles (if any) are currently active, and
which profiles (if any) should be active by default.

Properties play an important role in almost all applications and may originate from a
variety of sources: properties files, JVM system properties, system environment
variables, JNDI, servlet context parameters, ad-hoc Properties objects, Map objects, and
so on. The role of the Environment object with relation to properties is to provide the
user with a convenient service interface for configuring property sources and
resolving properties from them.

Bean Definition Profiles

Bean definition profiles provide a mechanism in the core container that allows for
registration of different beans in different environments. The word, “environment,”
can mean different things to different users, and this feature can help with many
use cases, including:

• Working against an in-memory datasource in development versus looking up


that same datasource from JNDI when in QA or production.

• Registering monitoring infrastructure only when deploying an application into a


performance environment.

• Registering customized implementations of beans for customer A versus


customer B deployments.

Consider the first use case in a practical application that requires a DataSource. In a
test environment, the configuration might resemble the following:

47
Java
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("my-schema.sql")
.addScript("my-test-data.sql")
.build();
}

Kotlin
@Bean
fun dataSource(): DataSource { return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("my-schema.sql")
.addScript("my-test-data.sql")
.build()
}

Now consider how this application can be deployed into a QA or production


environment, assuming that the datasource for the application is registered with the
production application server’s JNDI directory. Our dataSource bean now looks like the
following listing:

Java
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception { Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}

Kotlin
@Bean(destroyMethod = "")
fun dataSource(): DataSource { val ctx = InitialContext()
return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
}

The problem is how to switch between using these two variations based on the
current environment. Over time, Spring users have devised a number of ways to get
this done, usually relying on a combination of system environment variables and
XML <import/> statements containing ${placeholder} tokens that resolve to the
correct configuration file path depending on the value of an environment variable.
Bean definition profiles is a core container feature that provides a solution to this
problem.

48
If we generalize the use case shown in the preceding example of environment-
specific bean definitions, we end up with the need to register certain bean
definitions in certain contexts but not in others. You could say that you want to
register a certain profile of bean definitions in situation A and a different profile in
situation B. We start by updating our configuration to reflect this need.

Using @Profile

The @Profile annotation lets you indicate that a component is eligible for registration
when one or more specified profiles are active. Using our preceding example, we can
rewrite the dataSource configuration as follows:

Java
@Configuration @Profile("development")
public class StandaloneDataConfig {

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}

Kotlin
@Configuration @Profile("development") class StandaloneDataConfig {

@Bean
fun dataSource(): DataSource { return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build()
}
}

49
Java
@Configuration @Profile("production")
public class JndiDataConfig {

@Bean(destroyMethod="")
public DataSource dataSource() throws Exception { Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}

Kotlin
@Configuration @Profile("production") class JndiDataConfig {

@Bean(destroyMethod = "")
fun dataSource(): DataSource { val ctx = InitialContext()
return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
}
}

As mentioned earlier, with @Bean methods, you typically choose to


use programmatic JNDI lookups, by using either Spring’s
JndiTemplate
/JndiLocatorDelegate helpers or the straight JNDI InitialContext usage
shown earlier but not the JndiObjectFactoryBean variant, which would
force you to declare the return type as the FactoryBean type.

The profile string may contain a simple profile name (for example, production) or a
profile expression. A profile expression allows for more complicated profile logic to
be expressed (for example, production & us-east). The following operators are
supported in profile expressions:

• !: A logical “not” of the profile


• &: A logical “and” of the profiles
• |: A logical “or” of the profiles

You cannot mix the & and | operators without using parentheses. For
example, production & us-east | eu-central is not a valid expression. It must
be expressed as production & (us-east | eu-central).

You can use @Profile as a meta-annotation for the purpose of creating a custom
composed annotation. The following example defines a custom @Production annotation
that you can use as a drop-in replacement for @Profile("production"):

50
Java
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Profile("production")
public @interface Production {
}

Kotlin
@Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @Profile("production
annotation class Production

If a @Configuration class is marked with @Profile, all of the @Bean


methods and @Import annotations associated with that class are
bypassed unless one or more of the specified profiles are active. If a
@Component or @Configuration class is marked with @Profile({"p1",
"p2"}), that class is not registered or processed unless profiles 'p1' or
'p2' have been activated. If a given profile is prefixed with the NOT
operator (!), the annotated element is registered only if the profile is
not active. For example, given @Profile({"p1", "!p2"}), registration will
occur if profile 'p1' is active or if profile 'p2' is not active.

@Profile can also be declared at the method level to include only one particular
bean of a configuration class (for example, for alternative variants of a particular
bean), as the following example shows:

51
Java
@Configuration
public class AppConfig {

@Bean("dataSource") @Profile("development") ①
public DataSource standaloneDataSource() { return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}

@Bean("dataSource") @Profile("production") Ⓒ
public DataSource jndiDataSource() throws Exception { Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}

① The standaloneDataSource method is available only in the development profile.


Ⓒ The jndiDataSource method is available only in the production profile.

Kotlin
@Configuration class AppConfig {

@Bean("dataSource") @Profile("development") ①
fun standaloneDataSource(): DataSource { return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build()
}

@Bean("dataSource") @Profile("production") Ⓒ fun jndiDataSource() =


InitialContext().lookup("java:comp/env/jdbc/datasource") as DataSource
}

① The standaloneDataSource method is available only in the development profile.


Ⓒ The jndiDataSource method is available only in the production profile.

52
With @Profile on @Bean methods, a special scenario may apply: In the
case of overloaded @Bean methods of the same Java method name
(analogous to constructor overloading), a @Profile condition needs to be
consistently declared on all overloaded methods. If the conditions are
inconsistent, only the condition on the first declaration among the
overloaded methods matters. Therefore, @Profile can not be used to
select an overloaded method with a particular argument signature
over another. Resolution between all factory methods for the same
bean

follows Spring’s constructor resolution algorithm at creation time.


If you want to define alternative beans with different profile
conditions, use distinct Java method names that point to the same
bean name by using the @Bean name attribute, as shown in the
preceding example. If the argument signatures are all the same (for
example, all of the variants have no-arg factory methods), this is the
only way to represent such an arrangement in a valid Java class in the
first place (since there can only be one method of a particular name
and argument signature).

XML Bean Definition Profiles

The XML counterpart is the profile attribute of the <beans> element. Our
preceding sample configuration can be rewritten in two XML files, as follows:

<beans profile="development" xsi:schemaLocation="...">

<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>

<beans profile="production" xsi:schemaLocation="...">

<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>


</beans>

It is also possible to avoid that split and nest <beans/> elements within the
same file, as the following example shows:

53
<beans
xmlns="http://www.springframework.org/schema/bea
ns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"
xmlns:jdbc="http://www.springframework.org/schema
/jdbc"
xmlns:jee="http://www.springframework.org/schema/j
ee" xsi:schemaLocation="...">

<!-- other bean definitions -->

<beans profile="development">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>

<beans profile="production">
<jee:jndi-lookup id="dataSource"
jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>

The spring-bean.xsd has been constrained to allow such elements only as the last ones
in the file. This should help provide flexibility without incurring clutter in the XML files.

The XML counterpart does not support the profile expressions


described earlier. It is possible, however, to negate a profile by using
the ! operator. It is also possible to apply a logical “and” by nesting
the profiles, as the following example shows:

<beans xsi:schemaLocation="...">

<!-- other bean definitions -->

<beans profile="production">
<beans profile="us-east">
<jee:jndi-lookup id="dataSource" jndi- name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>
</beans>

In the preceding example, the dataSource bean is exposed if both the


production
and us-east profiles are active.

54
Activating a Profile

Now that we have updated our configuration, we still need to instruct Spring which
profile is active. If we started our sample application right now, we would see a
NoSuchBeanDefinitionException thrown, because the container could not find the
Spring bean named dataSource.

Activating a profile can be done in several ways, but the most straightforward is to
do it programmatically against the Environment API which is available through an
ApplicationContext. The following example shows how to do so:

Java
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnviro

Kotlin
val ctx = AnnotationConfigApplicationContext().apply { environment.setActiveProfiles("developmen
JndiDataConfig::class.java) refresh()
}

In addition, you can also declaratively activate profiles through the


spring.profiles.active property, which may be specified through system environment
variables, JVM system properties, servlet context parameters in web.xml, or even as an
entry in JNDI (see PropertySource Abstraction). In integration tests, active profiles can
be declared by using the @ActiveProfiles annotation in the spring-test module (see
context configuration with environment profiles).

Note that profiles are not an “either-or” proposition. You can activate multiple profiles
at once. Programmatically, you can provide multiple profile names to the
setActiveProfiles() method, which accepts String… varargs. The following example
activates multiple profiles:

Java
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");

Kotlin
ctx.getEnvironment().setActiveProfiles("profile1", "profile2")

Declaratively, spring.profiles.active may accept a comma-separated list of profile


names, as the following example shows:

55
-Dspring.profiles.active="profile1,profile2"

Default Profile

The default profile represents the profile that is enabled by default. Consider the
following example:

Java
@Configuration @Profile("default")
public class DefaultDataConfig {

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}

Kotlin
@Configuration @Profile("default")
class DefaultDataConfig {

@Bean
fun dataSource(): DataSource { return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build()
}
}

If no profile is active, the dataSource is created. You can see this as a way to
provide a default definition for one or more beans. If any profile is enabled, the
default profile does not apply.

You can change the name of the default profile by using setDefaultProfiles() on the
Environment or, declaratively, by using the spring.profiles.default property.

PropertySource Abstraction

Spring’s Environment abstraction provides search operations over a configurable


hierarchy of property sources. Consider the following listing:

56
Java
ApplicationContext ctx = new GenericApplicationContext(); Environment env = ctx.getEnvironment
boolean containsMyProperty = env.containsProperty("my-property"); System.out.println("Does my

Kotlin
val ctx = GenericApplicationContext() val env = ctx.environment
val containsMyProperty = env.containsProperty("my-property")
println("Does my environment contain the 'my-property' property? $containsMyProperty")

In the preceding snippet, we see a high-level way of asking Spring whether the my-
property property is defined for the current environment. To answer this question, the
Environment object performs a search over a set of PropertySource objects. A
PropertySource is a simple abstraction over any source of key-value pairs, and
Spring’s StandardEnvironment is configured with two PropertySource objects — one
representing the set of JVM system properties (System.getProperties()) and one
representing the set of system environment variables (System.getenv()).

These default property sources are present for StandardEnvironment,


for use in standalone applications. StandardServletEnvironment is
populated with additional default property sources including servlet
config, servlet context parameters, and a JndiPropertySource if JNDI is
available.

Concretely, when you use the StandardEnvironment, the call to


env.containsProperty("my-property") returns true if a my-property system property or
my-property environment variable is present at runtime.

57
The search performed is hierarchical. By default, system properties
have precedence over environment variables. So, if the my-property
property happens to be set in both places during a call to
env.getProperty("my-property"), the system property value “wins” and is
returned. Note that property values are not merged but rather
completely overridden by a preceding entry.

For a common StandardServletEnvironment, the full hierarchy is as


follows, with the highest-precedence entries at the top:

1. ServletConfig parameters (if applicable — for


example, in case of a
DispatcherServlet context)

2. ServletContext parameters (web.xml context-param entries)

3. JNDI environment variables (java:comp/env/ entries)


4. JVM system properties (-D command-line arguments)

5. JVM system environment (operating system environment variables)

Most importantly, the entire mechanism is configurable. Perhaps you have a custom
source of properties that you want to integrate into this search. To do so, implement
and instantiate your own PropertySource and add it to the set of PropertySources for
the current Environment. The following example shows how to do so:

Java
ConfigurableApplicationContext ctx = new GenericApplicationContext(); MutablePropertySources so

Kotlin
val ctx = GenericApplicationContext()
val sources = ctx.environment.propertySources sources.addFirst(MyPropertySource())

In the preceding code, MyPropertySource has been added with highest precedence in the
search. If it contains a my-property property, the property is detected and returned, in
favor of any my-property property in any other PropertySource. The
MutablePropertySources API exposes a number of methods that allow for precise
manipulation of the set of property sources.

Using @PropertySource

The @PropertySource annotation provides a convenient and declarative mechanism


for adding a
PropertySource to Spring’s Environment.

Given a file called app.properties that contains the key-value pair


testbean.name=myTestBean, the following @Configuration class uses @PropertySource in
such a way that a call to testBean.getName() returns myTestBean:

58
Java
@Configuration @PropertySource("classpath:/com/myco/app.properties") public class AppConfig {

@Autowired Environment env;

@Bean
public TestBean testBean() {
TestBean testBean = new TestBean(); testBean.setName(env.getProperty("testbean.name")); retur
}
}

Kotlin
@Configuration @PropertySource("classpath:/com/myco/app.properties") class AppConfig {

@Autowired
private lateinit var env: Environment

@Bean
fun testBean() = TestBean().apply {
name = env.getProperty("testbean.name")!!
}
}

Any ${…} placeholders present in a @PropertySource resource location are resolved


against the set of property sources already registered against the environment, as the
following example shows:

59
Java
@Configuration @PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties"

@Autowired Environment env;

@Bean
public TestBean testBean() {
TestBean testBean = new TestBean(); testBean.setName(env.getProperty("testbean.name")); retur
}
}

Kotlin
@Configuration @PropertySource("classpath:/com/\${my.placeholder:default/path}/app.properties

@Autowired
private lateinit var env: Environment

@Bean
fun testBean() = TestBean().apply {
name = env.getProperty("testbean.name")!!
}
}

Assuming that my.placeholder is present in one of the property sources already


registered (for example, system properties or environment variables), the placeholder is
resolved to the corresponding value. If not, then default/path is used as a default. If no
default is specified and a property cannot be resolved, an IllegalArgumentException is
thrown.

The @PropertySource annotation is repeatable, according to Java 8


conventions. However, all such @PropertySource annotations need to be
declared at the same level, either directly on the configuration class or as
meta-annotations within the same custom annotation. Mixing direct
annotations and meta-annotations is not recommended, since direct
annotations effectively override meta-annotations.

Placeholder Resolution in Statements

Historically, the value of placeholders in elements could be resolved only against


JVM system properties or environment variables. This is no longer the case. Because
the Environment abstraction is integrated throughout the container, it is easy to
route resolution of placeholders through it. This means that you may configure the

60
resolution process in any way you like. You can change the

61
precedence of searching through system properties and environment variables or
remove them entirely. You can also add your own property sources to the mix, as
appropriate.

Concretely, the following statement works regardless of where the customer property
is defined, as long as it is available in the Environment:

<beans>
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>

2.1.14. Registering a LoadTimeWeaver


The LoadTimeWeaver is used by Spring to dynamically transform classes as they are
loaded into the Java virtual machine (JVM).

To enable load-time weaving, you can add the @EnableLoadTimeWeaving to one of your
@Configuration
classes, as the following example shows:

Java
@Configuration @EnableLoadTimeWeaving public class AppConfig {
}

Kotlin
@Configuration @EnableLoadTimeWeaving class AppConfig

Alternatively, for XML configuration, you can use the context:load-time-weaver element:

<beans>
<context:load-time-weaver/>
</beans>

Once configured for the ApplicationContext, any bean within that ApplicationContext
may implement LoadTimeWeaverAware, thereby receiving a reference to the load-time
weaver instance. This is particularly useful in combination with Spring’s JPA support
where load-time weaving may be necessary for JPA class transformation. Consult the
LocalContainerEntityManagerFactoryBean javadoc for more detail. For more on AspectJ
load-time weaving, see Load-time Weaving with AspectJ in the Spring Framework.

62
2.1.15. Additional Capabilities of the ApplicationContext
As discussed in the chapter introduction, the org.springframework.beans.factory package
provides basic functionality for managing and manipulating beans, including in a
programmatic way. The org.springframework.context package adds the
ApplicationContext interface, which extends the BeanFactory interface, in addition to
extending other interfaces to provide additional functionality in a more application
framework-oriented style. Many people use the ApplicationContext in a completely
declarative fashion, not even creating it programmatically, but instead relying on
support classes such as ContextLoader to automatically instantiate an ApplicationContext
as part of the normal startup process of a Jakarta EE web application.

To enhance BeanFactory functionality in a more framework-oriented style, the context


package also provides the following functionality:

• Access to messages in i18n-style, through the MessageSource interface.


• Access to resources, such as URLs and files, through the ResourceLoader interface.
• Event publication, namely to beans that implement the ApplicationListener interface,
through the use of the ApplicationEventPublisher interface.

• Loading of multiple (hierarchical) contexts, letting each be focused on one


particular layer, such as the web layer of an application, through the
HierarchicalBeanFactory interface.

Internationalization using MessageSource

The ApplicationContext interface extends an interface called MessageSource and,


therefore, provides internationalization (“i18n”) functionality. Spring also provides
the HierarchicalMessageSource interface, which can resolve messages hierarchically.
Together, these interfaces provide the foundation upon which Spring effects
message resolution. The methods defined on these interfaces include:

• String getMessage(String code, Object[] args, String default, Locale loc): The basic method
used to retrieve a message from the MessageSource. When no message is found for
the specified locale, the default message is used. Any arguments passed in become
replacement values, using the MessageFormat functionality provided by the standard
library.
• String getMessage(String code, Object[] args, Locale loc): Essentially the same as the
previous method but with one difference: No default message can be specified. If the
message cannot be found, a NoSuchMessageException is thrown.
• String getMessage(MessageSourceResolvable resolvable, Locale locale): All properties
used in the preceding methods are also wrapped in a class named
MessageSourceResolvable, which you can use with this method.

When an ApplicationContext is loaded, it automatically searches for a MessageSource


bean defined in the context. The bean must have the name messageSource. If such a
bean is found, all calls to the preceding methods are delegated to the message source. If
no message source is found, the ApplicationContext attempts to find a parent
containing a bean with the same name. If it does, it uses that bean as the
MessageSource. If the ApplicationContext cannot find any source for messages, an empty
DelegatingMessageSource is instantiated in order to be able to accept calls to the
methods defined above.

63
Spring provides three MessageSource implementations, ResourceBundleMessageSource,
ReloadableResourceBundleMessageSource and StaticMessageSource. All of them
implement HierarchicalMessageSource in order to do nested messaging. The
StaticMessageSource is rarely used but provides programmatic ways to add messages to
the source. The following example shows ResourceBundleMessageSource:

<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>

The example assumes that you have three resource bundles called format, exceptions
and windows defined in your classpath. Any request to resolve a message is handled
in the JDK-standard way of resolving messages through ResourceBundle objects. For
the purposes of the example, assume the contents of two of the above resource bundle
files are as follows:

# in format.properties message=Alligators rock!

# in exceptions.properties argument.required=The {0} argument is required.

The next example shows a program to run the MessageSource functionality. Remember
that all ApplicationContext implementations are also MessageSource implementations
and so can be cast to the MessageSource interface.

Java
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH); System.out.p
}

64
Kotlin
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("message", null, "Default", Locale.ENGLISH) println(message
}

The resulting output from the above program is as follows:

Alligators rock!

To summarize, the MessageSource is defined in a file called beans.xml, which exists at


the root of your classpath. The messageSource bean definition refers to a number of
resource bundles through its basenames property. The three files that are passed in the
list to the basenames property exist as files at the root of your classpath and are called
format.properties, exceptions.properties, and windows.properties, respectively.

The next example shows arguments passed to the message lookup. These
arguments are converted into String objects and inserted into placeholders in the
lookup message.

<beans>

<!-- this MessageSource is being used in a web application -->


<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessage
<property name="basename" value="exceptions"/>
</bean>

<!-- lets inject the above MessageSource into this POJO -->
<bean id="example" class="com.something.Example">
<property name="messages" ref="messageSource"/>
</bean>

</beans>

65
Java
public class Example {

private MessageSource messages;

public void setMessages(MessageSource messages) { this.messages = messages;


}

public void execute() {


String message = this.messages.getMessage("argument.required", new Object [] {"userDao"}, "Re
System.out.println(message);
}
}

Kotlin
class Example {
lateinit var messages: MessageSource fun execute() {
val message = messages.getMessage("argument.required", arrayOf("userDao"), "Required", Locale
println(message)
}
}

The resulting output from the invocation of the execute() method is as follows:

The userDao argument is required.

With regard to internationalization (“i18n”), Spring’s various MessageSource


implementations follow the same locale resolution and fallback rules as the standard
JDK ResourceBundle. In short, and continuing with the example messageSource defined
previously, if you want to resolve messages against the British (en-GB) locale, you would
create files called format_en_GB.properties, exceptions_en_GB.properties, and
windows_en_GB.properties, respectively.

Typically, locale resolution is managed by the surrounding environment of the


application. In the following example, the locale against which (British) messages
are resolved is specified manually:

# in exceptions_en_GB.properties
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.

66
Java
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); String message =
new Object [] {"userDao"}, "Required", Locale.UK); System.out.println(message);
}

Kotlin
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml") val message = resources.getMessag
arrayOf("userDao"), "Required", Locale.UK) println(message)
}

The resulting output from the running of the above program is as follows:

Ebagum lad, the 'userDao' argument is required, I say, required.

You can also use the MessageSourceAware interface to acquire a reference to any
MessageSource that has been defined. Any bean that is defined in an
ApplicationContext that implements the MessageSourceAware interface is injected with
the application context’s MessageSource when the bean is created and configured.

Because Spring’s MessageSource is based on Java’s ResourceBundle, it does


not merge bundles with the same base name, but will only use the first
bundle found. Subsequent message bundles with the same base name
are ignored.

As an alternative to ResourceBundleMessageSource, Spring provides a


ReloadableResourceBundleMessageSource class. This variant supports
the same bundle file format but is more flexible than the standard JDK
based ResourceBundleMessageSource implementation. In particular, it
allows for reading files from any Spring resource location (not only from
the classpath) and supports hot reloading of bundle property files
(while efficiently caching them in between). See the
ReloadableResourceBundleMessageSource javadoc for details.

Standard and Custom Events

Event handling in the ApplicationContext is provided through the ApplicationEvent class


and the ApplicationListener interface. If a bean that implements the ApplicationListener
interface is deployed into the context, every time an ApplicationEvent gets published to
the ApplicationContext, that bean is notified. Essentially, this is the standard Observer
design pattern.

67
As of Spring 4.2, the event infrastructure has been significantly
improved and offers an annotation-based model as well as the ability
to publish any arbitrary event (that is, an object that does not
necessarily extend from ApplicationEvent). When such an object is
published, we wrap it in an event for you.

The following table describes the standard events that Spring provides:

Table 7. Built-in Events

Event Explanation
ContextRefreshedEvent Published when the ApplicationContext is initialized or
refreshed (for example, by using the refresh() method on the
ConfigurableApplicationContext interface). Here, “initialized”
means that all beans are loaded, post-processor beans are
detected and activated, singletons are pre-instantiated, and
the ApplicationContext object is ready for use. As long as the
context has not been closed, a refresh can be triggered
multiple times, provided that the chosen ApplicationContext
actually supports such “hot” refreshes. For example,
XmlWebApplicationContext supports hot refreshes, but
GenericApplicationContext does not.
ContextStartedEvent Published when the ApplicationContext is started by using
the start() method on the ConfigurableApplicationContext
interface. Here, “started” means that all Lifecycle beans
receive an explicit start signal. Typically, this signal is used
to restart beans after an explicit stop, but it may also be
used to start components that have not been configured for
autostart (for example, components that have not already
started on initialization).
ContextStoppedEvent Published when the ApplicationContext is stopped by using
the stop() method on the ConfigurableApplicationContext
interface. Here, “stopped” means that all Lifecycle beans
receive an explicit stop signal. A stopped context may be
restarted through a start() call.

ContextClosedEvent Published when the ApplicationContext is being closed by


using the close() method on the
ConfigurableApplicationContext interface or via a JVM
shutdown hook. Here, "closed" means that all singleton
beans will be destroyed. Once the context is closed, it
reaches its end of life and cannot be refreshed or restarted.
RequestHandledEvent A web-specific event telling all beans that an HTTP request
has been serviced. This event is published after the request
is complete. This event is only applicable to web applications
that use Spring’s DispatcherServlet.
ServletRequestHandledEve A subclass of RequestHandledEvent that adds Servlet-specific
nt context information.

68
You can also create and publish your own custom events. The following example
shows a simple class that extends Spring’s ApplicationEvent base class:

69
Java
public class BlockedListEvent extends ApplicationEvent {

private final String address; private final String content;

public BlockedListEvent(Object source, String address, String content) { super(source);


this.address = address; this.content = content;
}

// accessor and other methods...


}

Kotlin
class BlockedListEvent(source: Any,
val address: String,
val content: String) : ApplicationEvent(source)

To publish a custom ApplicationEvent, call the publishEvent() method on an


ApplicationEventPublisher. Typically, this is done by creating a class that implements
ApplicationEventPublisherAware and registering it as a Spring bean. The following
example shows such a class:

70
Java
public class EmailService implements ApplicationEventPublisherAware {

private List<String> blockedList;


private ApplicationEventPublisher publisher;

public void setBlockedList(List<String> blockedList) { this.blockedList = blockedList;


}

public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = pu


}

public void sendEmail(String address, String content) { if (blockedList.contains(address)) {


publisher.publishEvent(new BlockedListEvent(this, address, content)); return;
}
// send email...
}
}

Kotlin
class EmailService : ApplicationEventPublisherAware {

private lateinit var blockedList: List<String>


private lateinit var publisher: ApplicationEventPublisher

fun setBlockedList(blockedList: List<String>) { this.blockedList = blockedList


}

override fun setApplicationEventPublisher(publisher: ApplicationEventPublisher) { this.publisher =


}

fun sendEmail(address: String, content: String) { if (blockedList!!.contains(address)) {


publisher!!.publishEvent(BlockedListEvent(this, address, content)) return
}
// send email...
}
}

At configuration time, the Spring container detects that EmailService


implements
ApplicationEventPublisherAware and automatically calls setApplicationEventPublisher(). In
reality,

71
the parameter passed in is the Spring container itself. You are interacting with the
application context through its ApplicationEventPublisher interface.

To receive the custom ApplicationEvent, you can create a class that implements
ApplicationListener
and register it as a Spring bean. The following example shows such a class:

Java
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> { private String
public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificati
}

public void onApplicationEvent(BlockedListEvent event) {


// notify appropriate parties via notificationAddress...
}
}

Kotlin
class BlockedListNotifier : ApplicationListener<BlockedListEvent> { lateinit var notificationAddress
override fun onApplicationEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
}

Notice that ApplicationListener is generically parameterized with the type of your


custom event (BlockedListEvent in the preceding example). This means that the
onApplicationEvent() method can remain type-safe, avoiding any need for downcasting.
You can register as many event listeners as you wish, but note that, by default,
event listeners receive events synchronously. This means that the publishEvent()
method blocks until all listeners have finished processing the event. One advantage of
this synchronous and single-threaded approach is that, when a listener receives an
event, it operates inside the transaction context of the publisher if a transaction
context is available. If another strategy for event publication becomes necessary, see
the javadoc for Spring’s ApplicationEventMulticaster interface and
SimpleApplicationEventMulticaster implementation for configuration options.

The following example shows the bean definitions used to register and configure
each of the classes above:

72
<bean id="emailService" class="example.EmailService">
<property name="blockedList">
<list>

</list>
</property>
</bean>

<bean id="blockedListNotifier" class="example.BlockedListNotifier">


<property name="notificationAddress"
</bean>

Putting it all together, when the sendEmail() method of the emailService bean is called,
if there are any email messages that should be blocked, a custom event of type
BlockedListEvent is published. The blockedListNotifier bean is registered as an
ApplicationListener and receives the BlockedListEvent, at which point it can notify
appropriate parties.

Spring’s eventing mechanism is designed for simple communication


between Spring beans within the same application context. However,
for more sophisticated enterprise integration needs, the separately
maintained Spring Integration project provides complete support for
building lightweight, pattern- oriented, event-driven architectures
that build upon the well-known Spring programming model.

Annotation-based Event Listeners

You can register an event listener on any method of a managed bean by using the
@EventListener
annotation. The BlockedListNotifier can be rewritten as follows:

Java
public class BlockedListNotifier { private String notificationAddress;
public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificati
}

@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}

73
Kotlin
class BlockedListNotifier {
lateinit var notificationAddress: String @EventListener
fun processBlockedListEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
}

The method signature once again declares the event type to which it listens, but,
this time, with a flexible name and without implementing a specific listener
interface. The event type can also be narrowed through generics as long as the
actual event type resolves your generic parameter in its implementation hierarchy.

If your method should listen to several events or if you want to define it with no
parameter at all, the event types can also be specified on the annotation itself. The
following example shows how to do so:

Java
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class}) public void handleCon
// ...
}

Kotlin
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class) fun handleContextStar
// ...
}

It is also possible to add additional runtime filtering by using the condition attribute
of the annotation that defines a SpEL expression, which should match to actually invoke
the method for a particular event.

The following example shows how our notifier can be rewritten to be invoked only if
the content
attribute of the event is equal to my-event:

Java
@EventListener(condition = "#blEvent.content == 'my-event'") public void processBlockedListEven
// notify appropriate parties via notificationAddress...
}

74

You might also like