Spring Boot Starter: Comfortable modularization and abstraction

This is the english version of an article about writing custom Spring Boot starters I’ve written for upcoming german JavaAktuell magazine. iJUG and DOAG members will get it for free.

Abstract

Even in the light of microservices good architecture and well thought modularization is vital for all kinds of application. A microservice without inner structure is as bad maintainable as an old-fashioned monolith, maybe even worse. Candidates for external modules that are intrinsic parts of many microservices without being one of their own could be:

  • Datastructures (entities) and corresponding data access objects or repositories for recurrent, structured information
  • Logging, status (healthchecks) and similar, cross-cutting or other non-functional requirements, which are paramount to manage the growing number of services
  • Integrations (for databases, caches and many others)

Those modules must not only be provided but also be configured, but often this configuration can be an opinionated configuration using sensible defaults which reduces manual configuration in many cases by magnitudes.

In this article i will show you how to create custom "Starters" for Spring Boot based applications that provide opinionated configuration, custom functionality and more.

Spring Boot

Spring Boot can be seen as an umbrella project for the Spring eco-system that has been created with the following goals in mind:

  • To provide a radically faster and widely accessible ‘getting started’ experience for all Spring development
  • To be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults
  • To provide a range of non-functional features that are common to large classes of projects (e.g. embedded servers, security, metrics, health checks, externalized configuration)

Spring Boot does not generate code during bootstrapping or during runtime and there is absolutely no requirement for XML configuration. All tools that are used by Spring Boot to detect available libraries, environment and configuration are available outside Spring Boot for the creation of custom configuration classes or whole "Starters".

The use case: Manage your JavaScript dependencies with Maven, Webjars und wro4j

Rumor has it, that there's still a need for server-side generated HTML code, JavaScript as progressive enhancement und simple management of JavaScript dependencies without the the need to use the "Universal install script", even in 2016:

The universal install script

We all have been cursed about Maven, but fact is: It works most of the time unobtrusive and it is also pretty good in managing dependencies, resolve them and find conflicting versions (for example using the enforcer-plugin).

WebJars is a project by James Ward and facilitates the explicit and easy management of the client-side dependencies in JVM-based web applications through Maven, SBT, Gradle, ivy or other build tools. Spring Boot supports WebJars out of the box and dependencies can be used like this:

<link rel='stylesheet' href='/webjars/bootstrap/3.1.0/css/bootstrap.min.css'>

wro4j is a tool for analyzing and optimizing web resources that combines many modern tools from frontend development.

The goal is to express frontend dependencies inside the pom.xml:

<properties>
	<jquery.version>1.11.3</jquery.version>
	<angularjs.version>1.2.29</angularjs.version>
</properties>
<!-- ... -->
<dependencies>
	<dependency>
	    <groupId>org.webjars</groupId>
	    <artifactId>jquery</artifactId>
	    <version>${jquery.version}</version>
	</dependency>	
	<dependency>
	    <groupId>org.webjars</groupId>
	    <artifactId>angularjs</artifactId>
	    <version>${angularjs.version}</version>
	</dependency>
</dependencies>

and to combine those dependencies into logical modules. The following example shows a library group that is combined with custom JavaScript code into one module:

<groups xmlns="http://www.isdc.ro/wro">
    <group name="lib">
        <js minimize="false">/webjars/jquery/@jquery.version@/jquery.min.js</js>
        <js minimize="false">/webjars/angularjs/@angularjs.version@/angular.min.js</js>
        <js minimize="false">/webjars/angularjs/@angularjs.version@/angular-route.min.js</js>
    </group>
 
    <group name="biking2">
    	<group-ref>lib</group-ref>
 
        <css>/css/stylesheet.css</css>
 
        <js>/js/app.js</js>
        <js>/js/controllers.js</js>
        <js>/js/directives.js</js>
    </group>
</group>

We're using the Xml configuration of Wro4j but there's also a nice Groovy DSL available.

This necessary configuration of what is to be used combined with the starter should be enough to include jQuery, AngularJS and custom functionality through one single HTML element.

Spring Boot Starter

A Spring Boot starter consists of two components:

  • One autoconfigure module containing the automatic configuration including it's default values.
  • The starter module itself, providing the dependency to the autoconfigure module and all needed additional dependencies

Including the starter as a dependency for a Spring Boot application should be enough to start using that library. If a separation of those two isn't needed, combine everything into the starter module.

