imagen

Autor | Josh Long
Traductor | Zhang Weibin
Planificación | Ding Xiaoyun
Este artículo es parte de la serie "Compilaciones nativas potencian Java". Puede recibir notificaciones de actualizaciones de nuevos artículos suscribiéndose a RSS.

Java está dominando las aplicaciones empresariales. Pero en la nube, adoptar Java es más costoso que sus competidores. La compilación nativa con GraalVM reduce el costo de Java en la nube: crea aplicaciones que se inician más rápido y usan menos memoria.

La compilación nativa plantea muchas preguntas para los usuarios de Java: ¿Cómo cambiará Java nativo la forma en que se realiza el desarrollo? ¿Cuándo deberíamos cambiar a Java nativo? ¿Cuándo se debe evitar pasar a Java nativo? Para usar Java nativo, ¿qué framework deberíamos usar? Los artículos de esta serie responderán estas preguntas.
1La comunidad de Java lo abarca todo

Sabemos que Java es un ecosistema increíble y es difícil enumerar lo que es mejor para nosotros. Al hacerlo, la lista parece interminable. Sin embargo, no es tan difícil nombrar algunas de sus deficiencias. Como se describe en esta serie de artículos, las aplicaciones que se ejecutan en JRE suelen tardar 10 segundos o más en iniciarse y requieren cientos o gigabytes de memoria.

Tal desempeño no está en la posición de liderazgo en el mundo actual. Están surgiendo algunas áreas y oportunidades nuevas: ofertas de función como servicio, contenedorización y orquestación de contenedores. Tienen una cosa en común, es decir, tienen altos requisitos de velocidad de inicio y consumo de memoria.

2¡Hacia GraalVM!

GraalVM ofrece un camino a seguir, pero también tiene un precio. GraalVM es una alternativa a OpenJDK que tiene una herramienta llamada Native Image que admite compilación anticipada (AOT).

La compilación AOT tiene algunas diferencias con la compilación Java normal. Como se describe en el primer artículo de esta serie, Native Image elimina "todo lo innecesario" en Java. Entonces, ¿cómo sabe Native Image cuáles son innecesarios en Java o Spring Boot?

Native Image sondea nuestro código fuente y determina todo el código accesible, es decir, el código al que se puede vincular a través de llamadas o usos de nuestro código. Todo lo demás, ya sea en el classpath de la aplicación o en el JRE, se considera innecesario y se descarta.

El problema surge cuando hacemos algo que Native Image no sabe exactamente qué hacer con ello. Después de todo, Java es un lenguaje muy dinámico. Es posible crear una aplicación Java que, en tiempo de ejecución, compile una cadena en un archivo de clase Java válido en el sistema de archivos, lo cargue en un ClassLoader y luego use la reflexión para crear una instancia o un proxy para él. También podríamos serializar la instancia en el disco y cargarla en otra JVM. En este proceso, es posible que no necesitemos vincular clases más específicas que java.lang.Object . Sin embargo, todos estos enfoques no funcionarán correctamente en Java nativo si estos tipos no se colocan en el montón ejecutable nativo.

Sin embargo, no hemos perdido nada. Podemos decirle a Native Image qué tipos mantener en un archivo de configuración, de modo que aún funcione cuando use funciones como reflexión, proxies, carga de recursos de classpath, JNI, etc. en tiempo de ejecución.

En este momento, el ecosistema de Java y Spring es enorme. Sería un fastidio configurarlo todo. Entonces tenemos dos opciones: 1) Enseñar a Spring a evitar el uso de estos mecanismos tanto como sea posible, o 2) Enseñar a Spring a proporcionar tantos archivos de configuración como sea posible.Este archivo de configuración debe contener Spring Framework y Spring Boot, y hasta cierto punto Contiene características de integración de terceros compatibles con Spring Boot. Spoiler amigable, ¡necesitamos ambas opciones!

Para ejecutar el proyecto de muestra, debe tener instalado GraalVM en su máquina. GraalVM tiene una guía de instalación. Si usa una Mac, también puede usar SDKMAN! para instalar GraalVM.

3nativo de primavera

El equipo de Spring inició el proyecto Spring Native en 2019, que introdujo la compilación de programas ejecutables nativos en el ecosistema Spring Boot. Ha proporcionado sitios de investigación para muchas modalidades diferentes. Sin embargo, Spring Native no cambia fundamentalmente Framework 5.xo Spring Boot 2.x. Y de ninguna manera es el final, solo el primer paso en un largo viaje: ha probado muchos conceptos para la próxima generación de Spring Framework (6.x) y Spring Boot (3.x), ambos esperados en 2022 Publicado en una fecha posterior. ¡Estos proyectos de próxima generación estarán más optimizados, por lo que el futuro parece muy brillante! Dado que estas versiones aún no se han lanzado, analizaremos Spring Native en este artículo.

