This is the second Post in my series Developing a web application with Spring Boot, AngularJS and Java 8.
Java 8 has a major history. Many features should have been already included in Java 7 (i.e. Lambdas (anonymous functions for the elderly), modules and some stuff from project coin), which was released in 2011.
Now, in March 2014, Java 8 will be released (and i will be enjoying the release party in JavaLand) and it’s time to get some impression, what’s new and what’s doable.
Java 8 support in Spring 4
Spring 4 has already full support for Java 8:
Spring Framework 4.0 provides support for several Java 8 features. You can make use of lambda expressions and method references with Spring’s callback interfaces. There is first-class support for java.time (JSR-310), and several existing annotations have been retrofitted as @Repeatable. You can also use Java 8’s parameter name discovery (based on the -parameters compiler flag) as an alternative to compiling your code with debug information enabled.
Java 8 (as well as 6 and 7)
What is not available is Java 8 support in Eclipse based Springsource Tool Suite. I tried really hard to like JetBrains IntelliJ IDEA but it didn’t click at all. One example: I’m (still) a big Maven fan and adding a XML file for every dependency in a Maven project is a no-go for me. Also, on my 2012 iMac with 16GiB ram but HDD the product is way to slow.
So in the end i gave NetBeans IDE another try. I’m using 7.4 at the moment and i must say i really like it. Maven support is outstanding, didn’t have any problem with broken builds, plugins (JPA meta model, byte code enhancements) work as expected and the IDE feels very snappy.
I really like the CSS and JavaScript support as well. Autocomplete for CSS classes for example is a very handy feature.
And finally, NetBeans 7.4 has first class support for Lambdas and Java 8 features. There are many helpful hints to convert anonymous inner classes to Lambdas, for example.
Lambdas as configuration classes
The following represents one way to add filters to a Servlet 3 context using Spring Boots ServletContextInitializer:
@Bean
public ServletContextInitializer servletContextInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
final CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(false);
servletContext.addFilter("characterEncodingFilter", characterEncodingFilter).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
};
} |
@Bean
public ServletContextInitializer servletContextInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
final CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(false);
servletContext.addFilter("characterEncodingFilter", characterEncodingFilter).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
};
}
You have to create an inner class, clutter your code with the onStartup method and what you really want to is just to add stuff to the Servlet context:
@Bean
public ServletContextInitializer servletContextInitializer() {
return servletContext -> {
final CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(false);
servletContext.addFilter("characterEncodingFilter", characterEncodingFilter).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
};
} |
@Bean
public ServletContextInitializer servletContextInitializer() {
return servletContext -> {
final CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(false);
servletContext.addFilter("characterEncodingFilter", characterEncodingFilter).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
};
}
The type of the lambda is determined by the return type of servletContextInitializer() and the rest is very simple to read: An instance of ServletContext is passed to the block and can be configured. Puritans would mind the braces, but i don’t. I like this syntax very much: Reduced to the stuff i want to do with little clutter as possible.
If Spring Boot was Java 8 only, the ServletContextInitializer interface should have been annotated with FunctionalInterface that has exactly one abstract method. The interface enforces that only one abstract method is present and the type is an interface type. Bear in mind, that any interface that meet this conditions, like the one above, are treated as functional as well.
Streams
I love Streams:
A sequence of elements supporting sequential and parallel aggregate operations. […] To perform a computation, stream operations are composed into a stream pipeline. A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate)), and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer)). Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.
In my biking application i wanted to note my tachometer each month and add the current total milage and all computations should be done by the app. The old application was a Ruby app and this could be done in a one liner. A Java 8 stream allows nearly the same:
milages are the list of total milage at a given point in time. This method computes all monthly periods by:
- Creating an IntStream from 1 (inclusive) to the size of milages (exclusive)
- Map those ints to a LocalDate and a value by collecting them in a TreeMap.
In the “collect” call you see a Method Reference to the constructor of TreeMap as the 1st parameter and to the putAll method as the last parameter. This means:
“Collect them by creating a new TreeMap as the target, use the lambda given as 2nd parameter and merge them using putAll when ran in parallel.”
The given lambda just takes the generated int, get the milage at the given and previous index, compute the date and the differences and add it to the map parameter.
If you get your brain around those notions of collecting, mapping and reducing, the stuff actually becomes very easy to read.
The following is an example that includes filtering of stuff and resolving associations from the controller that creates the nice history graph here:
This creates a stream of all bikes than resolves all associations (I’m a database guy after all) to periods and creates a new stream from all periods, those are filtered by date and than collected in a TreeMap representing years and an array of values for each month per year.
There is also an example of the new Map#computeIfAbsent that creates a new value for a key not contained in the map which is handy in soooo many cases. The “generate” call in the same line is actually the creation of a new array containing 12 zeros:
IntStream.generate(() -> 23).limit(42).toArray() |
IntStream.generate(() -> 23).limit(42).toArray()
just with a static import of #generate.
Also this statement includes an example that the stream operations may run in parallel and it’s sometimes not enough to use an simple Map#putAll as a combiner as in the example of the combiner in collect above. Here an existing array would have been overwritten when the operation runs in parallel so the addArray method must take care of this and is used for Map#merge.
Some say that Java 8 is pretty much recreating SQL with this kind of syntax and thinking, specifying what you want not how you want it. I – as stated above – like this.
I think this is just the tip of the things you can to with streams.
JSR 310 Date and Time API
FINALLY a build-in Date-Time API that doesn’t suck in pretty much every way and is not from Taligent.
Just let me start with an example of how broken the inheritance chain of java.util.Date and java.sql.Date is:
Date#toInstant: Converts this Date object to an Instant.
You see, a Date represents specific instant in time, with millisecond precision and can therefor be converted to a shiny new Instant.
As Hibernate doesn’t support JSR 310 yet, i wanted to use java.util.Date for my TemporalType.DATE’s to convert them to LocalDate like
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() |
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
The reason for Date instead of Calendar was Hibernate HHH-8617 discovered by me. I just wanted to do it right but was let down by the fact, that hibernate doesn’t return java.util.Date but java.sql.Date.
java.sql.Date represents only a date (yeah, the behavior is semantically correct), but there you have one of the bad designs in this area: Changing the behavior of the the class in a way that #toInstant of java.sql.Date throws an exception because it represents suddenly only a date is just horrible.
But enough of the old stuff. In the examples above you already see some new features of JSR 310, i.e. the query methods isBefore / isAfter.
Those classes are already supported in Spring 4 and can be used as controller method parameters together with a DateTimeFormat:
There are many other useful classes in java.time, be sure to have a look.
XML Parsing
Nothing that new here, but i’m doing Java for so long and although it seams that XML is an intrinsic part of the language there are is a plethora of solutions for XML parsing, for example JDOM, DOM4J, XOM and many more.
Java Architecture for XML Binding (JAXB) is part of Java since 1.6 but at least not used often enough by me.
To create a simple parser for MediaRSS it’s enough to write a few POJOs, annotate them a little, for example:
configure some namespaces
and you’re good to go:
JAXBContext rssContext = JAXBContext.newInstance(RSS.class);
final Unmarshaller unmarschaller = rssContext.createUnmarshaller();
RSS rss = (RSS) unmarschaller.unmarshal(inputStream); |
JAXBContext rssContext = JAXBContext.newInstance(RSS.class);
final Unmarshaller unmarschaller = rssContext.createUnmarshaller();
RSS rss = (RSS) unmarschaller.unmarshal(inputStream);
Neat, i’d say.
You can also use the new java.time classes with little effort:
Summary
I had really much fun exploring Java 8’s new features and i’m somewhat sad that i cannot use them in current projects of mine that are not recreational projects.
What i didn’t touch was Nashorn, but maybe i’ll find a use case for that too in my demo application.
Having written a major application in Ruby using Ruby on Rails 2.x, doing maintenance for a medium sized Grails application and looking sometimes at Scale, i’d always go back to plain, old Java, hands down. Maybe the syntax is more verbose, maybe strict typing is old school, but all that gives you a kind of assurance that other languages are missing. There are a lot of IDEs to generate most of the boring stuff for you. Also, don’t forget that many developers, senior or junior, seems a bit overwhelmed the last years by the continuing growth in technologies (I’ve written about that in Old and tired?) and not everybody is as keen as the writer of this post to learn something new. With Java 8 you can have both: Old-fashioned code or a streamlined, modern code base.
Filed in English posts
|