Not a kind of magic: Conditions

Pivotals Spring Developer Advocate Josh Long explicitly opposes calling auto configuration inside Spring Boot applications "magic" and he is right saying so.

The environments automatic configuration is based on two core components:

  • Profiles (@Profile, available since Spring 3.1) and
  • Conditions (@Conditional together with org.springframework.context.annotation.Condition, available since Spring 4)

Profiles allow provisioning of Beans based on active profiles (spring.profiles.active), whereas conditions can be used to model simple to complex conditions under which Beans are instantiated and configured:

  • @Conditional from the core framework allows with a custom implementation of a Condition arbitrary statements

Spring Boot contains several useful implementations

  • @ConditionalOnBean / @ConditionalOnMissingBean: Configuration and beans are only instantiated and used when certain other Beans are available or missing
  • @ConditionalOnClass / @ConditionalOnMissingClass: Configuration and beans are only instantiated and used when certain classes are available to the classloader
  • @ConditionalOnResource is triggered only when specific resources are available
  • @ConditionalOnProperty: A really powerful condition that matches concrete properties inside configurational files

As much the automatic configuration of Spring Boot applications and their adaption to environment seems magical, it doesn't fit Clarke's third law. It's all there to see and use. Spring Boot even provides a ConditionEvaluationReport, that allows exactly to understand, why a configuration in a given order took place.

And by the way: Spring uses it's own features: The @Profile annotation has been rewritten for Spring 4 and is based on Conditions itself.

Example: Providing the Wro4j Servlet filter

Wro4js core is basically the Servlet filter mapped to an URL pattern which processes the requests for CSS and JavaScript files configured in wro.xml or wro.groovy. This filter should be added the the servlet registration without manual interaction by just adding the wro4j-spring-boot-starter dependency:

@Configuration
@ConditionalOnClass(WroFilter.class)
@ConditionalOnMissingBean(WroFilter.class)
@EnableConfigurationProperties(Wro4jProperties.class)
@AutoConfigureAfter(CacheAutoConfiguration.class)
public class Wro4jAutoConfiguration {
}
 
@ConfigurationProperties("wro4j")
public class Wro4jProperties {
}

Wro4jAutoConfiguration is a common Spring @Configuration class (classes which use programmatically instantiate, configure and provide Beans). The configuration – all methods annotated with @Bean among others – should only be done if the WroFilter class is available and there isn't a bean of that type yet. Configuration should be done after the automatic cache configuration as this will be used later. This class also provides properties starting with "wro4j.*" inside an object, in an instance of Wro4jProperties.

There's a caveat here: Often you create configuration for a possible starter inside the project that's using the starter first. That works fine (as by default all @Configuration classes under the base package are evaluated) until the classes building that starter are extracted from the project and you forget to add a META-INF/spring.factories. This file is very similar to the Java "Service Provider Interfaces" and contains the list of classes that should take part in autoconfiguration:

org.springframework.boot.autoconfigure.EnableAutoConfiguration = ac.simons.spring.boot.wro4j.Wro4jAutoConfiguration

With this file Spring Boot neither doesn't have to scan the whole classpath for starter nor the user must take additional steps besides adding the starter itself.

Back to the example. The Servlet filter is a regular Spring Bean and is created like this inside Wro4jAutoConfiguration:

@Bean
ConfigurableWroFilter wroFilter(WroManagerFactory wroManagerFactory, Wro4jProperties wro4jProperties) {
	ConfigurableWroFilter wroFilter = new ConfigurableWroFilter();
	wroFilter.setProperties(wroFilterProperties(wro4jProperties));
	wroFilter.setWroManagerFactory(wroManagerFactory);
	return wroFilter;
}

As you can see: The filter depends on a WroManagerFactory and other properties which are injected as usual into this method.

This example shows how powerful the code based configuration of Spring Beans is: Complex conditions and configuration are right their in the source and can be understood, debugged and more, with or without tooling support by IDEs.

Example: Different environment

Wro4j supports caching of optimized resources through a simple, memory-based Least Recently Used strategy. The strategy is an interface and an implementation of a new CacheStrategy using the built-in org.springframework.cache.CacheManager is easily done. To make it work there must be an instance of Springs CacheManager. To get one, use @EnableCaching for example. As Spring supports several caches, ehcache, Redis and others, you get either get a manager backed by either one of those.

