Spring6.0新特性:
https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framework-6.x
SpringBoot3.0:
https://docs.spring.io/spring-boot/docs/current/reference/html/
下载地址:
https://github.com/graalvm/graalvm-ce-builds/releases
按照jdk版本下载GraalVM。SpringBoot3.0必须要使用jdk17以上了。
安装过程与普通的jdk安装一样。
安装成功之后,使用
java -version
,可以看到VM是GraalVM了。
GraalVM在编译成二进制可执行文件时,需要确定该应用到底用到了哪些类、哪些方法、哪些属性,从而把这些代码编译为机器指令(也就是exe文件)。
但是我们一个应用中某些类可能是动态生成的,也就是应用运行后才生成的,为了解决这个问题,GraalVM提供了配置的方式,比如我们可以在编译时告诉GraalVM哪些方法会被反射调用,比如我们可以通过
reflect-config.json
来进行配置。
略,注意配置阿里云加速。
为了应对Serverless大环境,使Springboot项目快速启动,所以才会推出AOT与直接编译为字节码的功能。
因为Java程序运行,需要启动虚拟机,然后由虚拟机将class字节码文件编译为机器指令,所以启动过程比较慢。
而如果像C语言那样,直接编译为机器指令,会大大提高启动速度,但是会丢失Java反射、动态代理等功能(有解决方案-RuntimeHints)。
而且Springboot3.0-AOT更是将Bean扫描阶段提前到了编译器,而不是启动期间进行扫描,大大提高了启动速度。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
-
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
-
视频教程:https://doc.iocoder.cn/video/
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>3.2.5version>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtoolsgroupId>
<artifactId>native-maven-pluginartifactId>
plugin>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Controller {
@GetMapping("/demo1")
public String demo1() {
return "hello world";
}
}
> 基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
>
> * 项目地址:
> * 视频教程:
# 注意需要使用GraalVM环境,需要有C语言环境 ,最好使用linux系统
mvn -Pnative native:compile
打包过程会久一些。
会将可执行文件、jar包都打出来:
运行demo:仅仅几十毫秒就能启动!!!
使用jar包运行:很明显会慢很多!!
# 打包成docker
mvn -Pnative spring-boot:build-image
docker run --rm -p 8080:8080 demo
# 如果要传参数,可以通过-e
docker run --rm -p 8080:8080 -e methodName=test demo
# 不过代码中,得通过以下代码获取:
String methodName = System.getenv("methodName")
#也可以使用Environment获取,注入Environment
environment.getProperty("methodName");
注意,打包的过程会下载java环境镜像,比较慢。
仅仅几十毫秒就可以启动一个简单的Springboot项目!
假设以下代码:
@Component
public class UserService {
public String test(){
String result = "";
try {
Method test = MyService.class.getMethod("test", null);
result = (String) test.invoke(MyService.class.newInstance(), null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
return result;
}
}
在MyService中,通过反射的方式使用到了MyService的无参构造方法(
MyService.class.newInstance()
),如果我们不做任何处理,那么打成二进制可执行文件后是运行不了的,可执行文件中是没有MyService的无参构造方法的,会报方法找不到的错误。
我们可以使用Spring提供的
Runtime Hints
机制来间接的配置
reflect-config.json
。
提供一个
RuntimeHintsRegistrar
接口的实现类,并导入到Spring容器中就可以了:
@Component
@ImportRuntimeHints(UserService.MyServiceRuntimeHints.class)
public class UserService {
public String test(){
String result = "";
try {
Method test = MyService.class.getMethod("test", null);
result = (String) test.invoke(MyService.class.newInstance(), null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
return result;
}
static class MyServiceRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
try {
hints.reflection().registerConstructor(MyService.class.getConstructor(), ExecutableMode.INVOKE);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
}
// 该类的所有方法都会编译为机器码
@RegisterReflectionForBinding(MyService.class)
public String test(){
String result = "";
try {
Method test = MyService.class.getMethod("test", null);
result = (String) test.invoke(MyService.class.newInstance(), null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
return result;
}
注意:如果代码中的
methodName
是通过参数获取的,那么GraalVM在编译时就不能知道到底会使用到哪个方法,那么test方法也要利用
RuntimeHints
来进行配置。
@Component
@ImportRuntimeHints(MyService.MyServiceRuntimeHints.class)
public class UserService {
public String test(){
String methodName = System.getProperty("methodName");
String result = "";
try {
Method test = MyService.class.getMethod(methodName, null);
result = (String) test.invoke(MyService.class.newInstance(), null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
return result;
}
static class MyServiceRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
try {
hints.reflection().registerConstructor(MyService.class.getConstructor(), ExecutableMode.INVOKE);
hints.reflection().registerMethod(MyService.class.getMethod("test"), ExecutableMode.INVOKE);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
}
public String test() throws ClassNotFoundException {
String className = System.getProperty("className");
Class> aClass = Class.forName(className);
Object o = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{aClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.getName();
}
});
return o.toString();
}
也可以利用
RuntimeHints
来进行配置要代理的接口:
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.proxies().registerJdkProxy(UserInterface.class);
}
对于反射用到的地方,我们可以直接加一个
@Reflective
,前提是MyService得是一个Bean:
@Component
public class MyService{
@Reflective
public MyService() {
}
@Reflective
public String test(){
return "hello";
}
}
以上Spring6提供的
RuntimeHints
机制,我们可以使用该机制更方便的告诉GraalVM我们额外用到了哪些类、接口、方法等信息,最终Spring会生成对应的
reflect-config.json
、
proxy-config.json
中的内容,GraalVM就知道了。