Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker
作者:mmseoamin日期:2024-01-19

Spring3新特性

graalvm打包Springboot+Mybatis

项目源代码

https://github.com/cmdch2017/SpringNative_Graalvm_Mybatis

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第1张

如何安装与运行

安装graalvm与配置环境

首先安装步骤参考这篇博客

https://blog.csdn.net/weixin_38943666/article/details/129505945

其次如何处理反射

https://blog.csdn.net/qq_32740973/article/details/131799510


第一步,直接拷贝我项目中的config文件夹到你的项目

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第2张

package com.example.communicationinterface30003.config;
import cn.hutool.core.util.ClassUtil;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
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;

/**
 * 反射将所有项目类扫描加入到服务, 大力出奇迹的操作,感觉不太合适,不过先让服务跑起来
 *
 * @author PC
 *
 */
@Component
public class ClassReflectConfig {
	static boolean begin = true;
	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 asList = Arrays.asList(proPackageName);
						for (String spn : asList) {
							try {
								Set> 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) {
			}
		});
	}
}
package org.example.config;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.cache.decorators.FifoCache;
import org.apache.ibatis.cache.decorators.LruCache;
import org.apache.ibatis.cache.decorators.SoftCache;
import org.apache.ibatis.cache.decorators.WeakCache;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.javassist.util.proxy.ProxyFactory;
import org.apache.ibatis.javassist.util.proxy.RuntimeSupport;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.reflection.TypeParameterResolver;
import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Configuration(proxyBeanMethods = false)
@ImportRuntimeHints(MyBatisNativeConfiguration.MyBaitsRuntimeHintsRegistrar.class)
public class MyBatisNativeConfiguration {
  @Bean
  MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() {
    return new MyBatisBeanFactoryInitializationAotProcessor();
  }
  @Bean
  static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() {
    return new MyBatisMapperFactoryBeanPostProcessor();
  }
  static class MyBaitsRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
      Stream.of(RawLanguageDriver.class,
              XMLLanguageDriver.class,
              RuntimeSupport.class,
              ProxyFactory.class,
              Slf4jImpl.class,
              Log.class,
              JakartaCommonsLoggingImpl.class,
              Log4j2Impl.class,
              Jdk14LoggingImpl.class,
              StdOutImpl.class,
              NoLoggingImpl.class,
              SqlSessionFactory.class,
              PerpetualCache.class,
              FifoCache.class,
              LruCache.class,
              SoftCache.class,
              WeakCache.class,
              SqlSessionFactoryBean.class,
              ArrayList.class,
              HashMap.class,
              TreeSet.class,
              HashSet.class
      ).forEach(x -> hints.reflection().registerType(x, MemberCategory.values()));
      Stream.of(
              "org/apache/ibatis/builder/xml/*.dtd",
              "org/apache/ibatis/builder/xml/*.xsd"
      ).forEach(hints.resources()::registerPattern);
    }
  }
  static class MyBatisBeanFactoryInitializationAotProcessor
          implements BeanFactoryInitializationAotProcessor, BeanRegistrationExcludeFilter {
    private final Set> excludeClasses = new HashSet<>();
    MyBatisBeanFactoryInitializationAotProcessor() {
      excludeClasses.add(MapperScannerConfigurer.class);
    }
    @Override public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) {
      return excludeClasses.contains(registeredBean.getBeanClass());
    }
    @Override
    public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
      String[] beanNames = beanFactory.getBeanNamesForType(MapperFactoryBean.class);
      if (beanNames.length == 0) {
        return null;
      }
      return (context, code) -> {
        RuntimeHints hints = context.getRuntimeHints();
        for (String beanName : beanNames) {
          BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName.substring(1));
          PropertyValue mapperInterface = beanDefinition.getPropertyValues().getPropertyValue("mapperInterface");
          if (mapperInterface != null && mapperInterface.getValue() != null) {
            Class mapperInterfaceType = (Class) mapperInterface.getValue();
            if (mapperInterfaceType != null) {
              registerReflectionTypeIfNecessary(mapperInterfaceType, hints);
              hints.proxies().registerJdkProxy(mapperInterfaceType);
              hints.resources()
                      .registerPattern(mapperInterfaceType.getName().replace('.', '/').concat(".xml"));
              registerMapperRelationships(mapperInterfaceType, hints);
            }
          }
        }
      };
    }
    private void registerMapperRelationships(Class mapperInterfaceType, RuntimeHints hints) {
      Method[] methods = ReflectionUtils.getAllDeclaredMethods(mapperInterfaceType);
      for (Method method : methods) {
        if (method.getDeclaringClass() != Object.class) {
          ReflectionUtils.makeAccessible(method);
          registerSqlProviderTypes(method, hints, SelectProvider.class, SelectProvider::value, SelectProvider::type);
          registerSqlProviderTypes(method, hints, InsertProvider.class, InsertProvider::value, InsertProvider::type);
          registerSqlProviderTypes(method, hints, UpdateProvider.class, UpdateProvider::value, UpdateProvider::type);
          registerSqlProviderTypes(method, hints, DeleteProvider.class, DeleteProvider::value, DeleteProvider::type);
          Class returnType = MyBatisMapperTypeUtils.resolveReturnClass(mapperInterfaceType, method);
          registerReflectionTypeIfNecessary(returnType, hints);
          MyBatisMapperTypeUtils.resolveParameterClasses(mapperInterfaceType, method)
                  .forEach(x -> registerReflectionTypeIfNecessary(x, hints));
        }
      }
    }
    @SafeVarargs
    private  void registerSqlProviderTypes(
            Method method, RuntimeHints hints, Class annotationType, Function>... providerTypeResolvers) {
      for (T annotation : method.getAnnotationsByType(annotationType)) {
        for (Function> providerTypeResolver : providerTypeResolvers) {
          registerReflectionTypeIfNecessary(providerTypeResolver.apply(annotation), hints);
        }
      }
    }
    private void registerReflectionTypeIfNecessary(Class type, RuntimeHints hints) {
      if (!type.isPrimitive() && !type.getName().startsWith("java")) {
        hints.reflection().registerType(type, MemberCategory.values());
      }
    }
  }
  static class MyBatisMapperTypeUtils {
    private MyBatisMapperTypeUtils() {
      // NOP
    }
    static Class resolveReturnClass(Class mapperInterface, Method method) {
      Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
      return typeToClass(resolvedReturnType, method.getReturnType());
    }
    static Set> resolveParameterClasses(Class mapperInterface, Method method) {
      return Stream.of(TypeParameterResolver.resolveParamTypes(method, mapperInterface))
              .map(x -> typeToClass(x, x instanceof Class ? (Class) x : Object.class)).collect(Collectors.toSet());
    }
    private static Class typeToClass(Type src, Class fallback) {
      Class result = null;
      if (src instanceof Class) {
        if (((Class) src).isArray()) {
          result = ((Class) src).getComponentType();
        } else {
          result = (Class) src;
        }
      } else if (src instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) src;
        int index = (parameterizedType.getRawType() instanceof Class
                && Map.class.isAssignableFrom((Class) parameterizedType.getRawType())
                && parameterizedType.getActualTypeArguments().length > 1) ? 1 : 0;
        Type actualType = parameterizedType.getActualTypeArguments()[index];
        result = typeToClass(actualType, fallback);
      }
      if (result == null) {
        result = fallback;
      }
      return result;
    }
  }
  static class MyBatisMapperFactoryBeanPostProcessor implements MergedBeanDefinitionPostProcessor, BeanFactoryAware {
    private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(
            MyBatisMapperFactoryBeanPostProcessor.class);
    private static final String MAPPER_FACTORY_BEAN = "org.mybatis.spring.mapper.MapperFactoryBean";
    private ConfigurableBeanFactory beanFactory;
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
      this.beanFactory = (ConfigurableBeanFactory) beanFactory;
    }
    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
      if (ClassUtils.isPresent(MAPPER_FACTORY_BEAN, this.beanFactory.getBeanClassLoader())) {
        resolveMapperFactoryBeanTypeIfNecessary(beanDefinition);
      }
    }
    private void resolveMapperFactoryBeanTypeIfNecessary(RootBeanDefinition beanDefinition) {
      if (!beanDefinition.hasBeanClass() || !MapperFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())) {
        return;
      }
      if (beanDefinition.getResolvableType().hasUnresolvableGenerics()) {
        Class mapperInterface = getMapperInterface(beanDefinition);
        if (mapperInterface != null) {
          // Exposes a generic type information to context for prevent early initializing
          beanDefinition
                  .setTargetType(ResolvableType.forClassWithGenerics(beanDefinition.getBeanClass(), mapperInterface));
        }
      }
    }
    private Class getMapperInterface(RootBeanDefinition beanDefinition) {
      try {
        return (Class) beanDefinition.getPropertyValues().get("mapperInterface");
      }
      catch (Exception e) {
        LOG.debug("Fail getting mapper interface type.", e);
        return null;
      }
    }
  }
}