Otherwise, if there's no cache manager, the starter should use the basic Wro4j strategy that certainly doesn't make use of any Spring features:

@Bean
@ConditionalOnBean(CacheManager.class)
@ConditionalOnProperty("wro4j.cacheName")
@ConditionalOnMissingBean(CacheStrategy.class)
@Order(-100)
<K, V> CacheStrategy<K, V> springCacheStrategy(CacheManager cacheManager, Wro4jProperties wro4jProperties) {
	return new SpringCacheStrategy<K, V>(cacheManager, wro4jProperties.getCacheName());
}
 
@Bean
@ConditionalOnMissingBean(CacheStrategy.class)
@Order(-90)
<K, V> CacheStrategy<K, V> defaultCacheStrategy() {
	return new LruMemoryCacheStrategy<K, V>();
}

The springCacheStrategy method is only executed if a CacheManager is active, the name for the actual cache is configured and there isn't a cache strategy already defined. By using an additional @Order annotation its ensured that this method is evaluated before defaultCacheStrategy, which instantiates the default Wro4j strategy.

There are certainly people who would add this exampel to @Annotatiomania™ in no time, but I think using this combination of annotations and code for conditions and configuration apart from business logic and objects is an appropriate and actually readable way.

Deployment

The example project is a standard Maven project, combining a shared autoconfigure and starter module. As such, it bundles the implementation of autoconfiguration as well as the dependencies to Wroj4:

<dependencies>
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter</artifactId>
	</dependency>
 
	<dependency>
	    <groupId>ro.isdc.wro4j</groupId>
	    <artifactId>wro4j-core</artifactId>
	    <version>${wro4j.version}</version>
	</dependency>
 
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-test</artifactId>
	    <scope>test</scope>
	</dependency>       
</dependencies>

The dependencies of the autoconfigure module are usually marked as optional and only finalized in the starter module when creating two separate projects.

There's one peculiarity of the starter presented here: It's a child artifact of the official spring-boot-starters parent project. I thought it would be a nice training for clean code and good coding guidelines learning from the settings the Spring team uses. But, as i heard, they only want to inflict them on themselves. What made my life easier was my preferred IDE, which supports Checkstyle, JaCoCo and others out of the box.

The project is available on GitHub.

Conclusion

The starter described here is in production in several projects in our company as well as in public, for example at the Euregio JUG. It saved us from manually managing dependencies and copy and pasting recurring configuration.

There are few alternatives to Spring Boot Starters when developing shared modules for Spring Boot based applications. You could use annotation based configuration for them (@Service and the like) but than you'd have to trigger classpath scanning on a broader level. Manually writing additional XML or Java based configuration seems tedious and may lead to errors.

There are many useful starters listed in the spring-boot-starters-repository: Be it database integration, support for various template engines or even remote shells.

The goal of providing a starter for Wro4j was reached. You have to add the starter and a wro.xml like above containing Maven placeholders like @jquery.version@, which are also used for the WebJar dependencies and processed by the Maven resource plugin:

<resources>
   <resource>
		<directory>src/main/resources</directory>
		<filtering>true</filtering>
		<includes>
		    <include>wro.xml</include>
		</includes>
    </resource>
</resources>

You are free to configure the Wro4j processors and url inside application.properties:

wro4j.filterUrl = /owr
wro4j.managerFactory.preProcessors = removeSourceMaps, cssUrlRewriting, cssImport, cssMinJawr, semicolonAppender, jsMin
wro4j.cacheName = wro4j

and as a result you'll get minimized CSS and JavaScript resources that can be included with a single statement in your HTML pages.

Outlook

The starter here can be extended by creating custom Thymeleaf elements that either render one single HTML element or links to the original resources so that they can be easily debugged. The approach would be the same: Check if Thymeleaf is on the path, register additional tags and so on.

Sources

| Comments (0) »

02-May-16


Book: arc42 by example

title_pageHere’s a shameless self plug for the first book i’ve contributed too:

arc42 by example by Gernot Starke, Stefan Zörner and myself.

This book contains several real-world software architectures, documented with the practical arc42 template.

You will learn about the inner workings of a chess engine, a huge CRM system, a cool web system to track biking activities and an extremely small menu-bar application.

