DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Comparing ModelMapper and MapStruct in Java: The Power of Automatic Mappers
  • Reactive Programming
  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Spring Boot: How To Use Java Persistence Query Language (JPQL)

Trending

  • Four Ways for Developers To Limit Liability as Software Liability Laws Seem Poised for Change
  • How To Deploy Helidon Application to Kubernetes With Kubernetes Maven Plugin
  • Bad Software Examples: How Much Can Poor Code Hurt You?
  • Choosing the Appropriate AWS Load Balancer: ALB vs. NLB
  1. DZone
  2. Coding
  3. Frameworks
  4. How Spring Achieves Compatibility With Java 6, 7 and 8

How Spring Achieves Compatibility With Java 6, 7 and 8

Pieter Humphrey user avatar by
Pieter Humphrey
CORE ·
Apr. 09, 15 · Interview
Like (1)
Save
Tweet
Share
14.99K Views

Join the DZone community and get the full member experience.

Join For Free
Originally written on the Spring blog by Stéphane Nicoll

As of Spring Framework 4.0, Java 8 is supported as a first-class citizen and we’ve seen some confusion in the Spring community since then. How do we manage to support Java 8 and remain compatible with Java 6 and Java 7 after all? This blog post provides some insight into how we’re handling this within the framework codebase.

Java 8 language features vs. Java 8 APIs

First, a distinction must be made between using new language features and new APIs in a given Java generation such as Java 8. If a class uses a Java 8 language feature such as a lambda expression, it has to be compiled with -source 1.8 -target 1.8 and therefore the whole compilation unit will only work on Java 8+. However, if a particular class in a library optionally uses a new Java 8 interface such as java.util.stream.Stream, the library can still run on a previous Java generation as long as it is being compiled with e.g.-source 1.6 -target 1.6 - and as long as the use of that particular Stream-based class is guarded to only kick in when actually running on Java 8+. As you may have guessed, we’re making extensive use of such arrangements within the Spring Framework codebase!

We’ve advertised how Spring Framework 4.0 naturally fits with Java 8 lambdas. For instance, retrieving the catalog of a given JDBC connection with a ConnectionCallback can be written as follows:

jdbcTemplate.execute(connection -> connection.getCatalog())

In fact, Spring Framework had now-so-called functional interfaces for years and we did not have to change any of those APIs to be compliant with Java 8’s compiler rules for functional interfaces. Lambda-based code such as the above, calling into Spring APIs, can be used in any Spring application - which then requires a Java 8 runtime, obviously. However, if you choose to write such code with a traditional inner class approach, against the very same Spring APIs in the very same Spring version, you can do so as well with a Java 6+ runtime:

jdbcTemplate.execute(new ConnectionCallback<String>() {
    @Override
    public String doInConnection(Connection con) throws SQLException {
        return con.getCatalog();
    }
});

The bottom line is that the choice is yours: We carefully designed Spring Framework 4.x to be naturally compatible with Java 6, 7 and 8, with the same Spring jars and no special setup steps. We don’t use any Java 8 language features in our own code, so we can compile our framework codebase with -source 1.6 -target 1.6, and we autodetect and automatically activate many Java 8 API features (if available at runtime) within that codebase arrangement. Your application code may then choose to use Java 6, 7 or 8 language level itself, interacting with our framework arrangement and naturally getting the most out of the JDK that you happen to be using - without any extra setup, just through combining Spring with your JDK at runtime.

Which Java 8 API features do we support?

We have dedicated support for a number of Java 8 specific API features such asjava.util.Optional, java.util.stream.Stream, java.time (JSR-310), repeatable annotations, method/constructor parameter names, and even the java.util.Base64 utility class. Those features get reflectively detected when you choose to use them in your own application classes, with the Spring Framework conditionally activating its support for those Java 8 features, e.g. registering default converters for Optional and Stream when Java 8 is present at runtime.

Let’s have a look at an example. In the upcoming Spring Framework 4.2, if you define a value of type Collection or array, you can inject it as a Stream and we will convert that for you. You can find the full code of StreamConverter on github but here’s an excerpt:

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.core.convert.*;
import org.springframework.lang.UsesJava8;

@UsesJava8
public class StreamConverter implements ConditionalGenericConverter {
    ....
}

StreamConverter is an isolated class using Java 8 specific APIs, so what we need to do now is to conditionally add StreamConverter to DefaultConverterService if Java 8 is present at runtime.

public class DefaultConversionService extends GenericConversionService {

    /** Java 8's java.util.stream.Stream class available? */
    private static final boolean streamAvailable = ClassUtils.isPresent(
            "java.util.stream.Stream", 
            DefaultConversionService.class.getClassLoader());

    private static void addCollectionConverters(
            ConverterRegistry converterRegistry) {
        ...

        if (streamAvailable) {
            converterRegistry.addConverter(
                    new StreamConverter(conversionService));
        }
    }
}

We conditionally check if the API is present at runtime and make a decision based on that, with you as a user simply experiencing fully Java 8 adapted setup by default. This is somewhat similar to the conditions infrastructure in Spring Boot except it is more low-level and internal.

Checking Java 6 compatibility

Since we are using Java 8 specific APIs in several isolated places, we need JDK 8 to compile the framework codebase overall. As a result, there is a risk that we accidentally introduce Java 8 specific API calls in places where we need to remain Java 6 compatible.

Fortunately, our CI build plan is configured to execute Animal Sniffer with each build. This checks our code against a given Java API signature (in our case Java 6 update 18) and fails the build if some incorrect usage happens to be found. So what about legitimate use cases then where we do need to call Java 7 or 8 APIs? You can configure the sniffer to exclude a list of classes or, better yet, provide a set of annotations that flag such exceptional cases.

That’s exactly what the @UsesJava8 annotation on StreamConverter (see above) indicates: It demarcates the whole class as an exception to the Java 6 API compatibility rule. You can flag an inner class or even a method in a similar manner. By looking at our usage of that annotation, we know all the places where Java 7/8 specific APIs are used in our codebase.

The Animal Sniffer configuration is pretty straightforward: check out our build or the official documentation for more details.

Wrapping Up

We chose not to use any Java 7 or Java 8 language features in our own codebase in order to give you the flexibility to write your Spring 4 applications for Java 6, 7 or 8. At the same time, we allow you to experience a very natural approach if you decide to use Java 8, with the Spring Framework essentially appearing as Java 8 based to you in such a scenario.

Fortunately, Java 8’s functional interface convention isn’t new for us. Many existing Spring APIs can be seamlessly used with Java 8 lambdas since they are naturally following the same convention. New Java 8 APIs such as java.time (JSR-310), Optional and Stream are automatically supported by the framework if you choose to use them in your own code.

On a forward-looking note, as of 4.2, our codebase is even being checked with early JDK 9 builds already! This will lead to a unique situation in the framework once JDK 9 becomes generally available next year: supporting four Java generations in the same release line - your choice of JDK 6, 7, 8 or 9 in combination with the same Spring Framework generation!

Spring Framework Java (programming language) Compatibility (chemical)

Published at DZone with permission of Pieter Humphrey, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Comparing ModelMapper and MapStruct in Java: The Power of Automatic Mappers
  • Reactive Programming
  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Spring Boot: How To Use Java Persistence Query Language (JPQL)

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: