Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Spring Boot: My Favorite Timesaving Convention-enabling Autoconfig-creating Bean-making Classpath-shaking Microcontainer

DZone's Guide to

Spring Boot: My Favorite Timesaving Convention-enabling Autoconfig-creating Bean-making Classpath-shaking Microcontainer

· Java Zone
Free Resource

The single app analytics solutions to take your web and mobile apps to the next level.  Try today!  Brought to you in partnership with CA Technologies

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:

@EnableAutoConfiguration
public class Application
{
  public static void main(String[] args)
  {
  SpringApplication.run(Application.class, args);
  }
}

Below you can see the lifecycle of Spring Boot in more detail:

Spring Boot Lifecycle

Initialisation phase

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 allApplicationListener implementations 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:

After that ApplicationContextInitialiser implementations get loaded in a similar fashion. These can also be defined inMETA-INF/spring.factories files within application’s classpath. At least those implementations already defined within the Spring Boot itself are included:

  • ParentContextCloserApplicationListener – Listener that closes the application context if it’s parent is closed
  • VcapApplicationListener – Listener for VCAP (Cloud Foundry) applications
  • FileEncodingApplicationListener – an ApplicationListenerthat 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 setspring.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 allApplicationListener 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 ApplicationStartedEvent andApplicationFailedEvent.
  • 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:
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <exclusions>
  <exclusion>
<groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-logging</artifactId>
  </exclusion>
  </exclusions>
</dependency>
<dependency>
<groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

Application started

After SpringApplication#run(..) is called, implementations ofSpringApplicationRunListener interface will be initialised. The purpose of these listeners is to be called in various phases of the run method (startedenvironmentPrepared,contextPreparedcontextLoaded, finished) and the only implementation as of version 1.1.6 of this interface isEventPublishingRunListener. This implementation then registers all ApplicationListener implementations loaded into the application. To implement a custom listener one can useaddListener of SpringApplication or add it tocontext.listener.classes property. An attempt to implementSpringApplicationRunListener 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 EventPublishingRunListenerwill publish the events to accordingly. In this phaseEventPublishingRunListener publishes theApplicationStartedEvent event and all registered listeners can react upon it.

Preparing environment

In this phase the environment will be created. The environment type is created in the following order:

  1. Custom environment will be returned if provided withsetEnvironment.
  2. StandardServletEnvironment if web dependencies found in the classpath (this was checked in the initialisation phase already).
  3. Otherwise StandardEnvironment will be created.

After that EventPublishingRunListener publishes theApplicationEnvironmentPreparedEvent 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:

  1. Custom Application Context if provided withsetApplicationContextClass.
  2. ConfigEmbeddedWebApplicationContext if web dependencies found in classpath.
  3. AnnotationConfigApplicationContext will be created.

After that a shutdown hook will be registered unless specified with setRegisterShutdownHook(false)ResourceLoader andBeanNameGenerator 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.

Refreshing context

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 @Order annotation. The ones with the highest priority that will be loaded first areMessageSourceAutoConfiguration,PropertyPlaceholderAutoConfiguration,WebSocketAutoConfiguration,EmbeddedServletContainerAutoConfiguration,DispatcherServletAutoConfiguration.

Sample application

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:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
  <groupId>com.comsysto</groupId>
   <artifactId>spring-boot-sample</artifactId>
  <version>1.0.0</version>
   <dependencies>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
  <version>1.1.6.RELEASE</version>
  </dependency>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>1.1.6.RELEASE</version>
  </dependency>
  <dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
  <version>3.0</version>
  </dependency>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
  <version>1.1.6.RELEASE</version>
  </dependency>
  <dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <version>1.4.181</version>
  </dependency>
   </dependencies>
   <build>
   </build>
</project>

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-jpadependency. 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:

@RestController
public class QuoteController 
{
  @Autowired
  @Qualifier("quoteRepository")
  private final QuoteRepository repository;




  private final static Quote NONE = new Quote("None");




  @Autowired
  public QuoteController(QuoteRepository repository) 
  {
   this.repository = repository;
  }




  @Secured("USER")
  @RequestMapping (value = "/api", method = RequestMethod.GET)
  public Iterable<Quote> getAll() 
  {
   return repository.findAll();
  }




  @Secured("USER")
  @RequestMapping (value = "/api/{id}", method = RequestMethod.GET)
  public Quote getOne(@PathVariable Long id)
  {
   if (repository.exists(id))
  {
  return repository.findOne(id);
  }
  else
  {
    return NONE;
    }
  }
}

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.

@Configuration
public class SecurityConfiguration extends GlobalAuthenticationConfigurerAdapter
{
  @Autowired
  @Qualifier("restSecurityProperties")
  RestSecurityProperties properties;




  @Override
  public void init(AuthenticationManagerBuilder builder) throws Exception {
  builder.inMemoryAuthentication()
  .withUser(properties.username())
  .password(properties.password())
  .roles("USER");
  }
}

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 isV1__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():

<bean id="flyway" class="org.flywaydb.core.Flyway" init-method="migrate">
  <property name="dataSource" ref="..."/>
  ...
</bean>

Fortunately Spring boot created this bean and added thedataSource 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 the dataSource bean to it. So, the developer only needs to define an Entity class and a Repository interface.

@Repository
public interface QuoteRepository extends CrudRepository<Quote, Long>
{
}




@Entity
public class Quote
{
  @Id
  @GeneratedValue
  Long id;
   private String quote;




   protected Quote() {}




   public Quote(String quote) 
   {
    this.quote = quote;
   }




   public Long getId() 
   {
    return id;
   }




   public String getQuote() 
   {
    return quote;
   }




   @Override
   public String toString() 
   {
    return "id: " + id + " Quote: " + quote;
   }
}

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 numerouscommunity events!

CA App Experience Analytics, a whole new level of visibility. Learn more. Brought to you in partnership with CA Technologies.

Topics:
java ,enterprise-integration ,frameworks ,tips and tricks

Published at DZone with permission of Comsysto Gmbh, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}