In this post I am looking into Spring Boot 1.4.0.RC1 version trying out the new features and enhancements. Spring Boot 1.4.0.RC1 upgrades to Spring 4.3.1.RELEASE and Hibernate 5.0.9 versions.
Spring 4.3
In Spring 4.3 there are lots of new features and enhancements. The full list you can find it here.
Some of my favourites are
-
The introduction of composed annotations from
@RequestMapping
like for example@GetMapping
as a shortcut for@RequestMapping(method = RequestMethod.GET)
@RestController class ChuckNorrisFactController { @GetMapping("/") public ChuckNorrisFact getOneRandomly() { return service.getOneRandomly(); } }
-
No need to specify
@Autowired
for constructor injection if the target bean defines only one constructor which is mostly the case.@RestController class ChuckNorrisFactController { private ChuckNorrisFactService service; public ChuckNorrisFactController(ChuckNorrisFactService service) { this.service = service; } }
-
The
SpringRunner
alias for theSpringJUnit4ClassRunner
Hibernate 5
The general upgrade instruction from the Hibernate team can be found here. If you have upgrade problems switching to Hibernate 5 can be postponed at a later stage by setting the hibernate.version
in your pom.xml
. The Spring team decided to go with Hibernate 5.0.9 as default, however you can find samples using version 5.2.x and 4.3.x
Spring Boot 1.4.0.RC1
Let’s see what new features and enhancements Spring Boot 1.4.0.RC1 contains on top of the above. The full list you can find in M1
, M2
M3
and RC1
release notes.
Here I selected couple of my favourites.
Testing support
The biggest change comes regarding testing support. There are two new modules spring-boot-test
and spring-boot-test-autoconfigure
@SpringBootTest
Spring already comes with great support for writing integration tests leveraging the spring-test
module. The only problem might be that it uses lots of annotations (@SpringApplicationConfiguration
, @ContextConfiguration
, @IntegrationTest
, @WebIntegrationTest
) to accomplish it.
Spring Boot 1.4 tries to simplify this by providing a single @SpringBootTest
annotation.
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Sql("/init-for-full-integration-test.sql")
public class ApplicationTests {
@Autowired
private TestRestTemplate template;
@Test
public void random() {
ResponseEntity<String> fact = template.getForEntity("/", String.class);
assertThat(fact.getBody(), containsString("Chuck Norris sleeps with a pillow under his gun."));
}
}
Here as you can see for convenience the injected TestRestTemplate
is already configured to make calls against the started server, no need to inject the port with @Value("${local.server.port}")
. However if you would like to know the port there is a better @LocalServerPort
annotation.
Testing slices of the application
Sometimes you would like to test a simple “slice” of the application instead of auto-configuring the whole application. Spring Boot 1.4 introduces 4 new test annotations:
@WebMvcTest
- for testing the controller layer@JsonTest
- for testing the JSON marshalling and unmarshalling@DataJpaTest
- for testing the repository layer@RestClientTests
- for testing REST clients
@WebMvcTest
In order to test only the controller layer or often a single controller the @WebMvcTest
used in combination with @MockBean
can be handy. @Service
or @Repository
components will not be scanned.
@RunWith(SpringRunner.class)
@WebMvcTest(ChuckNorrisFactController.class)
public class CheckNorrisFactControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private ChuckNorrisFactService chuckNorrisFactService;
@Test
public void getOneRandomly() throws Exception {
given(chuckNorrisFactService.getOneRandomly()).willReturn(new ChuckNorrisFact("Chuck Norris counted to infinity twice."));
mvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{'fact':'Chuck Norris counted to infinity twice.'}"));
}
}
@JsonTest
To test JSON marshalling and unmarshalling you can use the @JsonTest
annotation. @JsonTest
will auto-configure Jackson ObjectMappers and @JsonComponent
beans (see later).
@RunWith(SpringRunner.class)
@JsonTest
public class ChuckNorrisFactJsonTests {
private JacksonTester<ChuckNorrisFact> json;
@Test
public void serialize() throws IOException {
ChuckNorrisFact fact = new ChuckNorrisFact("When Chuck Norris turned 18, his parents moved out.");
JsonContent<ChuckNorrisFact> write = this.json.write(fact);
assertThat(this.json.write(fact)).isEqualToJson("expected.json");
}
@Test
public void deserialize() throws IOException {
String content = "{\"fact\":\"Chuck Norris knows Victoria's secret.\"}";
assertThat(this.json.parse(content)).isEqualTo(new ChuckNorrisFact("Chuck Norris knows Victoria's secret."));
}
}
@DataJpaTest
To test the JPA layer of an application you can use @DataJpaTest. By default it configures an in-memory database, scans for @Entity
classes and configures the Spring Data JPA repositories. @Service
, @Controller
, @RestController
beans will not be loaded.
@RunWith(SpringRunner.class)
@DataJpaTest
public class ChuckNorrisFactRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private ChuckNorrisFactRepository repository;
@Test
public void getOneRandomly() {
String fact = "If at first you don't succeed, you're not Chuck Norris.";
entityManager.persist(new ChuckNorrisFact(fact));
List<ChuckNorrisFact> facts = repository.getOneRandomly(new PageRequest(0, 10));
assertThat(facts.get(0).getText(), is(fact));
}
}
@RestClientTests
Is useful to test only beans which are using RestTemplate
or RestTemplateBuilder
. It will apply only configuration relevant to rest client tests (Jackson or GSON auto-configuration, and @JsonComponent
beans), but not regular @Component
beans.
@RunWith(SpringRunner.class)
@RestClientTest({ChuckNorrisFactClient.class, ChuckNorrisFactClientProperties.class})
public class ChuckNorrisFactClientTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Autowired
private ChuckNorrisFactClient client;
@Autowired
private MockRestServiceServer server;
@Test
public void getRandomChuckNorrisFactWhenResultIsSuccessShouldReturnFact() {
server.expect(requestTo("/")).andRespond(withSuccess(getClassPathResource("chucknorrisfact.json"), MediaType.APPLICATION_JSON));
ChuckNorrisFactClientResponse response = client.getRandomChuckNorrisFact();
assertThat(response.getFact()).isEqualTo("There is no theory of evolution. Just a list of animals Chuck Norris allows to live.");
}
@Test
public void getRandomChuckNorrisFactWhenResultIsErrorShouldThrowException() {
server.expect(requestTo("/")).andRespond(withServerError());
this.thrown.expect(HttpServerErrorException.class);
client.getRandomChuckNorrisFact();
}
private ClassPathResource getClassPathResource(String path) {
return new ClassPathResource(path, getClass());
}
}
RestTemplateBuilder
It is a great tool to customize and create a RestTemplate
with sensible defaults. It is also auto-configured which you can use to further customize and create a @RestTemplate
with which you call your REST service. It might event come with timeout customization support.
@Service
public class ChuckNorrisFactClient {
private final RestTemplate restTemplate;
public ChuckNorrisFactClient(ChuckNorrisFactClientProperties properties, RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.basicAuthorization(properties.getUsername(), properties.getPassword())
.rootUri(properties.getServiceRootUrl()).build();
}
public ChuckNorrisFactClientResponse getRandomChuckNorrisFact() {
return restTemplate.getForObject("/", ChuckNorrisFactClientResponse.class);
}
}
@JsonComponent
With @JsonComponent
you can easier register custom JSON serializers and deserializers. Below yon can see the additional utility classes (JsonObjectSerializer
and JsonObjectDeserializer
) which simplify even more the serialization and deserialization logic.
@JsonComponent
public class ChuckNorrisFactJson {
public static class Serializer extends JsonObjectSerializer<ChuckNorrisFact> {
@Override
protected void serializeObject(ChuckNorrisFact value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStringField("fact", value.getText());
}
}
public static class Deserilizer extends JsonObjectDeserializer<ChuckNorrisFact> {
@Override
protected ChuckNorrisFact deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec, JsonNode tree) throws IOException {
String fact = tree.get("fact").asText();
return new ChuckNorrisFact(fact);
}
}
}
Datasource binding
In Spring Boot 1.4 via spring.datasource
property namespace only the common datasource properties are set. For datasource provider specific ones new namespaces were introduced.
For example if you are using HikariCP as a datasource provider you can set
spring.datasource.hikari.maximum-pool-size=5
spring.datasource.hikari.connection-timeout=10
In IntelliJ with configuration assistance is nice to check which properties can be set for the given datasource provider instead of having them all mixed under spring.datasource
namespace.
Image Banners
You can use an image to render the ASCII banner. For inspiration have a look what people already created tweetbootbanner
Analysis of startup failures
Very nice feature is the startup analysis of common startup failures. It will provide a useful information instead of logging the exception stack trace. Of course you can get the stack trace with the well knwon --debug
switch.
2016-07-07 17:46:19.171 ERROR 84637 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Cannot determine embedded database driver class for database type NONE
Action:
If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
Summary
If you want to try out these new features and enhancements have a look my github repository. Also have a look at Phil Webb’s excellent post about testing support improvements in Spring Boot 1.4.