This is a direct result of a Gernots course for the iSAQB I attended at the end of last year. I’ve started to use arc42 and it really helped me a lot: For my pet project as well as “professional” software development and I’m more than happy that Gernot decided to include my example.

You can get our book at Leanpub.

Also, check out Gernots visit at the EuregioJUG: Rückblick zum aim42 Vortrag mit Gernot Starke (de).

| Comments (0) »

25-Apr-16


NetBeans settings for smooth debugging Spring Boot Applications

Here’s a short tip for a smoother debugging experience using NetBeans for developing Spring Boot Applications while using the Spring Boot Devtools:

Make sure you include the Devtools inside your dependency management:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

or

dependencies {
    compile("org.springframework.boot:spring-boot-devtools")
}

Devtools will automatically recognize that you’re running in an IDE respectively debugger and apart from a LiveReload server it’ll monitor your files and restart the context if necessary. To make this work you’ll to enable “Compile on save” in your NetBeans project (that is a project property) like this:


projectproperties

NetBeans however tries to apply all code changes in “Compile on save”-Mode. That works pretty well (not as good as JRebel, but then, NetBeans is free), but for example not for method deletions etc and you’ll end up with that error:


error-dialog

As you’re already opted to use Spring Boot Devtools, you won’t need that feature. The checkbox to turn it of is not a project specific setting but a global which you’ll find under the Java Debugger Tab inside Java options:


remove-apply-code

Turn “Apply code changes after save” of to enjoy a really smooth debugging and working on Spring Boot applications with NetBeans and Devtools.

| Comments (0) »

22-Apr-16


8 new features you’ll get with Spring Boot 1.4

Phil as a nice post about the improvements on testing in Spring Boot 1.4, check it out here: Testing improvements in Spring Boot 1.4.

I’d like to add a concrete example for those and some more, please have a look at the comments inside one of the most important controllers I’ve ever written 😉

Those are the dependencies you’ll need for the demo:

<dependencies>
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-web</artifactid>
    </dependency>		
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-test</artifactid>
        <scope>test</scope>
    </dependency>
    <dependency> 
        <groupid>org.springframework.restdocs</groupid>
        <artifactid>spring-restdocs-mockmvc</artifactid>	    	    
        <scope>test</scope>
    </dependency>
</dependencies>

Here’s the complete demo application:

import java.io.IOException;
import java.io.PrintStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
 
@SpringBootApplication
public class Demo14Application {
 
    @Controller
    @RequestMapping("/api/banner")
    static class BannerController {
 
	// 1) If you didn't chose to disable banner in your application (and why
	//    should you? ;) ), the banner will be availabe as a bean in your 
	//    application
	// 2) banner.jpg :) You can replace banner.txt with a banner.jpg or banner.png
	//    to generate some nice ascii art during startup
	private final Banner banner;
 
	private final Environment environment;
 
	// 3) no more @Autowired on constructors necessary 
	//    when there's on a unique non-default constructor
	//    This also works on @Configuration classes, which didn't support 
	//    constructor injection at all so far.
	//    This comes actually from Spring Framework 4.3.RC1   
	public BannerController(final Banner banner, final Environment environment) {
	    this.banner = banner;
	    this.environment = environment;
	}
 
	// 4) More annotations ;) 
	//    Precomposed @GetMapping, @PostMapping, @RequestScope, @SessionScope etc
	@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
	public void get(final HttpServletResponse response) throws IOException {
	    try (PrintStream printStream = new PrintStream(response.getOutputStream())) {
		banner.printBanner(environment, BannerController.class, printStream);
	    }
	}
    }
 
    public static void main(String[] args) {
	SpringApplication.run(Demo14Application.class, args);
    }
}

And here’s the test. I really like the simplifications there:

import BannerController;
import java.io.PrintStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.Banner;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
 
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
// 5) Easier named JUnit Runner ;)
@RunWith(SpringRunner.class)
// 6) Test slices of your application, in this case the Spring MVC slice
//    similiar options are available for @DataJpaTest and @JsonTest (incuding
//    JacksonTester).
@WebMvcTest(controllers = BannerController.class)	
// 7) Together with the above auto configuration comes autoconfiguration of
//    Spring REST docs, which removes the need for the JUnit rule and additional
//    configuration of the mock mvc instance
//    BTW: It seams that you can use addtitional formats like markdown for the
//    snippets
@AutoConfigureRestDocs(
	outputDir = "target/generated-snippets",
	uriHost = "banner-as-a-service.io",
	uriPort = 80
)
public class Demo14ApplicationTests {
 
