Under The Boot

Remember the times when we had to register dispatchers, viewResolvers, etc. to make our spring application web-app? Then there was @EnableWebMvc annotation and now even this is redundant.
These days the only thing you need to do is to add ‘org.springframework.boot:spring-boot-starter-web’ dependency to your project and everything else is done automagically.

The same goes for a database connection. Not that long ago minimum db-aware spring-context configuration was:

  • register data source (<jdbc:embedded-database id=”dataSource” type=”HSQL”/>)
  • register entity manager (through entity manager factory) (<bean id=”entityManagerFactory” class=”org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean”>)
  • register transaction manager (<bean id=”transactionManager” class=”org.springframework.orm.jpa.JpaTransactionManager” >)
  • turn on annotation driven transaction boundaries (<tx:annotation-driven transaction-manager=”transactionManager” proxy-target-class=”false”/>)

Along the way we dropped xml configs in favour of configurations. Now - everything you need to do is to add another dependency ‘org.springframework.boot:spring-boot-starter-data-jpa’ and some db driver (like ‘com.h2database:h2’) and again - spring creates everything behind the curtains.

I don’t know about you - but I grow suspicious when that many things happen without my knowledge. After having been using Spring Boot for a while I needed to look under the hood to feel safe again - not that much under - just enough to get back to my comfort zone.

High Level View

The basic mechanism goes like this:
All magically appearing beans are registered with spring configurations (@Configuration).
But those are loaded only if specific conditions are met - namely:

  • required class is available on the classpath (new beans magically created when dependency added)
  • required bean was not created explicitly by a programmer

So for example - to load WebMvcAutoConfiguration when Servlet, DispatcherServlet, WebMvcConfigurerAdapter classes are on the classpath @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class }) is used.
This configuration loads all web mvc defaults - but most of them again - only if specific bean doesn’t yet exist.
So for example - to check if defaultViewResolver() should be created - @ConditionalOnMissingBean(InternalResourceViewResolver.class) is used.

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
		WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
(...)
public class WebMvcAutoConfiguration {
	(...)

	@Bean
	@ConditionalOnMissingBean(InternalResourceViewResolver.class)
	public InternalResourceViewResolver defaultViewResolver() {
		InternalResourceViewResolver resolver = new InternalResourceViewResolver();
		resolver.setPrefix(this.prefix);
		resolver.setSuffix(this.suffix);
		return resolver;
	}
	
	(...)
}

 

Where Is It Triggered?

It all begins with @SpringBootApplication - the one you annotate you main class with. If you check it’s sources you’ll find out @EnableAutoConfiguration there - and this one is responsible for most of the magic.

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application.class);
        application.run(args);
    }
}

If you check spring-boot-autoconfigure.jar/META-INF/spring.factories you’ll find out org.springframework.boot.autoconfigure.EnableAutoConfiguration property which specifies which auto configurations will be used to ‘guess’ and create beans you require.

autoconfigurations

 

DB Magic Example

So let’s figure out how the required components for db access are created.
To do so let’s not use ‘org.springframework.boot:spring-boot-starter-data-jpa’ dependency in our showcase, but start with ‘org.springframework.boot:spring-boot-starter’ and see what dependencies should we add to create database aware app and how required steps are automagically performed.

With only ‘spring-boot-starter:1.2.6.RELEASE’ dependency I got 22 beans registered by spring in my app(18 spring beans + 4 application specific beans). There is no dataSource nor transactionManager amongst them.

I want to add hibernate entity to the project so I will include ‘org.hibernate:hibernate-entitymanager’ compile dependency.
Now JtaAutoConfiguration with jta properties beans were added as javax.transaction.Transaction appeared on the classpath.
I was little hoping for HibernateJpaAutoConfiguration to catch on - but this one also requires

  • LocalContainerEntityManagerFactoryBean (to provide entityManager)
  • EnableTransactionManagement (to provide transactionManager)

Both can be found in ‘org.springframework:spring-orm’ - so let’s add this dependency to the classpath.
Now spring will try to load HibernateJpaAutoConfiguration, but this one requires dataSource bean (@Autowired as a private field), and we are still missing it.
It’s easy to figure out that dataSource will be created by DataSourceAutoConfiguration (found on list in spring.factories).
It’s seems that all conditions are met here, but DataSourceAutoConfiguration class can’t be classloaded yet - as it uses org.apache.tomcat.jdbc.pool.DataSourceProxy so it depends on ‘org.apache.tomcat:tomcat-jdbc’ - let’s add it to the class path as well.

Running the app we can see that we’re getting closer - as this time ‘hibernate.dialect’ can’t be determined. No surprise here - it couldn’t have been determined by spring as we hadn’t added any db specific dependency. So let’s include ‘com.h2database:h2’ .

Everything seems to work now. 54 beans loaded by spring. Among them:

  • dataSource (org.apache.tomcat.jdbc.pool.DataSource - added by DataSourceAutoConfiguration.NonEmbeddedConfiguration.dataSource())
  • entityManagerFactory (org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - added by JpaBaseConfiguration.entityManagerFactory() (parent of HibernateJpaAutoConfiguration))
  • transactionManager (org.springframework.orm.jpa.JpaTransactionManager - added by JpaBaseConfiguration.transactionManager() (parent of HibernateJpaAutoConfiguration)

 

Dependencies are summarized on diagram below.

dependencies

 

Showcase project can be found “here”.

  **

Written on October 11, 2015
comments powered by Disqus