编译native镜像的两种方法:本地 & docker

2024/08/26 GraalVM 共 4974 字,约 15 分钟
Bob.Zhu

为了避免因为编译平台不一致导致的部署文件不一样,可以使用Docker进行编译,以保证编译环境的一致性。 以下是一些使用Docker进行编译的一些方法,和编译成native镜像的问题和解决方案。

开发测试

Tips:启动镜像并进入容器内部的方法

docker run --rm -it --name temp-container --entrypoint sh <镜像名称>

本地JVM编译测试

# 编译环境
export GRAALVM_HOME="/Library/Java/LibericaNativeImageKit/liberica-vm-full-24.0.2-openjdk22/Contents/Home"
export JAVA_HOME=$GRAALVM_HOME
export PATH="$JAVA_HOME/bin:$PATH"
# 编译
./mvnw clean package -DskipTests

Docker JVM编译测试

docker run -it --rm --name temp-container \ 
  -v $PWD:/home/build \
  -v $HOME/.m2:/root/.m2 \
  bellsoft/liberica-native-image-kit-container:jdk-22-nik-24.0.2-glibc \
  /bin/sh -c "cd /home/build && ./mvnw clean package -DskipTests"

本地Native编译测试

# 编译环境
export GRAALVM_HOME="/Library/Java/LibericaNativeImageKit/liberica-vm-full-24.0.2-openjdk22/Contents/Home"
export JAVA_HOME=$GRAALVM_HOME
export PATH="$JAVA_HOME/bin:$PATH"
# 编译
./mvnw -Pnative clean -DskipTests native:compile

Docker Native编译测试

# bellsoft的NIK镜像
docker pull bellsoft/liberica-native-image-kit-container:jdk-22-nik-24.0.2-glibc
# 映射本地文件和maven仓库进行编译
docker run -it --rm --name temp-container \
  -v $PWD:/home/build \
  -v $HOME/.m2:/root/.m2 \
  bellsoft/liberica-native-image-kit-container:jdk-22-nik-24.0.2-glibc \
  /bin/sh -c "cd /home/build && ./mvnw -Pnative clean -DskipTests native:compile"
# 运行测试
docker run -it --rm --name temp-container \
  -v $PWD/target/flutter-flex-backend:/home/myapp/flutter-flex-backend \
  -p 8080:8080 \
  bellsoft/alpaquita-linux-base:stream-glibc-240821 \
  /bin/sh -c "cd /home/myapp && ./flutter-flex-backend"

部署

JVM Docker 镜像

docker build -f Dockerfile.jvm -t mytool-backend-jvm:last .
docker run --name mytool-backend-jvm -d -p 8080:8080 --restart unless-stopped mytool-backend-jvm:last

Native Docker 镜像

docker build -f Dockerfile.native -t mytool-backend-native:last .
docker run --name mytool-backend-native -d -p 8080:8080 --restart unless-stopped mytool-backend-native:last

编译Spring项目Native问题汇总

org.mybatis.spring.mapper.ClassPathMapperScanner. Cause: java.lang.NullPointerException

No qualifying bean of type ‘java.lang.Class<?>’ available

修改 MyBatisNativeConfiguration 的 resolveMapperFactoryBeanTypeIfNecessary 方法解决下面的问题:

2024-08-25 08:49:27.837 mytool-backend-backend MateBook-Pro-2019.local [main]  WARN  o.s.c.s.AbstractApplicationContext -633- 
Exception encountered during context initialization - cancelling refresh attempt: 
org.springframework.beans.factory.UnsatisfiedDependencyException: 
    Error creating bean with name 'memberController': 
        Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'userService': 
        Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'sessionRecordService': 
        Unsatisfied dependency expressed through field 'mapper': Error creating bean with name 'sessionRecordMapper': 
        Unsatisfied dependency expressed through constructor parameter 0: 
            No qualifying bean of type 'java.lang.Class<?>' available: 
            expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

问题参考:

Caused by: java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 24

liberica-vm-full-23.1.4-openjdk21 升级为 liberica-vm-full-24.0.2-openjdk22,报错消失。

Ensure that the compiler uses the ‘-parameters’ flag

2024-08-28 10:42:49.361 [http-nio-8899-exec-1] ERROR c.b.s.global.exception.GlobalExceptionHandler - Name for argument of type [java.lang.String] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the '-parameters' flag.
java.lang.IllegalArgumentException: Name for argument of type [java.lang.String] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the '-parameters' flag.
	at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.updateNamedValueInfo(AbstractNamedValueMethodArgumentResolver.java:187)

解决方法,添加 <parameters>true</parameters> 配置:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.8.1</version>
  <configuration>
    <parameters>true</parameters>
  </configuration>
</plugin>

修改Java代码:

public ResponseEntity<CommandResponse> update(
    @PathVariable long param1,
    @RequestParam String param2) {
  // ...
}

改为:

public ResponseEntity<CommandResponse> update(
    @PathVariable("param1") long param1,
    @RequestParam("param2") String param2) {
  // ...
}

此配置用于启用 Java 编译器的 -parameters 标志。这个标志会在编译时保留方法参数的名称信息, 使得在运行时可以通过反射 API 获取参数名称。这在某些框架(如 Spring)中非常有用, 因为它们可以利用这些参数名称来进行依赖注入或参数绑定。

MapStruct

从静态方法调用改为实例方法调用,生成的代码包含@Component注解,解决native编译后找不到实现类的问题:


@Mapper
public interface UserConvertor {
  UserConvertor INSTANCE = Mappers.getMapper(UserConvertor.class);

  User regParamToPo(RegisterParam regParam);
}

改为:

import static org.mapstruct.MappingConstants.ComponentModel.SPRING;

@Mapper(componentModel = SPRING)
public interface UserConvertor {
  User regParamToPo(RegisterParam regParam);
}

MapScan

org.mybatis.spring.annotation.MapperScan 不要扫描 MapStruct 的 Mapper注解,否则会出错。

参考资料

文档信息

Search

    Table of Contents