    // 8) Spring Boot includes a @MockBean annotation that can be used to define
    //    a Mockito mock for a bean inside your ApplicationContext. You can use 
    //    the annotation to add new beans, or replace a single existing bean 
    //    definition. 
    @MockBean
    private Banner banner;
 
    @Autowired
    private MockMvc mockMvc;
 
    @Test
    public void testSomeMethod() throws Exception {
	doAnswer(invocation -> {
	    final PrintStream out = invocation.getArgumentAt(2, PrintStream.class);
	    out.write(bannerText.getBytes());
	    return null;
	}).when(banner).printBanner(anyObject(), anyObject(), anyObject());
 
	mockMvc
		.perform(
			get("/api/banner").accept(APPLICATION_JSON)
		)
		.andExpect(status().isOk())
		.andExpect(content().string(bannerText))
		.andDo(document("api/banner",
			preprocessRequest(prettyPrint()),
			preprocessResponse(prettyPrint())
		));
    }
 
    private final String bannerText
	    = " _____            _              ______ _____ _____ _____       _                \n"
	    + "/  ___|          (_)             | ___ \\  ___/  ___|_   _|     | |               \n"
	    + "\\ `--. _ __  _ __ _ _ __   __ _  | |_/ / |__ \\ `--.  | |     __| | ___   ___ ___ \n"
	    + " `--. \\ '_ \\| '__| | '_ \\ / _` | |    /|  __| `--. \\ | |    / _` |/ _ \\ / __/ __|\n"
	    + "/\\__/ / |_) | |  | | | | | (_| | | |\\ \\| |___/\\__/ / | |   | (_| | (_) | (__\\__ \\\n"
	    + "\\____/| .__/|_|  |_|_| |_|\\__, | \\_| \\_\\____/\\____/  \\_/    \\__,_|\\___/ \\___|___/\n"
	    + "      | |                  __/ |                                                 \n"
	    + "      |_|                 |___/                                                  \n"
	    + "          _ _   _        ___   _____ _____ _____ _____              _            \n"
	    + "         (_) | | |      / _ \\ /  ___/  __ \\_   _|_   _|            | |           \n"
	    + "__      ___| |_| |__   / /_\\ \\\\ `--.| /  \\/ | |   | |     __ _ _ __| |_          \n"
	    + "\\ \\ /\\ / / | __| '_ \\  |  _  | `--. \\ |     | |   | |    / _` | '__| __|         \n"
	    + " \\ V  V /| | |_| | | | | | | |/\\__/ / \\__/\\_| |_ _| |_  | (_| | |  | |_          \n"
	    + "  \\_/\\_/ |_|\\__|_| |_| \\_| |_/\\____/ \\____/\\___/ \\___/   \\__,_|_|   \\__|         \n"
	    + "                                                                                 \n"
	    + "                                                                                 ";
}

Have a look at the release notes or the updated reference documentation for more.

Read the complete article »

| Comments (1) »

17-Apr-16


Get your InstrumentationLoadTimeWeaver recognized by LocalContainerEntityManagerFactoryBean

org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean is LoadTimeWeaverAware and as thus should be provided with such. Usually this would be done with a @Configuration, but then there is SPR-10856. In short: a LoadTimeWeaverAwareProcessor is provided before post processing the bean factory. This means that an @Bean inside a configuration class comes to load for load time weaving.

At least for Spring Boot there’s a solution. You can register the LoadTimeWeaver before startup:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
 
@SpringBootApplication
@EnableConfigurationProperties
public class Application {    
    public static void main(final String... args) {
        final SpringApplication springApplication = new SpringApplication(Application.class);
        springApplication.addListeners(new ApplicationListener<ApplicationPreparedEvent>() {
            @Override
            public void onApplicationEvent(final ApplicationPreparedEvent event) {
                event.getApplicationContext().getBeanFactory().registerSingleton(ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, new InstrumentationLoadTimeWeaver());
            }
        });
        springApplication.run(args);
    }
}

Be careful not to use a lambda, because there’s SPR-14109.

Anyway: This way byte code enhancement for JPA / Hibernate entities will work. Another solution would be build time byte code enhancement, as Vlad points out here: For Hibernate 5 and 4.

| Comments (0) »

10-Apr-16