картина

Автор | Джош Лонг
Переводчик | Чжан Вэйбинь
Планирование | Дин Сяоюнь
Эта статья является частью серии "Native Compilations Boosts Java". Вы можете получать уведомления о новых обновлениях статей, подписавшись на RSS.

Java доминирует в корпоративных приложениях. Но в облаке внедрение Java обходится дороже, чем у конкурентов. Встроенная компиляция с помощью GraalVM снижает стоимость Java в облаке: она создает приложения, которые запускаются быстрее и используют меньше памяти.

Нативная компиляция ставит перед пользователями Java много вопросов: как нативная Java изменит способ разработки? Когда мы должны перейти на нативную Java? Когда следует избегать перехода на нативную Java? Какой фреймворк использовать, чтобы использовать нативную Java? Статьи этой серии ответят на эти вопросы.
1Сообщество Java всеохватывающее

Мы знаем, что Java — удивительная экосистема, и трудно перечислить, что лучше для нас. При этом список кажется бесконечным. Однако не так уж сложно назвать несколько его недостатков. Как описано в этой серии статей, запуск приложений, работающих на JRE, часто занимает 10 и более секунд и требует сотен или гигабайт памяти.

Такая производительность не на лидирующих позициях в современном мире. Появляются некоторые новые области и возможности: предложения «функция как услуга», контейнеризация и оркестрация контейнеров. У них есть одна общая черта, то есть они имеют высокие требования к скорости запуска и объему памяти.

2На пути к GraalVM!

GraalVM предлагает путь вперед, но за него приходится платить. GraalVM — это альтернатива OpenJDK, в которой есть инструмент под названием Native Image, поддерживающий компиляцию с опережением (AOT).

Компиляция AOT имеет некоторые отличия от обычной компиляции Java. Как указано в первой статье этой серии, Native Image удаляет «все ненужное» в Java. Итак, как Native Image узнает, какие из них не нужны в Java или Spring Boot?

Native Image исследует наш исходный код и определяет весь доступный код, то есть код, с которым можно связать через вызовы или использование нашего кода. Все остальное, будь то в пути к классам приложения или в JRE, считается ненужным и отбрасывается.

Проблемы возникают, когда мы делаем что-то, что Native Image не знает точно, что с этим делать. В конце концов, Java — очень динамичный язык. Можно создать приложение Java, которое во время выполнения компилирует строку в действительный файл класса Java в файловой системе, загружает его в ClassLoader, а затем использует отражение для создания его экземпляра или создания для него прокси. Мы также можем сериализовать экземпляр на диск и загрузить его в другую JVM. В этом процессе нам может не понадобиться связывать какие-либо более конкретные классы , чем java.lang.Object . Однако все эти подходы не будут работать должным образом в нативной Java, если эти типы не будут помещены в нативную исполняемую кучу.

Однако мы ничего не потеряли. Мы можем указать Native Image, какие типы следует хранить в файле конфигурации, чтобы он по-прежнему работал при использовании таких функций, как отражение, прокси, загрузка ресурсов пути к классам, JNI и т. д. во время выполнения.

Сейчас экосистема Java и Spring огромна. Было бы больно все настраивать. Таким образом, у нас есть два варианта: 1) Научить Spring максимально избегать использования этих механизмов или 2) Научить Spring предоставлять как можно больше файлов конфигурации.Этот файл конфигурации должен содержать Spring Framework и Spring Boot, и в какой-то степени содержит сторонние функции интеграции, поддерживаемые Spring Boot. Дружеский спойлер, нужны оба варианта!

Чтобы запустить пример проекта, на вашем компьютере должна быть установлена ​​GraalVM. GraalVM имеет руководство по установке. Если вы используете Mac, вы также можете использовать SDKMAN!, чтобы установить GraalVM.

3Весенний родной

Команда Spring запустила проект Spring Native в 2019 году, который представил компиляцию собственных исполняемых программ в экосистеме Spring Boot. Он предоставил исследовательские площадки для многих различных модальностей. Однако Spring Native принципиально не меняет Framework 5.x или Spring Boot 2.x. И это ни в коем случае не конец, а только первый шаг в долгом пути: он подтвердил множество концепций для следующего поколения Spring Framework (6.x) и Spring Boot (3.x), которые ожидаются в 2022 г. Опубликовано впоследствии. Эти проекты следующего поколения будут более оптимизированы, так что будущее выглядит очень ярким! Учитывая, что эти версии еще не выпущены, в этой статье мы рассмотрим Spring Native.

