Статьи

Как Spring достигает совместимости с Java 6, 7 и 8


Первоначально написано в весеннем блоге Стефана Николла

Начиная с Spring Framework 4.0, Java 8 поддерживается как первоклассный гражданин, и с тех пор в сообществе Spring наблюдается некоторая путаница. Как нам удается поддерживать Java 8 и оставаться совместимым с Java 6 и Java 7 в конце концов? Этот пост в блоге дает некоторое представление о том, как мы справляемся с этим в рамках кода базы.

ConnectionCallback может быть записано следующим образом:

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

Фактически, Spring Framework уже многие годы имел так называемые функциональные интерфейсы, и нам не нужно было менять какие-либо из этих API, чтобы они соответствовали правилам компилятора Java 8 для функциональных интерфейсов. Лямбда-код, такой как приведенный выше, для вызова Spring API, может использоваться в любом приложении Spring — для чего, очевидно, требуется среда выполнения Java 8. Однако, если вы решите написать такой код с традиционным подходом внутреннего класса, в отличие от тех же самых API-интерфейсов Spring в той же самой версии Spring, вы можете сделать это также с помощью среды выполнения Java 6+:

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

Суть в том, что выбор за вами: мы тщательно спроектировали Spring Framework 4.x, чтобы он был естественным образом совместим с Java 6, 7 и 8, с такими же Spring jar и без специальных шагов установки. Мы не используем никаких функций языка Java 8 в нашем собственном коде, поэтому мы можем скомпилировать нашу кодовую базу фреймворка  -source 1.6 -target 1.6, и мы автоматически определяем и автоматически активируем многие функции Java 8 API (если они доступны во время выполнения) в рамках этой структуры кодовой базы. Код вашего приложения может затем выбрать использование самого языкового уровня Java 6, 7 или 8, взаимодействуя с нашей структурой фреймворка и, естественно, получая максимальную отдачу от JDK, который вы используете — без какой-либо дополнительной настройки, просто путем объединения Spring с вашим JDK во время выполнения.

StreamConverter на github,  но вот выдержка:

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 является изолированным классом с использованием Java 8 конкретных API — интерфейсов, так что нам нужно сделать сейчас, чтобы условно добавить  StreamConverter к  , DefaultConverterService если Java 8 присутствуют во время выполнения.

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));
        }
    }
}

Мы условно проверяем, присутствует ли API во время выполнения, и на основании этого принимаем решение, когда вы, как пользователь, просто испытываете полностью адаптированную настройку Java 8 по умолчанию. Это несколько похоже на  инфраструктуру условий  в Spring Boot, за исключением того, что она более низкоуровневая и внутренняя.

Animal Sniffer  с каждой сборкой. Это проверяет наш код на соответствие заданной сигнатуре Java API   (в нашем случае Java 6 update 18) и завершает сборку, если обнаруживается неправильное использование. Так что же насчет законных случаев использования, когда нам нужно вызывать API Java 7 или 8? Вы можете настроить сниффер так, чтобы он исключал список классов или, что еще лучше, предоставлял набор аннотаций,  помечающих  такие исключительные случаи.

Это именно то, что   указывает @UsesJava8 аннотация  StreamConverter(см. Выше): она разграничивает весь класс как исключение из правила совместимости API Java 6. Вы можете пометить внутренний класс или даже метод подобным образом. Рассматривая использование этой аннотации, мы знаем все места, где в нашей кодовой базе используются специфические API Java 7/8.

Конфигурация Animal Sniffer довольно проста: ознакомьтесь с  нашей сборкой  или  официальной документацией  для получения более подробной информации.