Spring Native convertirá el código fuente enviado a Native Image. Por ejemplo, Spring Native convertirá el mecanismo de carga del servicio spring.factories en clases estáticas para que las aplicaciones Spring Native sepan cómo usarlas. Convierte todas las clases de configuración de Java (clases anotadas con @Configuration ) en la configuración funcional de Spring, eliminando la reflexión de la aplicación y sus dependencias.

Spring Native también analiza automáticamente nuestro código, detecta escenarios que requieren la configuración de GraalVM y proporciona esa configuración mediante programación. Spring Native proporciona clases de sugerencias para Spring, Spring Boot y la integración de terceros.

4La primera aplicación Spring Native: JPA, Spring MVC y H2

Comenzamos con Spring Native de la misma manera que todos los demás proyectos de Spring: vaya a Spring Initializr, presione cmd + B (o Ctrl + B) o Agregar dependencias y seleccione Spring Native.

Spring Initializr configura las compilaciones de Apache Maven y Gradle. Luego, simplemente agregue las dependencias necesarias. Comencemos con algunas dependencias típicas. Cambie el nombre del artefacto a jpa y luego agregue las siguientes dependencias: Spring Native Spring Web Lombok H2 Database y Spring Data JPA . Asegúrate de elegir Java 17, por supuesto que puedes elegir Java 11, pero es como si estuvieras corriendo por el mundo con una chica de goma, lo que parece bastante tonto, ¿verdad? Haga clic en "Generar" para descomprimir el proyecto generado e importarlo a su IDE favorito.

Este ejemplo es muy simple, cambie la clase JpaApplication.java para que se vea así:

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

También podemos compilar y ejecutar pruebas como ejecutables nativos. Pero cabe señalar que algunos contenidos no funcionan bien, como Mockito. Modificamos la clase de prueba JpaApplicationTests.java para que se vea así:

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

En este artículo, mostraré los comandos en macOS. Para Windows y Linux, ajuste en consecuencia.

Podemos ejecutar la aplicación y las pruebas de la forma habitual, como ejecutando el comando mvn spring-boot:run en la terminal. De hecho, es una buena idea ejecutar estos ejemplos directamente, al menos para asegurarse de que la aplicación funcione correctamente. Sin embargo, este no es nuestro propósito. En su lugar, queremos compilar la aplicación y sus pruebas en una aplicación GraalVM nativa.

Si observa el archivo pom.xml, verá una gran cantidad de configuración adicional que crea la imagen nativa de GraalVM y agrega un perfil de Maven (llamado native ) para admitir la creación de ejecutables nativos. Podemos usar el paquete mvn clean para compilar la aplicación como de costumbre. Las aplicaciones también se pueden compilar de forma nativa utilizando mvn -Pnative clean package . Lo que debe tener en cuenta es que debe configurar GraalVM como su propio JDK. Este proceso tomará unos minutos, así que ahora es el momento de tomar una taza de té, café, agua u otra bebida. Simplemente lo hice porque lo necesitaba. Cuando volví, vi un resultado como este:

...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] ------------------------------------------------------------------------

Nos tomó tres minutos compilar las pruebas nativas y, si las pruebas fueron exitosas, la aplicación nativa en sí. En el proceso, Native Image utilizó hasta 10,25 GB de RAM. Para acelerar la explicación, omitiré el proceso de compilación y ejecución de las pruebas más adelante en este artículo. Entonces, cuando compilemos el siguiente ejemplo, se utilizarán los siguientes comandos:

mvn -Pnative -DskipTests clean package

Los tiempos de compilación varían según el classpath de la aplicación. Como regla general, la mayoría de mis compilaciones demorarán entre 1 minuto y 90 segundos si se omiten las pruebas de compilación. Por ejemplo, esta aplicación incluye JPA (e Hibernate), Spring Data, base de datos H2, Apache Tomcat y Spring MVC.

Ejecute la aplicación:

./target/jpa

En mi máquina, verás:

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

¡Muy bonito, 80 milisegundos u 80 milésimas de segundo! Aún mejor, la aplicación casi no usa memoria. Utilizo el siguiente script para probar el RSS de la aplicación (tamaño del conjunto residente).

#!/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"

Necesitamos el ID de proceso (PID) de la aplicación en ejecución. En macOS, puedo obtenerlo ejecutando pgrep jpa . El script que uso se ve así:

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

Alrededor de 97 MB de RAM. Este valor puede variar según el sistema operativo y la arquitectura en la que se ejecuta la aplicación. Este valor es diferente cuando se ejecuta la aplicación en Linux en Intel y macOS en M1. Actualmente, esta es una mejora significativa con respecto a las aplicaciones JRE, pero aún no es la mejor.