Spring Native преобразует исходный код, отправленный в Native Image. Например, Spring Native преобразует механизм загрузки службы spring.factories в статические классы, чтобы приложения Spring Native знали, как их использовать. Он преобразует все классы конфигурации Java (классы, аннотированные @Configuration ) в функциональную конфигурацию Spring, устраняя отражение для приложения и его зависимостей.

Spring Native также автоматически анализирует наш код, обнаруживает сценарии, требующие конфигурации GraalVM, и предоставляет эту конфигурацию программно. Spring Native предоставляет классы подсказок для интеграции Spring, Spring Boot и сторонних производителей.

4Первое приложение Spring Native: JPA, Spring MVC и H2

Мы начинаем работать со Spring Native так же, как и со всеми другими проектами Spring: переходим в Spring Initializr, нажимаем cmd + B (или Ctrl + B) или Add Dependencies и выбираем Spring Native.

Spring Initializr настраивает сборки Apache Maven и Gradle. После этого просто добавьте необходимые зависимости. Начнем с некоторых типичных зависимостей. Измените имя артефакта на jpa, а затем добавьте следующие зависимости: Spring Native Spring Web Lombok H2 Database и Spring Data JPA . Убедитесь, что вы выбрали Java 17, конечно, вы можете выбрать и Java 11, но это похоже на то, что вы бегаете по миру с резиновой курицей, что выглядит довольно глупо, верно? Нажмите «Создать», чтобы разархивировать сгенерированный проект и импортировать его в свою любимую IDE.

Этот пример очень прост, измените класс JpaApplication.java , чтобы он выглядел следующим образом:

package com.example.jpa;
import lombok.*;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.*;import javax.persistence.*;import java.util.Collection;import java.util.stream.Stream;
@SpringBootApplicationpublic class JpaApplication {
public static void main(String[] args) { SpringApplication.run(JpaApplication.class, args); }}
@Componentrecord Initializr(CustomerRepository repository) implements ApplicationRunner {
@Override public void run(ApplicationArguments args) throws Exception { Stream.of("A", "B", "C", "D") .map(c ->; new Customer(null, c)) .map(this.repository::save) .forEach(System.out::println); }}
@RestControllerrecord CustomerRestController(CustomerRepository repository) {
@GetMapping("/customers") Collection<Customer> customers() { return this.repository.findAll(); }}
interface CustomerRepository extends JpaRepository<Customer, Integer> {}
@Entity@Getter@Setter@ToString@NoArgsConstructor@AllArgsConstructor@Table (name = "customer")class Customer { @Id @GeneratedValue private Integer id; private String name;}

Мы также можем компилировать и запускать тесты как собственные исполняемые файлы. Но следует отметить, что некоторый контент работает плохо, например, Mockito. Модифицируем тестовый класс JpaApplicationTests.java , чтобы он выглядел так:

package com.example.jpa;
import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.util.Assert;
@SpringBootTestclass JpaApplicationTests {
private final CustomerRepository customerRepository;
@Autowired JpaApplicationTests(CustomerRepository customerRepository) { this.customerRepository = customerRepository; }
@Test void contextLoads() { var size = this.customerRepository.findAll().size(); Assert.isTrue(size > 0, () -> "there should be more than one result!"); }
}

В этой статье я покажу команды под macOS. Для Windows и Linux отрегулируйте соответствующим образом.

Мы можем запустить приложение и тесты обычным способом, например, запустив команду mvn spring-boot:run в терминале. На самом деле рекомендуется запускать эти примеры напрямую, по крайней мере, чтобы убедиться, что приложение будет работать правильно. Однако это не наша цель. Вместо этого мы хотим скомпилировать приложение и его тесты в собственное приложение GraalVM.

Если вы посмотрите на файл pom.xml, вы увидите много дополнительной конфигурации, которая создает собственный образ GraalVM и добавляет профиль Maven (называемый native ) для поддержки создания собственных исполняемых файлов. Мы можем использовать пакет mvn clean для компиляции приложения как обычно. Приложения также могут быть скомпилированы с помощью mvn -Pnative clean package . Следует помнить, что вам нужно настроить GraalVM как собственный JDK. Этот процесс займет несколько минут, так что сейчас самое время выпить чашку чая, кофе, воды или другого напитка. Я просто сделал это, потому что мне это было нужно. Когда я вернулся, я увидел такой вывод:

