Рубрики
Без рубрики

Java-банки с несколькими выпусками

“Добавление поддержки реализаций Java InvocationHandler для методов интерфейса по умолчанию” описывает ho… Помеченный java.

“Добавление поддержки реализаций Java InvocationHandler для методов интерфейса по умолчанию” описывает, как реализовать InvocationHandler для вызова методов интерфейса по умолчанию . Этот механизм имеет решающее значение для Fluent Node реализация, описанная в “Интерфейсные фасады Java” . В первой статье также отмечается, что метод Обрабатывает. Поиск Метод, используемый с Java 8 не будет работать с Java 9 это означает, что весь API не будет работать на Java 9 и последующих JVM .

В этой статье описывается специфичное для Java 9 решение, рефакторинг InvocationHandler реализация для разделения и разделения логики решений, специфичных для Java 8 и Java 9, и вводит “JEP 238: файлы JAR с несколькими выпусками” для предоставления Java 8 и решения Java 9 (и более поздних версий) одновременно в одном и том же JAR.

Теория работы

Как описано в JEP 238 , банки с несколькими выпусками предоставляют средства для предоставления альтернативных версий классов, которые могут использовать преимущества определенных функций платформы. Альтернативные классы хранятся в JAR в иерархии, описанной /META-INF/versions/${java.specification.version}/ . (Иерархия реализации подробно показана в конце этой статьи.) Для архиваторов и загрузчиков классов, которые не поддерживают несколько выпусков (например, Java 8 ), эти дополнительные классы игнорируются. Однако для Java 9 и последующих сред эти дополнительные классы загружаются, если версия меньше или равна JVM/| ${java.specification.version} (причем последнее имеет приоритет).

Класс с кодом, специфичным для Java 8 , будет переработан для реализации суперинтерфейса с одним методом, воплощающим этот код. Эта существующая кодовая база будет скомпилирована как есть для создания JAR, подходящего для Java 8 Jvm. Кроме того, версия суперинтерфейса Java 9 будет скомпилирована для среды Java 9 и сохранена в JAR под /META-INF/versions/9/ иерархия.

Реализация

Defaultinvocationhandler.invoke(Объект,метод,Объект[]) учитывается для реализации DefaultInterfaceMethodInvocationHandler :

@NoArgsConstructor @ToString
public class DefaultInvocationHandler implements DefaultInterfaceMethodInvocationHandler {
    ...
    @Override
    public Object invoke(Object proxy, Method method, Object[] argv) throws Throwable {
        Object result = null;
        Class declarer = method.getDeclaringClass();

        if (method.isDefault()) {
            result = DefaultInterfaceMethodInvocationHandler.super.invoke(proxy, method, argv);
        } else if (declarer.equals(Object.class)) {
            result = method.invoke(this, argv);
        } else {
            result = invokeMethod(this, true, method.getName(), argv, method.getParameterTypes());
        }

        return result;
    }
    ...
}

С помощью реализации Java 8 :

public interface DefaultInterfaceMethodInvocationHandler extends InvocationHandler {
    @Override
    default Object invoke(Object proxy, Method method, Object[] argv) throws Throwable {
        Constructor constructor =
            MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);

        constructor.setAccessible(true);

        Class declarer = method.getDeclaringClass();
        Object result =
            constructor.newInstance(declarer)
            .in(declarer)
            .unreflectSpecial(method, declarer)
            .bindTo(proxy)
            .invokeWithArguments(argv);

        return result;
    }
}

В то время как реализация Java 9 является:

public interface DefaultInterfaceMethodInvocationHandler extends InvocationHandler {
    @Override
    default Object invoke(Object proxy, Method method, Object[] argv) throws Throwable {
        Class declarer = method.getDeclaringClass();
        Object result =
            MethodHandles.lookup()
            .findSpecial(declarer, method.getName(),
                         methodType(method.getReturnType(), method.getParameterTypes()), declarer)
            .bindTo(proxy)
            .invokeWithArguments(argv);

        return result;
    }
}

Альтернатива Java 9 сохраняется в ${basedir}/src/main/java9/ball/lang/reflect/DefaultInterfaceMethodInvocationHandler.java в рамках проекта Maven. Компиляция с помощью maven-compiler-plugin проста, поскольку следующий профиль демонстрирует инкрементную конфигурацию. 1


  ...
  
  ...
    
      [+] src/main/java9
      
        ${basedir}/src/main/java9
      
      
        
          
            
              org.apache.maven.plugins
              maven-compiler-plugin
              
                
                  jdk9
                  
                    compile
                  
                  
                    9
                    
                      ${project.basedir}/src/main/java9
                    
                    true
                  
                
              
            
          
        
      
    
  ...
  
  ...

Чтобы завершить настройку JAR, в манифесте JAR должен быть установлен флаг Multi-Release .

Manifest-Version: 1.0
...
Multi-Release: true
...

Результирующая иерархия JAR показана ниже.

ball-util.jar
├── META-INF
│   ├── MANIFEST.MF
│   ├── ...
│   └── versions
│       └── 9
│           └── ball
│               └── lang
│                   └── reflect
│                       └── DefaultInterfaceMethodInvocationHandler.class
└── ball
    ├── ...
    ├── lang
    │   ├── ...
    │   └── reflect
    │       ├── DefaultInterfaceMethodInvocationHandler.class
    │       ├── DefaultInvocationHandler.class
    │       └── ...
    ├── ...
    ...

Резюме

Банки с несколькими выпусками предоставляют решение для одного JAR для поддержки различных версий платформы Java. Разработчики должны знать, что решение действительно создает некоторые проблемы: скомпилированные файлы классов version нельзя запускать вне JAR (что затрудняет тестирование), а детали реализации загрузчика классов повлияют на обнаружение и загрузку ресурсов. 2 (и это лишь некоторые из них). Однако, как показано в этом примере использования, это мощный инструмент для создания JAR-файлов, поддерживающих широкий спектр платформ Java.

[1] Родительский POM имеет аналогичные профили для Java с 10 по 14. ↩

[2] Пожалуйста, ознакомьтесь с обсуждением в “JEP 238: Файлы JAR с несколькими выпусками” . ↩

Оригинал: “https://dev.to/allenball/java-multi-release-jars-5c1l”