разделить библиотеку банку на банку impl и api jar? - PullRequest
1 голос
/ 03 апреля 2020

Представьте, что у нас есть Lib .jar и два исполняемых jar-файла - App1 и App2, которые зависят от Lib .jar во время компиляции. Наша текущая ситуация такова, что всякий раз, когда мы меняем реализацию Lib (сохраняя при этом подписи нетронутыми), мы также должны менять App1 и App2: хотя исходный код в самих исполняемых файлах вообще не меняется, мы все еще требуется обновить номер версии Lib в pom-файле каждого исполняемого файла, увеличить номер каждой исполняемой версии и повторно развернуть их - здоровенно.

Я представляю себе лучшую ситуацию, в которой после изменения реализации Lib развертывается jar-библиотека Lib и исполняемые файлы необходимо перезапустить только в производственной среде, чтобы принять эти изменения - нет необходимости изменять pom каждого исполняемого файла; нет необходимости создавать новые исполняемые фляги; нет необходимости развертывать новые исполняемые файлы jar в среде prod.

Как можно улучшить эту ситуацию? Мы используем Maven в качестве нашей системы сборки.

Обратите внимание, что мы бы предпочли подход, при котором библиотека загружается в той же JVM, что и каждый из исполняемых файлов - мы не хотим иметь архитектуру веб-службы с реализацией Lib, работающей на другой виртуальной машине.

Кроме того, учтите, что мы также поддерживаем исполняемые файлы и разрабатываем их на наших рабочих столах, т. Е. В IDE, запускаем команды maven в командной строке и т. Д. c, и мы хотели бы сохранить эту возможность.

Спасибо.

1 Ответ

0 голосов
/ 05 апреля 2020

Вот попытка разбить библиотеку на два jar-файла: api jar и impl jar - и приложение, которое во время компиляции зависит только от lib api, следовательно, его не нужно изменять при изменении lib lib - смотрите ma?!:

  • приложение должно зависеть во время компиляции от lib api (1)
  • зависимость приложения только от lib impl указано в помете приложения для целей разработки (2) и хранится как зависимость времени выполнения, чтобы избежать введения нежелательных зависимостей времени компиляции (3)
  • во время компиляции приложения зависит только от lib api и, таким образом, он не знает, как создавать объекты реализации, чье построение становится ответственностью другого, например Spring (4)
  • API основан на интерфейсах (5) . Это помогает в определении сигнатур в одном месте, а также позволяет избежать ограничения единственного наследования Java на классы реализации.
  • для организации загрузки файла impl jar во время выполнения, я полагаюсь на Соглашение о включении сторожевого класса в файл impl jar, который загружается изначально при запуске приложения (6) , чтобы принудительно загрузить весь файл impl jar в JVM приложения
  • , что осталось do (не показано ниже) - ссылка на файл impl jar во время выполнения в среде prod с помощью параметра командной строки classpath

lib-api

public interface Foo { // 5
    void bar();
}

lib-impl

pom. xml

<dependencies>
    <dependency>
        <groupId>splitlib</groupId>
        <artifactId>lib-api</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId> <!-- 4 -->
    </dependency>
</dependencies>

ImplFoo. java

import org.springframework.stereotype.Component;

@Component
final class ImplFoo implements Foo {
    @Override
    public void bar() {
        System.out.println("hello world");
    }
}

Sentinel. java

public final class Sentinel {
    private Sentinel() {}
}

app

pom. xml

<profiles>
    <profile>
        <id>development</id> <!-- 2 -->
        <activation>
            <os><family>mac</family></os>

            <!-- or whatever -->

        </activation>
        <dependencies>
            <dependency>
                <groupId>splitlib</groupId>
                <artifactId>lib-impl</artifactId> <!-- 2 -->
                <scope>runtime</scope> <!-- 3 -->
            </dependency>
        </dependencies>
    </profile>
</profiles>

<dependencies>
    <dependency>
        <groupId>splitlib</groupId>
        <artifactId>lib-api</artifactId>    <!-- 1 -->
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId> <!-- 4 -->
    </dependency>
</dependencies>

Main. java

final class Main {
    static {
        try {
            // 6
            Class.forName(format("%s.Sentinel",  Foo.class.getPackage().getName()));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        // 4
        new AnnotationConfigApplicationContext(Foo.class.getPackage().getName())
                .getBean(Foo.class)
                .bar();
    }
}
...