...13.9s (16.9% of total time) in 71 GCs | Peak RSS: 10.25GB | CPU load: 5.66...[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time:  03:00 min[INFO] Finished at: 2022-04-28T17:57:56-07:00[INFO] ------------------------------------------------------------------------

На компиляцию нативных тестов у нас ушло три минуты, а если тесты прошли успешно, то и само нативное приложение. При этом Native Image использовал до 10,25 ГБ оперативной памяти. Чтобы ускорить объяснение, я пропущу процесс компиляции и запуска тестов далее в статье. Итак, когда мы компилируем следующий пример, будут использоваться следующие команды:

mvn -Pnative -DskipTests clean package

Время компиляции зависит от пути к классам приложения. Как правило, большинство моих сборок занимает от 1 минуты до 90 секунд, если пропустить тесты компиляции. Например, это приложение включает JPA (и Hibernate), Spring Data, базу данных H2, Apache Tomcat и Spring MVC.

Запустите приложение:

./target/jpa

На моей машине вы увидите:

…Started TraditionalApplication in 0.08 seconds (JVM running for 0.082)

Очень красиво, 80 миллисекунд или 80 тысячных секунды! Более того, приложение почти не использует память. Я использую следующий сценарий для проверки RSS приложения (размер резидентного набора).

#!/usr/bin/env bash  PID=$1RSS=`ps -o rss ${PID} | tail -n1`RSS=`bc <<< "scale=1; ${RSS}/1024"`echo "RSS memory (PID: ${PID}): ${RSS}M"

Нам нужен идентификатор процесса (PID) работающего приложения. В macOS я могу получить его, запустив pgrep jpa . Сценарий, который я использую, выглядит следующим образом:

~/bin/RSS.sh $(pgrep jpa)RSS memory (PID: 35634): 96.9M

Около 97 МБ оперативной памяти. Это значение может варьироваться в зависимости от операционной системы и архитектуры, в которой работает приложение. Это значение отличается при запуске приложения в Linux на Intel и macOS на M1. В настоящее время это значительное улучшение по сравнению с приложениями JRE, но все же не самое лучшее.

Мне нравится реактивное программирование, и я думаю, что теперь оно лучше подходит для моей рабочей нагрузки. Я создал аналогичное реактивное приложение. Оно не только занимало меньше места (по ряду причин, включая поддержку Spring Data R2DBC синтаксиса записи Java 17), время компиляции приложения составляло 1:14 (почти на две минуты быстрее), а время запуска составляло 0,044 секунды. Он занимает на 35% меньше памяти и составляет около 63,5 МБ. Приложение также может обрабатывать гораздо больше запросов в секунду. Таким образом, он быстрее компилируется и выполняется, более эффективно использует память, быстрее запускается и может обрабатывать более высокий трафик. Я говорю, что это хорошая сделка во всех отношениях.

5Интегрированное приложение

Spring — это не только конечные точки HTTP, есть много других вещей. Он включает в себя множество фреймворков, таких как Spring Batch, Spring Integration, Spring Security, Spring Cloud и растущий список других, все из которых обеспечивают хорошую поддержку Spring Native.

Давайте посмотрим на пример приложения Spring Integration. Spring Integration — это платформа, поддерживающая интеграцию корпоративных приложений (EAI). Основополагающая книга Грегора Хопе и Бобби Вульфа «Шаблоны интеграции предприятия» содержит общий термин для шаблонов интеграции. Spring Integration предоставляет абстракции для реализации этих шаблонов.

Вернитесь в Spring Initializr, назовите интеграцию проекта, выберите Java 17, добавьте Spring Native , Spring Integration , Spring Web и нажмите Generate . Нам нужно pom.xmlвручную добавить зависимость в файл:

<dependency>  <groupId>org.springframework.integration</groupId>  <artifactId>spring-integration-file</artifactId>  <version>${spring-integration.version}</version></dependency>

Измените код IntegrationApplication.java следующим образом:

package com.example.integration;
import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;import org.springframework.integration.dsl.IntegrationFlow;import org.springframework.integration.dsl.IntegrationFlows;import org.springframework.integration.file.dsl.Files;import org.springframework.integration.file.transformer.FileToStringTransformer;import org.springframework.integration.transformer.GenericTransformer;
import java.io.File;
@SpringBootApplicationpublic class IntegrationApplication {
@Bean IntegrationFlow integration(@Value("file://${user.home}/Desktop/integration") File root) { var in = new File(root, "in"); var out = new File(root, "out"); var inboundFileAdapter = Files .inboundAdapter(in) .autoCreateDirectory(true) .get(); var outboundFileAdapter = Files .outboundAdapter(out) .autoCreateDirectory(true) .get(); return IntegrationFlows // .from(inboundFileAdapter, spec -> spec.poller(pm -> pm.fixedRate(1000)))// .transform(new FileToStringTransformer()) .transform((GenericTransformer<String, String>) source -> new StringBuilder(source) .reverse() .toString() .trim()) .handle(outboundFileAdapter) .get(); }
public static void main(String[] args) { SpringApplication.run(IntegrationApplication.class, args); } }

Приложение очень простое: оно отслеживает каталог ( $HOME/Desktop/integration/in ) на наличие новых файлов. Как только новый файл найден, он создает копию Stringс содержимым, полностью противоположным исходному файлу, и записывает его в $HOME/Desktop/integration/out . В JRE время запуска приложения составляет 0,429 секунды. Это довольно круто, давайте посмотрим, какие изменения он внесет, чтобы преобразовать его в исполняемый файл GraalVM.

mvn -Pnative -DskipTests clean package

Время компиляции приложения составило 55,643 секунды. Запуск занял 0,029 секунды ( ./target/integration ) и занял 35,5 МБ ОЗУ. неплохо!

Как видим, так называемого типичного результата нет. Ввод процесса компиляции оказывает большое влияние на результат.

6Запустите приложение в производство

В какой-то момент мы можем захотеть развернуть наше приложение в рабочей среде, которой сегодня обычно является Kubernetes. Kubernetes работает как контейнер. Основная концепция проекта Buildpacks заключается в централизации и повторном использовании обычной практики преобразования артефактов приложений в контейнеры. Есть много способов использовать Buildpacks, вы можете использовать интерфейс командной строки пакета, вы можете использовать KPack в кластере Kubernetes или вы можете использовать плагин сборки Spring Boot. Мы будем использовать последний метод, потому что для него требуется только Docker Desktop. Посетите официальный сайт для получения дополнительной информации о Docker Desktop.

mvn spring-boot:build-image

Эта команда создает собственный исполняемый файл внутри контейнера, поэтому мы получаем контейнер Linux, содержащий собственные двоичные файлы Linux. Затем мы можем пометить его тегом docker и докером нажать и отправить в реестр контейнеров по нашему выбору. Когда я пишу это в мае 2022 года, пакеты Docker Buildpack все еще немного нестабильны на компьютерах Mac с архитектурой M1. Но я верю, что это скоро решится.

7Предоставьте некоторые подсказки для Native Image

В примерах, которые мы видели до сих пор, мы не сделали многого, чтобы приложение работало как собственный исполняемый файл. В соответствии с приведенной выше конфигурацией по умолчанию он может работать естественным образом. В большинстве случаев эта простота использования — это то, что нам нужно. Но иногда нам нужно дать Native Image некоторые подсказки, как я упоминал ранее в разделе «На пути к GraalVM!».

Давайте посмотрим на другой пример. Сначала перейдите в Spring Initializr, назовите расширения проекта, выберите Java 17 и добавьте Spring Native , затем нажмите Generate . Далее мы вручную добавим зависимость, которой нет в Initialzr:

<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-json</artifactId></dependency>

Наша цель здесь — увидеть, что происходит, когда что-то идет не так. Spring Native предоставляет набор подсказок, которые позволяют нам легко улучшить конфигурацию по умолчанию. Измените ExtensionsApplication.java , чтобы он выглядел следующим образом:

package com.example.extensions;
import com.fasterxml.jackson.core.type.TypeReference;import com.fasterxml.jackson.databind.ObjectMapper;import org.aopalliance.intercept.MethodInterceptor;import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.boot.*;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.core.io.ClassPathResource;import org.springframework.nativex.hint.*;import org.springframework.stereotype.Component;import org.springframework.util.*;
import java.io.InputStreamReader;import java.util.List;import java.util.function.Supplier;
@SpringBootApplicationpublic class ExtensionsApplication { public static void main(String[] args) { SpringApplication.run(ExtensionsApplication.class, args); }}
@Componentclass ReflectionRunner implements ApplicationRunner {
private final ObjectMapper objectMapper ;
ReflectionRunner(ObjectMapper objectMapper) { this.objectMapper = objectMapper; }
record Customer(Integer id, String name) { }
@Override public void run(ApplicationArguments args) throws Exception { var json = """ [ { "id" : 2, "name": "Dr. Syer"} , { "id" : 1, "name": "Jürgen"} , { "id" : 4, "name": "Olga"} , { "id" : 3, "name": "Violetta"} ] """; var result = this.objectMapper.readValue(json, new TypeReference<List<Customer>>() { }); System.out.println("there are " + result.size() + " customers."); result.forEach(System.out::println); }}
@Componentclass ResourceRunner implements ApplicationRunner {
@Override public void run(ApplicationArguments args) throws Exception { var resource = new ClassPathResource("Log4j-charsets.properties"); Assert.isTrue(resource.exists(), () -> "the file must exist"); try (var in = new InputStreamReader(resource.getInputStream())) { var contents = FileCopyUtils.copyToString(in); System.out.println(contents.substring(0, 100) + System.lineSeparator() + "..."); } }}
@Componentclass ProxyRunner implements ApplicationRunner {
private static Animal buildAnimalProxy(Supplier<String> greetings) { var pfb = new ProxyFactoryBean(); pfb.addInterface(Animal.class); pfb.addAdvice((MethodInterceptor) invocation -> { if (invocation.getMethod().getName().equals("speak")) System.out.println(greetings.get());
return null; }); return (Animal) pfb.getObject(); }
@Override public void run(ApplicationArguments args) throws Exception { var cat = buildAnimalProxy(() -> "meow!"); cat.speak();
var dog = buildAnimalProxy(() -> "woof!"); dog.speak(); }
interface Animal { void speak(); }}

Этот образец содержит три экземпляра ApplicationRunner, которые приложение Spring будет запускать при запуске. Каждый bean-компонент будет делать что-то, что делает GraalVM Native Image неудобным. Однако на JVM они работают нормально: mvn spring-boot:run.

第一个ApplicationRunner,即ReflectionRunner,会读取 JSON 数据并使用反射将它的结构映射到一个 Java 类Customer上。它无法正常运行,因为 Native Image 将会移除Customer类。使用mvn -Pnative -DskipTests clean package构建应用,并使用./target/extensions运行它。我们将会看到“com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces”这样的错误。

我们可以使用@TypeHint注解来修复该问题。添加如下的内容到ExtensionsApplication类上:

@TypeHint(types =  ReflectionRunner.Customer.class, access = { TypeAccess.DECLARE

在这里,我们声明我们希望对ReflectionRunner.Customer的构造器和方法进行反射访问。对于不同类型的反射,还有其他的TypeAccess值。

第二个ApplicationRunnerResourceRunner,会从 classpath 下某个依赖的.jar中加载文件。它也无法正常运行,并且会提示“java.lang.IllegalArgumentException: the file must exist”这样的错误。原因在于该文件位于其他的.jar中,而不是在我们的应用代码中。如果文件位于src/main/resources中的话,加载资源是可以正常运行的。我们可以使用@ResourceHint注解来解决这个问题。将如下的内容添加到ExtensionsApplication类中:

@ResourceHint(patterns = "Log4j-charsets.properties", isBundle = false)

第三个ApplicationRunner,即ProxyRunner,创建了一个 JDK 代理。代理会创建相关类型的子类或实现类。Spring 支持两种类型的代理,即 JDK 代理和 AOT 代理。JDK 代理仅限于使用 Java java.lang.reflect.Proxy的接口。AOT 代理则是 Spring 特有的,并不是 JRE 的一部分。JDK 代理通常是给定具体类的子类,也可能是接口。Native Image 需要知道我们的代理要使用哪些接口和具体类。

继续将第三个应用编译为原生可执行文件。Native Image 将会给出一条友好的错误信息“com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces”,并且会列出所有 Spring 试图要代理的接口。请注意这些类型:com.example.extensions.ProxyRunner.Animalorg.springframework.aop.SpringProxyorg.springframework.aop.framework.Advisedorg.springframework.core.DecoratingProxy。我们将会使用它们为ExtensionsApplication添加如下的线索:

@JdkProxyHint(types = {    com.example.extensions.ProxyRunner.Animal.class,    org.springframework.aop.SpringProxy.class,    org.springframework.aop.framework.Advised.class,    org.springframework.core.DecoratingProxy.class})

如果你现在尝试构建(mvn -DskipTests -Pnative clean package)并运行(./target/extensions)样例的话,就不会有任何问题了。

8 构建期和运行期的 Processor

Spring 有很多的Processor实现。Spring Native 提供了一些新的Processor接口,它们只会在构建期激活。这些Processor会动态地为构建过程提供线索信息。理想情况下,这些Processor的实现会位于一个可重用的库中。访问 Spring Initializr,将项目命名为 processors,并添加Spring Native。在 IDE 中打开生成的项目,在pom.xml文件中移除build节点,这样会删除所有的 Maven 插件配置。接下来,我们需要手动添加一个新的库:

<dependency>  <groupId>org.springframework.experimental</groupId>  <artifactId>spring-aot</artifactId>  <version>${spring-native.version}</version>  <scope>provided</scope></dependency>

Maven 构建会生成一个常规的 Java “.jar”制品,我们可以像对待任意 Maven “.jar”那样对其进行安装和部署:mvn -DskipTests clean install。

这个新的库会引入新的类型,包括:

  • BeanFactoryNativeConfigurationProcessor:它在构建期的行为等同于BeanFactoryPostProcessor

  • BeanNativeConfigurationProcessor:它在构建期的行为等同于BeanPostProcessor

我发现自己大多数时候都在和这两个接口打交道。在每个接口中,我们都可以得到一个可供探测的引用以及一个注册表的引用,我们据此能够以编程的方式向注册表中贡献线索内容。如果使用BeanNativeConfigurationProcessor,我们会得到一个 bean 元数据的实例,它代表了 bean factory 中的一个 bean。如果使用BeanFactoryNativeConfigurationProcessor的话,我们会得到对整个BeanFactory本身的引用。需要注意的是,我们只能使用 bean 的名称和BeanDefinition实例,无法使用真正的 bean。BeanFactory能够知道所有在运行时会存在的对象,但是它此时还没有实例化它们。相反,它的作用是帮助我们理解运行中的应用中 bean 的样子,比如类、方法等,以便于得到适当的线索信息。

我们不能以常规 Spring bean 的形式来注册这些Processor类型,而是要在spring.factories服务加载器中进行注册。所以,鉴于BeanFactoryNativeConfigurationProcessor的实现名为com.example.nativex.MyBeanFactoryNativeConfigurationProcessorBeanNativeConfigurationProcessor的实现名为com.example.nativex.MyBeanNativeConfigurationProcessor,spring.factories文件如下所示:

org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.BeanFactoryNativeConfigurationProcessor=\  com.example.nativex.MyBeanFactoryNativeConfigurationProcessororg.springframework.aot.context.bootstrap.generator.infrastructure.nativex.BeanNativeConfigurationProcessor=\  com.example.nativex.MyBeanNativeConfigurationProcessor

借助这些 Processor 类型,我们可以很容易地在 Spring Native 应用中消费集成功能或库。我写了一个库(com.joshlong:hints:0.0.1),里面包含了各种集成功能(如 Kubernetes Java 客户端、Fabric8 Kubernetes Java 客户端、Spring GraphQL、Liquibase 等),这些集成功能不大适合放到官方的 Spring Native 版本中。目前这就是一个大杂烩,但结果是很酷的:只要把相关的功能添加到 classpath 中,就像 Spring Boot 的自动配置一样,我们就会得到一个很棒的结果!

9 更多信息

我希望你能够从这个关于 Spring Native 原生可执行文件的简单介绍中有所收获。请继续关注 Spring 博客 和我的 Twitter (@starbuxman) ,以获取更多信息。

作者介绍:

Josh Long(Twitter 为 @starbuxman)是第一个 Spring 开发者倡导者,始于 2010 年。Josh 是一个 Java Champion,写了 6 本图书(包括 O'Reilly 的“Cloud Native Java: Designing Resilient Systems with Spring Boot, Spring Cloud, and Cloud Foundry”和“Reactive Spring”)和制作了许多畅销的视频培训(包括与 Spring Boot 联合创始人 Phil Webb 合作的“Building Microservices with Spring Boot Livelessons”),并且是开源贡献者(Spring Boot、Spring Integration, Spring Cloud、Activiti 和 Vaadin 等)、播客(“A Bootiful Podcast”)和 YouTuber。

原文链接:

https://www.infoq.com/articles/native-java-spring-boot/

点击底部阅读原文 访问 InfoQ 官网,获取更多精彩内容!

今日好文推荐

微软开始封禁商业开源:从 App Store 入手,7 月 16 日生效?!

迁移进行时,告别 GitHub 的时候到了?

腾讯安全回应数据产品线裁撤;马斯克称终止收购推特;拼多多“砍一刀”涉嫌欺诈案一审宣判 |Q 资讯

GitLab 技术选型为何如此不同:坚持用过气 Web 框架十多年、坚决不用微服务

картина

点个在看少个 bug 👇