目录
- 设置上下文
- 示例应用程序
- 常见变化
- 迁移 web 层
- 迁移数据访问层
- 迁移执行器
设置上下文
JVM是一项伟大的技术。现代版本将运行的字节码编译为本机代码,具体取决于现有的工作负载。出于这个原因,JVM 应用程序在运行时性能方面与本机可执行文件不相上下,甚至胜过本机可执行文件。
JVM 应用程序有一个预热时间,在此期间它们性能不佳。在运行时加载类没有帮助。Spring 和 Jakarta EE 等框架一直在使用类路径扫描和反射,这使得启动时间更长。对于长时间运行的进程,例如传统的应用程序服务器,这不是问题。
在容器的上下文中,它是。因为人们把容器当作牛而不是宠物来处理,所以 Kubernetes等平台会定期杀死 pod 并安排新的 pod。启动时间越长,JVM 的相关性就越低。在需要快速自动缩放 pod 数量的无服务器环境中,情况会变得更糟。
为了赶上潮流,Oracle 提供了SubstrateVM。GraalVM 的子组件 SubstrateVM 允许将 JVM 字节码转换为本机可执行文件。为此,SubstrateVM 编译字节码AOT。出于这个原因,您需要在运行时显式地向它提供 JVM 上可用的信息。例如反射的情况。请注意,某些 JVM 功能未移植到 GraalVM。此外,AOT编译是一个耗时的过程。
结果是,一方面,我们拥有 JVM 及其框架所利用的所有功能;另一方面,我们有本机可执行文件,需要精细的手动配置和大量的构建时间。
新一代的框架已经产生,旨在找到一个中间地带 ,即Micronaut 和 Quarkus。它们都旨在生成字节码 AOT。请注意,此 AOT 与上面提到的不同。两个框架都没有在运行时使用昂贵的反射,而是在构建时生成额外的类。这也使我们能够避免在启动时进行类路径扫描。简而言之,这个想法是关于在构建时提供尽可能多的代码。
示例应用程序
我希望迁移的示例应用程序足够简单,这样我就可以自己迁移它,但又不至于变得微不足道。它由以下部分组成:
- Spring MVC实现的控制器层
- 由 Spring Data JPA 实现的存储库层
- 一个 JPA 实体
- 通过 Spring Boot 在启动时生成模式和数据插入
- Spring Boot 执行器,启用了
health
和beans
端点,无需身份验证即可访问
该应用程序是用 Kotlin 编写的。我将使用 H2 作为数据库来简化整个设置。
常见变化
第一个变化是替换父 POM。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<parent>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-parent</artifactId>
<version>2.1.3</version>
</parent>
因为 Micronaut 在构建时生成字节码,所以我们需要在编译期间添加注释处理器。因此,最后的第二步是在 POM 中配置它。
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
...
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId> <!-- 1 -->
<version>${micronaut.version}</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>io.micronaut.data</groupId>
<artifactId>micronaut-data-processor</artifactId> <!-- 2 -->
<version>${micronaut.data.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
...
</executions>
...
</plugin>
- 处理依赖注入
- 处理持久化相关的类
您可以通过查看target/classes
文件夹来检查那些额外的类。例如,示例应用程序显示以下内容:
$Person$Introspection$$0.class PersonRepository$Intercepted$$proxy0.class
$Person$Introspection$$1.class PersonRepository$Intercepted$$proxy1.class
$Person$Introspection$$2.class PersonRepository$Intercepted$$proxy10.clas
$Person$Introspection$$3.class PersonRepository$Intercepted$$proxy2.class
$Person$Introspection.class PersonRepository$Intercepted$$proxy3.class
$Person$IntrospectionRef.class PersonRepository$Intercepted$$proxy4.class
$PersonControllerDefinition$$exec1.class PersonRepository$Intercepted$$proxy5.class
$PersonControllerDefinition$$exec2.class PersonRepository$Intercepted$$proxy6.class
$PersonControllerDefinition.class PersonRepository$Intercepted$$proxy7.class
$PersonControllerDefinitionClass.class PersonRepository$Intercepted$$proxy8.class
$PersonRepository$InterceptedDefinition.class PersonRepository$Intercepted$$proxy9.class
$PersonRepository$InterceptedDefinitionClass.class PersonRepository$Intercepted.class
Person.class PersonRepository.class
PersonController.class SpringToMicronautApplicationKt.class
Micronaut 创建包含Introspection
并Intercepted
通过kapt
.
为了启动应用程序,Spring Boot 引用了一个类。
@SpringBootApplication
class SpringToMicronautApplication
fun main(args: Array<String>) {
runApplication<SpringToMicronautApplication>(*args)
}
Micronaut 允许我们只使用标准main
函数。
fun main(args: Array<String>) {
build()
.args(*args)
.packages("ch.frankel.springtomicronaut")
.start()
}
Spring Boot 插件可以main
“自动”找到函数。在 Micronaut 中,当前版本要求您在 POM 中显式设置它:
<properties>
...
<exec.mainClass>ch.frankel.s2m.SpringToMicronautApplicationKt</exec.mainClass>
</properties>
迁移 web 层
迁移到 web 层需要:
- 用相关的 Micronaut 依赖项替换 Spring Boot 启动器
- 用 Micronaut 的注释替换 Spring Boot 的注释
为了使应用程序成为 Web 应用程序,Micronaut 要求添加嵌入式服务器依赖项。Tomcat、Jetty 和 Undertow 可用。由于Spring Boot默认是Tomcat,所以我们使用Tomcat:
<dependency>
<groupId>io.micronaut.servlet</groupId>
<artifactId>micronaut-http-server-tomcat</artifactId>
<scope>runtime</scope>
</dependency>
Spring 和 Micronaut 的注解几乎是一一对应的。使用 Micronaut 只是使用一个包的注释而不是另一个包的注释的问题。Controller
不同之处在于 Spring 提供了使用专门的注解将序列化为 JSON 的能力, @RestController
. Micronaut 不需要也不需要在Controller
注解上设置属性。
春天 | 微航 |
o.s.w.b.a.RestController | i.m.h.a.Controller(produces = [TEXT_JSON]) |
o.s.w.b.a.GetMapping | i.m.h.a.Get |
o.s.w.b.a.PathVariable | i.m.h.a.PathVariable |
o.s.w.b.a=org.springframework.web.bind.annotation
i.m.h.a=io.micronaut.http.annotation
迁移数据访问层
要迁移到数据访问层,必须:
- 使用 Micronaut 的依赖项而不是 Spring Boot 的
- 将 Micronaut 的 Spring Boot 替换
Repository
为 Micronaut 的 - 使用 Micronaut 创建模式并加载初始数据
要创建数据源和连接池,Spring Boot 需要一个 Spring Data starter 和相关的驱动程序。Micronaut 需要三个不同的部分:
- 数据访问依赖
- 驱动程序依赖
- 连接池依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>io.micronaut.data</groupId>
<artifactId>micronaut-data-hibernate-jpa</artifactId>
<version>${micronaut.data.version}</version>
</dependency>
<dependency>
<groupId>io.micronaut.sql</groupId>
<artifactId>micronaut-jdbc-hikari</artifactId>
</dependency>
请注意,如果您忘记了连接池,您将在运行时遇到此错误:
No backing RepositoryOperations configured for repository. Check your configuration and try again
Spring Data JPA 在运行时生成存储库的实现。Micronaut Data 在构建时生成它们。对于开发人员来说,主要区别在于存储库接口必须使用 Micronaut 的@Repository
.
@Repository
interface PersonRepository : CrudRepository<Person, Long>
需要配置 Micronaut 以扫描存储库和实体:
jpa.default:
packages-to-scan:
- 'ch.frankel.springtomicronaut'
要创建模式,您可以通过两种不同的方式配置 Spring Boot:依赖 Hibernate 的模式创建或create.sql
在类路径的根目录中提供一个文件。同样,要插入初始数据,您可以添加一个data.sql
.
Micronaut 不提供开箱即用的机制来插入数据。但它提供了与 Flyway 的集成。放置 Flyway 的迁移的默认位置是db/migration
,就像 Spring Boot 一样。
<dependency>
<groupId>io.micronaut.flyway</groupId>
<artifactId>micronaut-flyway</artifactId>
<version>2.1.1</version>
</dependency>
jpa.default:
properties.hibernate:
hbm2ddl.auto: none # 1
show_sql: true # 2
flyway.datasources.default: enabled # 3
- 禁用 Hibernate 的模式创建
- 记录 SQL 语句
- 启用 Flyway 迁移
H2 驱动程序依赖性保持不变。虽然 Spring Boot 使用默认参数创建连接,但 Micronaut 需要显式配置它:
datasources.default:
url: jdbc:h2:mem:test
driverClassName: org.h2.Driver
username: sa
dialect: H2
迁移执行器
Micronaut 还提供管理端点。它与 Spring Boot 的基本相同。
需要替换依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-management</artifactId>
</dependency>
与 Spring Boot 最大的区别是开发人员需要单独配置端点:
endpoints:
all.path: /actuator # 1
beans:
enabled: true
sensitive: false
health:
enabled: true
sensitive: false
flyway:
enabled: true
sensitive: false
这样 一个Springboot项目迁移到Micronaut项目就完成啦!