One of the common misconceptions when it comes to Spring based Java applications is that these require a sheer amount of configuration before one can even start working on the actual domain problem that the application is suppose to solve. This is mainly because of XML configurations that were greatly reduced with annotations already. But still, if you want to set-up a web application as quickly as possible without Spring (XML) configuration files you need to download and configure a web server, set-up a database connection, then write all the required beans, persistence.xml for hibernate, web.xml, etc. Since what you actually wanted was to code the solution to your very own problem you start asking yourself whether it really has to be so complicated!?
Spring Boot aims to solve the problems listed above and enable fast bootstrapping of Spring applications. It is important to understand that Spring Boot is not a code generation tool. It can be rather seen as a timesaving conventionenabling autoconfigcreating beanmaking classpathshaking microcontainer. For example, if you have a Jetty dependency in the classpath, it will create a embedded Jetty server instance and run your application on it. If there is no web server dependency, it will run it as a standard Java application.
How it works
Spring Boot launches an application from a class which is annotated with@EnableAutoConfiguration. Here is one simple example:
Below you can see the lifecycle of Spring Boot in more detail:
During the initialisation phase Spring Boot verifies whether the application is meant to be run in a web environment based on dependencies found in its classpath. It basically scans the applications classpath for so called web environment types like Servlet andConfigurableWebApplicationContext. In case these are indeed found in the classpath, Spring Boot assumes that the developer wants to run it as a web application. After that all ApplicationListenerimplementations defined inside META-INF/spring.factories files throughout application’s classpath will be instantiated so that they can be notified with according events during the application lifecycle later on. These include at least those listener implementations already defined within the Spring Boot itself:
- ParentContextCloserApplicationListener – Listener that closes the application context if it’s parent is closed
- VcapApplicationListener – Listener for VCAP (Cloud Foundry) applications
- FileEncodingApplicationListener – an ApplicationListener that halts application startup if the system file encoding does not match an expected value set in the environment. By default has no effect, but if you set spring.mandatory_file_encoding (or some camelCase or UPPERCASE variant of that) to the name of a character encoding (e.g. “UTF-8″) then this initialiser throws an exception when the file.encoding System property does not equal it.
- ConfigFileApplicationListener – Listener in charge of loading application properties when the Spring Boot application starts.
- DelegatingApplicationListener – Registers all ApplicationListener implementations defined inapplication.properties file under context.listener.classes property. For examplecontext.listener.classes=mypackage.MyApplicationListener.
- LiquibaseServiceLocatorApplicationListener – Replaces the liquibase ServiceLocator with a version that works with Spring Boot executable archives.
- ClasspathLoggingApplicationListener – Is logging the classpath on ApplicationStartedEventand ApplicationFailedEvent.
- LoggingApplicationListener – Configures the logging framework and scans for logback and log4j in the classpath. Logback has priority and is included by default in the spring boot starter dependencies. So, in order to use log4j, one has to exclude logback like shown bellow:
After that ApplicationContextInitialiser implementations get loaded in a similar fashion. These can also be defined in META-INF/spring.factories files within application’s classpath. At least those implementations already defined within the Spring Boot itself are included:
- DelegatingApplicationContextInitializer – Loads and applies initialisers specified with context.initializer.classes in application.properties.
- ContextIdApplicationContextInitializer – Set’s the application context id based on various properties. For more details consult documentation.
After SpringApplication#run(..) is called, implementations of SpringApplicationRunListenerinterface will be initialised. The purpose of these listeners is to be called in various phases of the run method (started, environmentPrepared, contextPrepared, contextLoaded, finished) and the only implementation as of version 1.1.6 of this interface is EventPublishingRunListener. This implementation then registers all ApplicationListener implementations loaded into the application. To implement a custom listener one can use addListener of SpringApplication or add it to context.listener.classes property. An attempt to implement SpringApplicationRunListener to listen to the lifecycle of Spring Boot will not succeed because the implementations of this interface will be loaded only from META-INF/spring.factories. So, the way to have access to the Spring Boot lifecycle is to implement ApplicationListener that EventPublishingRunListener will publish the events to accordingly. In this phase EventPublishingRunListener publishes theApplicationStartedEvent event and all registered listeners can react upon it.
In this phase the environment will be created. The environment type is created in the following order:
- Custom environment will be returned if provided with setEnvironment.
- StandardServletEnvironment if web dependencies found in the classpath (this was checked in the initialisation phase already).
- Otherwise StandardEnvironment will be created.
After that EventPublishingRunListener publishes the ApplicationEnvironmentPreparedEvent event.
Creating and preparing application context
As with the environment, the type of the application context which will be created goes in the following order:
- Custom Application Context if provided with setApplicationContextClass.
- ConfigEmbeddedWebApplicationContext if web dependencies found in classpath.
- AnnotationConfigApplicationContext will be created.
After that a shutdown hook will be registered unless specified withsetRegisterShutdownHook(false). ResourceLoader and BeanNameGenerator implementations will be loaded if specified with corresponding setters and will replace default implementations.At the end of this phase custom and default initialisers will be called and the source classes are loaded. At this moment the application context is prepared and EventPublishingRunListenerpublishes the ApplicationPreparedEvent event.
In this phase, the context is refreshed and all beans from the root package (the one class with@EnableAutoConfiguration) will be loaded into the context. All auto configuration classes will be loaded (defined in META-INF/spring.factories) excluding the ones explicitly disabled with@EnableAutoConfiguration(exlude=ExludedClass.class). Then they are sorted based on the @Orderannotation. The ones with the highest priority that will be loaded first areMessageSourceAutoConfiguration, PropertyPlaceholderAutoConfiguration,WebSocketAutoConfiguration, EmbeddedServletContainerAutoConfiguration,DispatcherServletAutoConfiguration.
This sample application will illustrate how much can be bootstrapped with Spring Boot. We will make a simple REST service which shows various quotes. Following technologies will be used:
- Spring MVC for the REST service
- Spring Security for endpoint authentication
- Spring Data JPA for data access
- Flyway Migration tool to create and populate the database
- H2 embedded database
The pom.xml file is given below:
You can see that there are only 5 dependencies. If you want a web application, you just need to provide spring-boot-starter-data-web dependency. You want to start right away with JPA. No problem, just provide spring-boot-starter-data-jpa dependency. Many dependencies for one use-case are no longer needed. In this way dependency management is also simplified.
Also, we don’t need a web.xml descriptor because the application runs inside an embedded web server (Tomcat by default). To run a web service we only need to write a controller class as given bellow and Spring Boot will deploy it on embedded Tomcat:
Spring boot adds authentication by default to the web services if it finds Spring Security in the classpath. While this is not so practical, this was configured in another class. Adding “USER“ role to the web service methods overrides the default username and password.
For the application to work properly we need to create a table in the database and populate it. For this Flyway migration tool is used. It scans the folder db.migration for a file with a name<prefix><version>__<description>.sql. In our case it is V1__init.sql. Manually, we would need to create a bean with the class Flyway and a attach a dataSource bean as a property like bellow and call migrate():
Fortunately Spring boot created this bean and added the dataSource to it. So the only part required from the developer was to create the migration file i.e. no configuration required.
As to the data access, Spring boot will auto-configure any repository class it finds and attach thedataSource bean to it. So, the developer only needs to define an Entity class and a Repository interface.
The source code of this example can be found on GitHub.
Well that’s how simple it is to get a single web application with Spring Boot up and running. Back then in the bad old days of enterprise java this would be considered a full-blown enterprise-ready three-tier J2EE application. With Spring Boot (thanks to a few other excellent open source frameworks like Spring, Spring Data and Flyway in this case) there’s almost nothing but auto-configuration left ;). My next blog post will describe how a slightly bit more complex Spring Batch application with quite a few XML config files can benefit from integration into the Spring Boot dreamland.
In case you’ve become curious and would like to discover which potential for innovative software solutions your company holds, get to know us: either in a first conversation, an individually crafted workshop or at one of our numerous community events!