Spring3新特性
graalvm打包Springboot+Mybatis
项目源代码
https://github.com/cmdch2017/SpringNative_Graalvm_Mybatis
如何安装与运行
安装graalvm与配置环境
首先安装步骤参考这篇博客
https://blog.csdn.net/weixin_38943666/article/details/129505945
其次如何处理反射
https://blog.csdn.net/qq_32740973/article/details/131799510
运行核心步骤
1、如上图所示,点击mvn clean
2、如上图所示,点击mvn compile
3、执行反射编译语句
java -agentlib:native-image-agent=config-output-dir=C:\Demos\graalvm\src\main\resources\META-INF\native-image -jar .\target\graalvm-0.0.1-SNAPSHOT.jar
4、打开 x64 Native Toogls Command Prompt for VS 并 cd到项目文件夹
mvn -Pnative -DskipTests clean native:compile
5、PowerShell中进入项目target目录下
.\graalvm.exe
遇到的零散问题
问题1
org.apache.catalina.LifecycleException: An invalid Lifecycle transition was attempted ([before_stop]) for component
用不了反射,所以需要这个文件去
package org.wxy.example.sqlite.config;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import cn.hutool.core.util.ClassUtil;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
/**
* 反射将所有项目类扫描加入到服务, 大力出奇迹的操作,感觉不太合适,不过先让服务跑起来
*
* @author PC
*
*/
@Component
public class ClassReflectConfig {
static boolean begin = true;
// @Value("${scanclass}")
private Boolean scanclass=true;
@Autowired
private ThreadPoolTaskExecutor executorService;
@PostConstruct
public void init() {
if (scanclass) {
System.err.println("配置文件下 scanclass 开启了生成反射类");
} else {
System.err.println("配置文件下 scanclass 关闭了生成反射类");
}
synchronized (ClassReflectConfig.class) {
if (begin && scanclass) {
begin = false;
executorService.submit(() -> {
{
// 扫描系统第二级开始的包
String packageName = ClassReflectConfig.class.getPackageName();
String proPackageName = packageName.substring(0,
packageName.indexOf(".", packageName.indexOf(".") + 1));
// 可以在这个地方,添加除了服务以外其他的包,将会加入反射,以供graalvm生成配置
List<String> asList = Arrays.asList(proPackageName);
for (String spn : asList) {
try {
Set<Class<?>> doScan = ClassUtil.scanPackage(spn);
for (Class clazz : doScan) {
handlerClass(clazz);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
});
}
}
}
private void handlerClass(Class clazz) {
if (clazz.equals(ClassReflectConfig.class)) {
// 跳过自己,避免形成循环
return;
}
executorService.submit(() -> {
try {
System.err.println("反射注入:" + clazz.getName());
// 生成所有的构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
// 找到无参构造器然后实例化
Constructor declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object newInstance = declaredConstructor.newInstance();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
try {
// 实例化成功,那么调用一下
method.setAccessible(true);
// graalvm必须需要声明方法
method.invoke(newInstance);
} catch (Throwable e) {
}
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
field.getType();
String name = field.getName();
field.get(newInstance);
} catch (Throwable e) {
}
}
System.err.println("反射注入完成:" + clazz.getName());
} catch (Throwable e) {
}
});
}
}
之后报错信息
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sqliteController': Unsatisfied dependency expressed through field 'personService': Error creating bean with name 'personService': Unsatisfied dependency expressed through field 'dao': Error creating bean with name 'personMapper': Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolveValue(AutowiredFieldValueResolver.java:195) ~[na:na]
at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolveObject(AutowiredFieldValueResolver.java:154) ~[na:na]
at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolve(AutowiredFieldValueResolver.java:143) ~[na:na]
at org.wxy.example.sqlite.controllers.SqliteController__Autowiring.apply(SqliteController__Autowiring.java:14) ~[na:na]
at org.springframework.beans.factory.support.InstanceSupplier$1.get(InstanceSupplier.java:83) ~[na:na]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[mysqlandmybatis.exe:6.0.8]
问题2 报错信息提示无法读取外部 DTD ‘mybatis-3-mapper.dtd’
无法读取外部 DTD ‘mybatis-3-mapper.dtd’, 因为 accessExternalDTD 属性设置的限制导致不允许 ‘http’ 访问。
允许对 ‘http’ 的访问:
如果你确定从 ‘http’ 地址下载 DTD 文件是安全的,可以配置解析器以允许对 ‘http’ 的访问。在SpringbootApplication 中,可以使用以下代码:
System.setProperty("javax.xml.accessExternalDTD", "all");
问题3 exe文件闪退日志
用powerShell打开
问题4 报错信息有提示http
加上支持http协议的参数
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<buildArgs combine.children="append">
<buildArg>--enable-url-protocols=http</buildArg>
</buildArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>sonatype-oss-snapshots</id>
<name>Sonatype OSS Snapshots Repository</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
问题5 RestController层的问题
org.apache.catalina.LifecycleException: An invalid Lifecycle transition was attempted ([before_stop]) for component [StandardEngine[Tomcat]] in state [INITIALIZED]
at org.apache.catalina.util.LifecycleBase.invalidTransition(LifecycleBase.java:430) ~[mysqlandmybatis.exe:10.1.8]
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:244) ~[mysqlandmybatis.exe:10.1.8]
at org.apache.catalina.core.StandardService.stopInternal(StandardService.java:491) ~[na:na]
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:257) ~[mysqlandmybatis.exe:10.1.8]
at org.apache.catalina.core.StandardServer.stopInternal(StandardServer.java:966) ~[na:na]
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:257) ~[mysqlandmybatis.exe:10.1.8]
at org.apache.catalina.util.LifecycleBase.destroy(LifecycleBase.java:293) ~[mysqlandmybatis.exe:10.1.8]
at org.apache.catalina.startup.Tomcat.destroy(Tomcat.java:507) ~[mysqlandmybatis.exe:10.1.8]
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.destroySilently(TomcatWebServer.java:262) ~[mysqlandmybatis.exe:3.0.6]
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:141) ~[mysqlandmybatis.exe:3.0.6]
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104) ~[mysqlandmybatis.exe:3.0.6]
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:488) ~[mysqlandmybatis.exe:3.0.6]
或者是下面的Controller层报错
原因是你没按照网上的教程来,在加了问题1的ClassReflectConfig 内容后,mvn -Pnative -DskipTests clean native:compile之后,你得执行以下语句,注意这里C:\Demos\springboot3-demo3-master\src\main\resources\META-INF\native-image还有.\target\springboot3-demo3-0.0.1-SNAPSHOT.jar的路径看你项目中的路径
java -agentlib:native-image-agent=config-output-dir=C:\Demos\springboot3-demo3-master\src\main\resources\META-INF\native-image -jar .\target\springboot3-demo3-0.0.1-SNAPSHOT.jar
问题6 Mapper层问题
检查yml文件中是否配置好了
mybatis:
mapper-locations: classpath:mapper/*.xml
typeAliasesPackage: org.wxy.example.*.model
问题7 只支持mybaits,不支持mybatis-plus
mybatis-plus暂不支持,官方也回应暂时没有计划支持graalvm
问题8 报错信息中有sqlSessionFactory
@MapperScan(basePackages ="org.wxy.example.*.mapper",sqlSessionFactoryRef = "sqlSessionFactory")