Me encanta la programación reactiva y creo que ahora se adapta mejor a mi carga de trabajo. Creé una aplicación reactiva similar. No solo consumió menos espacio (por varias razones, incluida la compatibilidad de Spring Data R2DBC con la sintaxis de registro de Java 17), el tiempo de compilación de la aplicación fue de 1:14 (casi dos minutos más rápido) y el tiempo de inicio fue de 0,044 segundos. Ocupa un 35 % menos de memoria con unos 63,5 MB. La aplicación también puede manejar muchas más solicitudes por segundo. Por lo tanto, compila y ejecuta más rápido, es más eficiente en memoria, se inicia más rápido y puede manejar un tráfico más alto. Lo que digo es que es una buena oferta en todos los sentidos.

5aplicación integrada

Spring no son solo puntos finales HTTP, hay muchas otras cosas. Incluye muchos marcos como Spring Batch, Spring Integration, Spring Security, Spring Cloud y una lista cada vez mayor de otros, todos los cuales brindan un buen soporte para Spring Native.

Veamos una aplicación de muestra de Spring Integration. Spring Integration es un marco que admite la integración de aplicaciones empresariales (EAI). El libro seminal Enterprise Integration Patterns de Gregor Hohpe y Bobby Woolf proporciona un término genérico para los patrones de integración. Spring Integration proporciona abstracciones para implementar estos patrones.

Vuelva a Spring Initializr, nombre la integración del proyecto, seleccione Java 17, agregue Spring Native , Spring Integration , Spring Web y haga clic en Generate . Necesitamos pom.xmlagregar manualmente una dependencia en el archivo:

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

Modifique el código de IntegrationApplication.java de la siguiente manera:

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

La aplicación es muy simple: monitorea un directorio ( $HOME/Desktop/integration/in ) en busca de nuevos archivos. Una vez que se encuentra un nuevo archivo, crea una copia Stringcon el contenido opuesto exacto del archivo de origen y lo escribe en $HOME/Desktop/integration/out . En el JRE, el tiempo de inicio de la aplicación es de 0,429 segundos. Eso es genial, veamos qué cambios hace para convertirlo en un ejecutable de GraalVM.

mvn -Pnative -DskipTests clean package

El tiempo de compilación de la aplicación fue de 55,643 segundos. Tardó 0,029 segundos en iniciarse ( ./target/integration ) y ocupó 35,5 MB de RAM. ¡nada mal!

Como podemos ver, no existe el llamado resultado típico. La entrada del proceso de compilación tiene una gran influencia en la salida.

6Poner la aplicación en producción

En algún momento, es posible que queramos implementar nuestra aplicación en producción, que hoy en día suele ser Kubernetes. Kubernetes se ejecuta como contenedores. El concepto central detrás del proyecto Buildpacks es centralizar y reutilizar la práctica habitual de convertir artefactos de aplicaciones en contenedores. Hay muchas maneras de usar Buildpacks, puede usar la CLI del paquete, puede usar KPack en un clúster de Kubernetes o puede usar el complemento de compilación Spring Boot. Usaremos el último método porque solo requiere Docker Desktop. Visite el sitio web oficial para obtener más información sobre Docker Desktop.

mvn spring-boot:build-image

Este comando crea el ejecutable nativo dentro del contenedor, por lo que obtenemos un contenedor de Linux que contiene los archivos binarios nativos de Linux. Luego podemos etiquetarlo con docker tag y docker push y empujar al registro de contenedores de nuestra elección. Mientras escribo esto en mayo de 2022, los Docker Buildpacks todavía son un poco inestables en las Mac con arquitectura M1. Pero creo que esto se resolverá pronto.

7Proporcione algunas pistas para la imagen nativa

En los ejemplos que hemos visto hasta ahora, no hemos hecho mucho para que la aplicación se ejecute como un ejecutable nativo. De acuerdo con la configuración predeterminada anterior, puede ejecutarse de forma natural. En la mayoría de los casos, esta facilidad de uso es lo que queremos. Pero a veces, necesitamos dar algunas pistas a Native Image, como mencioné anteriormente en el capítulo "¡Hacia GraalVM!".

Veamos otro ejemplo. Primero, vaya a Spring Initializr, nombre las extensiones del proyecto, seleccione Java 17 y agregue Spring Native , luego haga clic en Generate . A continuación, agregaremos manualmente una dependencia que no existe en Initialzr:

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

Nuestro objetivo aquí es ver qué sucede cuando algo sale mal. Spring Native proporciona un conjunto de señales que nos permiten mejorar fácilmente la configuración predeterminada. Modifique ExtensionsApplication.java para que se vea así:

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

Esta muestra contiene tres instancias de ApplicationRunner que la aplicación Spring ejecutará cuando se inicie. Cada bean hará algo que hará que la imagen nativa de GraalVM sea incómoda. Sin embargo, en la JVM funcionan bien: 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 框架十多年、坚决不用微服务

imagen

点个在看少个 bug 👇