Autoconfiguring Spring beans
Spring Boot comes with many features. But the most well-known one, by far, is autoconfiguration.
In essence, when a Spring Boot application starts up, it examines many parts of our application, including classpath
. Based on what the application sees, it automatically adds additional Spring beans to the application context.
Understanding application context
If you’re new to Spring, then it’s important to understand what we’re talking about when you hear application context.
Whenever a Spring Framework application starts up, whether or not Spring Boot is involved, it creates a container of sorts. Various Java beans that are registered with Spring Framework’s application context are known as Spring beans.
Tip
What’s a Java bean? Java beans are objects that follow a specific pattern: all the fields are private; they provide access to their fields through getters and setters, they have a no-argument constructor, and they implement the Serializable
interface.
For example, an object of the Video
type with name
and location
fields would set those two fields to private
and offer getName()
, getLocation()
, setName()
, and setLocation()
as the ways to mutate the state of this bean. On top of that, it would have a no-argument Video()
constructor call. It’s mostly a convention. Many tools provide property support by leveraging the getters and setters. The requirement to implement the Serializable
interface, though, is not as tightly enforced.
Spring Framework has a deep-seated concept known as dependency injection (DI), where a Spring bean can express its need for a bean of some other type. For example, a BookRepository
bean may require a DataSource
bean:
@Bean
public BookRepository bookRepository(DataSource dataSource) {
return new BookRepository(dataSource);
}
This preceding Java configuration, when seen by the Spring Framework, will cause the following flow of actions:
bookRepository
needs aDataSource
.- Ask the application context for a
DataSource
. - The application context either has it or will go create one and return it.
bookRepository
executes its code while referencing the app context’sDataSource
.BookRepository
is registered in the application context under the namebookRepository
.
The application context will ensure all Spring beans needed by the application are created and properly injected into each other. This is known as wiring.
Why all this instead of a handful of new operations in various class definitions? Simple. For the standard situation of powering up our app, all the beans are wired together as expected.
For a test case, it’s possible to override certain beans and switch to stubbed or mocked beans.
For cloud environments, it’s easy to find all DataSource
and replace them with beans that link to bound data services.
By removing the new operation from our example BookRepository
, and delegating that responsibility to the application context, we open the door to flexible options that make the whole life cycle of application development and maintenance much easier.
We’ll explore how Spring Boot heavily leverages the Spring Framework’s ability to inject beans based on various circumstances throughout this book. It is important to realize that Spring Boot doesn’t replace the Spring Framework but rather highly leverages it.
Now that you know what an application context is, it is time to dive into the many ways Spring Boot makes use of it through autoconfiguration.
Exploring autoconfiguration policies in Spring Boot
Spring Boot comes with a fistful of autoconfiguration policies. These are classes that contain @Bean
definitions that are only registered based on certain conditional circumstances. Perhaps an example is in order?
If Spring Boot detects the class definition of DataSource
somewhere on the classpath
, a class found inside any Java Database Connectivity (JDBC) driver, it will activate its DataSourceAutoConfiguration
. This policy will fashion some version of a DataSource
bean. This is driven by the @ConditionalOnClass({ DataSource.class })
annotation found on that policy.
Inside DataSourceAutoConfiguration
are inner classes, each driven by various factors. For example, some classes will discern whether or not we have used an embedded database such as H2 compared to a pooled JDBC asset such as HikariCP
.
And just like that, the need for us to configure an H2 DataSource
is removed. A small piece of infrastructure that is often the same across a multitude of applications is taken off our plate and instead managed by Spring Boot. And we can move more quickly toward writing business code that uses it.
Spring Boot autoconfiguration also has smart ordering built in, ensuring beans are added properly. Don’t worry! Using Spring Boot doesn’t depend on us having to know this level of detail.
Most of the time, we don’t have to know what Spring Boot is up to. It’s designed to do the right thing when various things are added to the build configuration.
The point is that many features, such as servlet handlers, view resolvers, data repositories, security filters, and more are activated, simply based on what dependencies we add to the build file.
And do you know what’s even better than automagically adding Spring beans? Backing off.
Some beans are created based on the classpath
settings. But if a certain bean definition is detected inside our code, the autoconfiguration won’t kick in.
Continuing with the example from earlier, if we put something such as H2
in our classpath
but define a DataSource
bean and register it in the application context, Spring Boot will accept our DataSource
bean over theirs.
No special hooks. No need to tell Spring Boot about it. Just create your own bean as you see fit, and Spring Boot will pick it up and run with it!
This may sound low-level, but Spring Boot’s autoconfiguration feature is transformational. If we focus on adding all the dependencies our project needs, Spring Boot will, as stated earlier, do what’s right.
Some of the autoconfiguration policies baked into Spring Boot extend across these areas:
- Spring AMQP: Communicate asynchronously using an Advanced Message Queueing Protocol (AMQP) message broker
- Spring AOP: Apply advice to code using Aspect-Oriented Programming
- Spring Batch: Process large volumes of content using batched jobs
- Spring Cache: Ease the load on services by caching results
- Data store connections (Apache Cassandra, Elasticsearch, Hazelcast, InfluxDB, JPA, MongoDB, Neo4j, Solr)
- Spring Data (Apache Cassandra, Couchbase, Elasticsearch, JDBC, JPA, LDAP, MongoDB, Neo4j, R2DBC, Redis, REST): Simplify data access
- Flyway: Database schema management
- Templating engines (Freemarker, Groovy, Mustache, Thymeleaf)
- Serialization/deserialization (Gson and Jackson)
- Spring HATEOAS: Add Hypermedia as the Engine of Application State (HATEOAS) or hypermedia to web services
- Spring Integration: Support integration rules
- Spring JDBC: Simplify accessing databases through JDBC
- Spring JMS: Asynchronous through Java Messaging Service (JMS)
- Spring JMX: Manage services through Java Management Extension (JMX)
- jOOQ: Query databases using Java Object Oriented Querying (jOOQ)
- Apache Kafka: Asynchronous messaging
- Spring LDAP: Directory-based services over Lightweight Directory Access Protocol (jOOQ)
- Liquibase: Database schema management
- Spring Mail: Publish emails
- Netty: An asynchronous web container (non-servlet-based)
- Quartz scheduling: Timed tasks
- Spring R2DBC: Access relational databases through Reactive Relational Database Connectivity (R2DBC)
- SendGrid: Publish emails
- Spring Session: Web session management
- Spring RSocket: Support for the async wire protocol known as RSocket
- Spring Validation: Bean validation
- Spring MVC: Spring’s workhorse for servlet-based web apps using the Model-View-Controller (MVC) paradigm
- Spring WebFlux: Spring’s reactive solution for web apps
- Spring Web Services: Simple Object Access Protocol (SOAP)-based services
- Spring WebSocket: Support for the WebSocket messaging web protocol
This is a general list and is by no means exhaustive. It’s meant to give us a glance at the breadth of Spring Boot.
And as cool as this set of policies and its various beans are, it’s lacking a few things that would make it perfect. For example, can you imagine managing the versions of all those libraries? And what about hooking in our own settings and components? We’ll cover these aspects in the next few sections.