第二步,pom.xml加入依赖


		
			
				org.graalvm.buildtools
				native-maven-plugin
			
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	

第三步执行下面的语句

java -agentlib:native-image-agent=config-output-dir=D:\eclipse_file\new_ws\springboot3-demo3\src\main\resources\META-INF\native-image  -jar  .\springboot3-demo3-0.0.1-SNAPSHOT.jar

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第3张

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第4张

1、如上图所示,点击mvn clean
2、如上图所示,点击mvn install
3、执行反射编译语句
java -agentlib:native-image-agent=config-output-dir=C:\Demos\CommunicationInterface30003\src\main\resources\META-INF\native-image  -jar .\target\comu300003-1.0.0-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 componentSpring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第5张

用不了反射,所以需要这个文件去

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 asList = Arrays.asList(proPackageName);
						for (String spn : asList) {
							try {
								Set> 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.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");

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第6张

问题3 exe文件闪退日志

用powerShell打开

问题4 报错信息有提示http

加上支持http协议的参数

  
    
      
        org.graalvm.buildtools
        native-maven-plugin
        
          
            --enable-url-protocols=http
          
        
      
      
        org.springframework.boot
        spring-boot-maven-plugin
      
    
  
  
    
      sonatype-oss-snapshots
      Sonatype OSS Snapshots Repository
      https://oss.sonatype.org/content/repositories/snapshots
    
  

问题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.(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]

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第7张

或者是下面的Controller层报错

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第8张

原因是你没按照网上的教程来,在加了问题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

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第9张

问题6 Mapper层问题

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第10张

检查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")

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第11张

打包成Docker核心总结

https://zhuanlan.zhihu.com/p/602486720?utm_id=0

下面将介绍把spring boot项目打包成docker镜像,大部分操作与上诉内容一致,但需要在自己本地电脑安装docker,这里需要修改一下pom文件中spring-boot-maven-plugin插件配置,如下所示


	org.springframework.boot
	spring-boot-maven-plugin
	
		
			paketobuildpacks/builder:tiny
			
				true
			
		
		
			
				org.projectlombok
				lombok
			
		
	

搭梯子运行下面的语句

1、在终端输入指令:mvn -Pnative spring-boot:build-image

2、打开docker desktop

3、在终端输入指令:端口号注意是你自己项目的端口号

docker run -itd -p 8001:8001 --name graalvm graalvm:0.0.1-SNAPSHOT

Spring3新特性:Graalvm打包Springboot+Mybatis;Graalvm打包成Docker,在这里插入图片描述,第12张