1、试用环境准备
  • 准备好centos7系统
  • 安装好jdk17
  • 安装好docker20

实践环境

2、编写项目并构建native镜像
  • 项目准备

start.spring.io创建一个新的项目,项目名为mynative,在ADD DEPENDENCIES中可以看到排在第一位的依赖就是spring-native,不过目前还处理试验阶段,要谨慎使用;

spring-native依赖

添加几个基础依赖,主要是用来简单测试一下一个http请求到web应用,并将请求信息保存到内存数据库h2中,然后打开内存数据库的console控制台查询校验数据,看数据是否正常;

基础依赖

  • mynative项目源码结果如下

代码结构树

  • 代码片断
RequestLogController类
---
@Slf4j
@RestController
@RequestMapping("/")
public class RequestLogController {

    @Autowired
    private RequestLogRepository requestLogRepository;

    @GetMapping("/log")
    public String log(HttpServletRequest request) {
        RequestLog entity = new RequestLog();
        entity.setId(UUID.randomUUID().toString().toUpperCase());
        Enumeration<String> names = request.getHeaderNames();
        StringBuilder builder = new StringBuilder(request.getRequestURL()).append("\r\n");
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            builder.append(name).append(" = ").append(request.getHeader(name)).append("\r\n");
        }
        entity.setContent(builder.toString());
        entity.setLogDate(new Date());
        requestLogRepository.save(entity);
        return HttpStatus.OK.toString();
    }

}
---
RequestLog类
---
@Getter
@Setter
@Entity
public class RequestLog {

    @Id
    private String id;

    @Column(length = 1024)
    private String content;

    private Date logDate;

}
---
RequestLogRepository类
---
@Repository
public interface RequestLogRepository extends JpaRepository<RequestLog, String> {

}
---
MyNativeApplication类
---
@EnableJpaRepositories
@SpringBootApplication
public class MyNativeApplication {

	public static void main(String[] args) {
		SpringApplication.run(MyNativeApplication.class, args);
	}

}
---
application.yaml配置文件
---
server:
  port: 8888

logging:
  level:
    root: info
spring:
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
  jpa:
    show-sql: true
---

简单起见,需要在配置文件中开启内存数据库h2的管理控制台,并且允许web访问(不仅是通过localhost),如上面的application.yaml配置

  • 编译项目
mvn spring-boot:buid-image

首次编译期间会去下载以下镜像,耗时较长,而且这个构建过程比较耗cpu资源,其使用率经常保持在较高的水平,大概10分钟左右完成镜像的构建;

docker.io/paketobuildpacks/builder:tiny
docker.io/paketobuildpacks/run:tiny-cnb

构建镜像片断

[INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 59%
[INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 71%
[INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 84%
[INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 100%
[INFO]  > Pulled builder image 'paketobuildpacks/builder@sha256:782b8eff60d91ae734bd82c40c973b0f002c16ca881e91aeb977984699af666b'
[INFO]  > Pulling run image 'docker.io/paketobuildpacks/run:tiny-cnb' 100%
[INFO]  > Pulled run image 'paketobuildpacks/run@sha256:ecc1c097c43a313151bbcb544b4b6c17ec808e93707a777e01c1e5b37d4dd6bc'
......
[INFO]     [creator]     ===> EXPORTING
[INFO]     [creator]     Reusing layer 'paketo-buildpacks/ca-certificates:helper'
[INFO]     [creator]     Adding 1/1 app layer(s)
[INFO]     [creator]     Adding layer 'launcher'
[INFO]     [creator]     Adding layer 'config'
[INFO]     [creator]     Reusing layer 'process-types'
[INFO]     [creator]     Adding label 'io.buildpacks.lifecycle.metadata'
[INFO]     [creator]     Adding label 'io.buildpacks.build.metadata'
[INFO]     [creator]     Adding label 'io.buildpacks.project.metadata'
[INFO]     [creator]     Adding label 'org.opencontainers.image.title'
[INFO]     [creator]     Adding label 'org.opencontainers.image.version'
[INFO]     [creator]     Adding label 'org.springframework.boot.version'
[INFO]     [creator]     Setting default process type 'web'
[INFO]     [creator]     Saving docker.io/library/native:0.0.1...
[INFO]     [creator]     *** Images (9ebd0ad2764f):
[INFO]     [creator]           docker.io/library/native:0.0.1
[INFO]     [creator]     Reusing cache layer 'paketo-buildpacks/bellsoft-liberica:native-image-svm'
[INFO]     [creator]     Reusing cache layer 'paketo-buildpacks/syft:syft'
[INFO]     [creator]     Adding cache layer 'paketo-buildpacks/native-image:native-image'
[INFO] 
[INFO] Successfully built image 'docker.io/library/native:0.0.1'

编译成功后生成了一个native:0.0.1镜像如下,可以看到镜像大小有145M,可以看到created时间不对,这里是42 years age相应的解析https://medium.com/buildpacks/time-travel-with-pack-e0efd8bf05db,大意是构建镜像的sha会随文件时间戳而变化

生成镜像

3、运行native并观察结果
  • 运行native镜像
docker run -it --rm -p 8888:8888 native:0.0.1

可以看到启动速度非常快,在1秒内完成了启动,正常通过java -jar target/native-0.0.1.jar启动需要约5秒钟,有显著的差异,启动的过程中有一个细节可以看到H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:f3f1583f-1323-4dbe-914d-cc342d73a3d3',由于在spring.datasource.url没有指定路径,所以默认会随机生成一个uuid字符作为数据库名称,若指定一个数据库名则会直接使用指定的名称;

  • 模拟数据

通过curl http://192.168.91.132:8888/log模拟访问先造一些数据,可以看到控制台会有一些insert sql语句打印出来,数据可以正常写入了。然后打开H2控制台 http://192.168.91.132:8888/h2-console ,发现报异常了,不能正常打开页面,报错如下

h2-console报错异常

4、解决native镜像运行h2-console无法访问的问题

根据报错的提示跟踪一下源码org.h2.server.web.WebServer

org.h2.server.web.WebServer源码

此处报空指针异常是transnull,即说明getFile返回为null,跟踪getFile方法,可以看到是通过Utils.getResource("/org/h2/server/web/res/" + file)获取,继续跟踪getResource方法可以发现资源是从RESOURCES中获取,而这个资源是loadResource方法从Utils.class.getResourceAsStream("data.zip")此处获取,即在Utils包的目录下拿到一个data.zip文件。

org.h2.server.web.WebServer源码

接着打开h2-1.4.200.jar包,进到目录org\h2\util中可以找到data.zip

h2-console的data.zip资源包

结果就清晰了,可能是spring-native构建镜像的时候将此资源文件排除了,将其放到项目的resources/org/h2/util中,再一次构建镜像运行,就可以正常看到h2数据库的控制台了,多请求几次测试url,即可查到数据。

spring-native

spring-native

5、最新版支持AOP切面
  • 编写一个日志切面

用此切面拦截Controller中的日志log请求,查看效果

package com.minxyz.mynative.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Around("execution(* com.minxyz.mynative.controller.RequestLogController.log(..))")
    public Object processInsertRoleConfig(ProceedingJoinPoint point) throws Throwable {

        log.info("enter LogAspect function");
        Object[] args = point.getArgs();
        return point.proceed(args);
    }

}

同时需在启动类中增加@AotProxyHint注解配置,指定类

@AotProxyHint(targetClass=com.minxyz.mynative.controller.RequestLogController.class,proxyFeatures = ProxyBits.IS_STATIC)

附:源码 https://gitee.com/viturefree/spring-native-test.git


赞赏(Donation)
微信(Wechat Pay)

donation-wechatpay