小鸭子的学习笔记duck

Duck Blog

唐如飞

( ^∀^)/欢迎\( ^∀^)

79 文章数
14 评论数

Java云原生应用配置类(Mybaties兼及mybaties-plus兼容)

tangrufei
2024-01-16 / 1 评论 / 318 阅读 / 0 点赞

重点!!! 打成exe本地化,自动注入不能使用@Resource (会导致空指针!!!)

特殊配置

reflect-config.json (反射配置文件)

 {
    "name": "com.baomidou.mybatisplus.extension.plugins.pagination.Page",
    "allPublicConstructors": true,
    "allDeclaredConstructors": true
  }


proxy-config.json(代理配置)

 {
    "interfaces":["jakarta.xml.bind.annotation.XmlAccessorType", "org.glassfish.jaxb.core.v2.model.annotation.Locatable"]
  },
  {
    "interfaces":[ "com.alibaba.fastjson2.annotation.JSONField", "org.glassfish.jaxb.core.v2.model.annotation.Locatable" ]
  },

pom.xml兼容配置


      <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.5</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>5.7.20</version>
        </dependency>
		打包依赖
		  
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
            </plugin>

启动类配置

System.setProperty("javax.xml.accessExternalDTD", "all");
SpringApplication.run(ParseJsonToXmlApplication.class, args);

mybaties兼容配置


package com.example.mybatisnativesample;

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.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
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<Class<?>> 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 <T extends Annotation> void registerSqlProviderTypes(
        Method method, RuntimeHints hints, Class<T> annotationType, Function<T, Class<?>>... providerTypeResolvers) {
      for (T annotation : method.getAnnotationsByType(annotationType)) {
        for (Function<T, Class<?>> 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<Class<?>> 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;
      }
    }

  }
}

mybaties-plus兼容配置

package com.hyway.corsConfig;

import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.interfaces.Compare;
import com.baomidou.mybatisplus.core.conditions.interfaces.Func;
import com.baomidou.mybatisplus.core.conditions.interfaces.Join;
import com.baomidou.mybatisplus.core.conditions.interfaces.Nested;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.session.SqlSession;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;

import java.util.Collections;

@Configuration(proxyBeanMethods = false)
@ImportRuntimeHints(MybatisPlusRuntimeHintsRegistrar.class)
public class MybatisPlusRuntimeHintsRegistrar implements RuntimeHintsRegistrar {


    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        hints.proxies()
                .registerJdkProxy(Func.class)
                .registerJdkProxy(Join.class)
                .registerJdkProxy(Query.class)
                .registerJdkProxy(IPage.class)
                .registerJdkProxy(Nested.class)
                .registerJdkProxy(Compare.class)
                .registerJdkProxy(Executor.class)
                .registerJdkProxy(IService.class)
                .registerJdkProxy(SqlSession.class)
                .registerJdkProxy(StatementHandler.class)
        ;
        hints.reflection()
                .registerType(Wrapper.class, builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
                .registerType(Wrappers.class, builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
                .registerType(MybatisXMLLanguageDriver.class, builder -> builder.withMethod("<init>", Collections.emptyList(), ExecutableMode.INVOKE))
                .registerType(QueryWrapper.class, builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
                .registerType(AbstractWrapper.class, builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
                .registerType(AbstractChainWrapper.class, builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
                .registerType(Page.class, builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS))
                .registerType(BoundSql.class, builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
        ;
    }

}

反射配置类

package com.hyway.corsConfig;

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<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) {
			}
		});
	}

}

使用 Native Image Agent 收集配置

java -agentlib:native-image-agent=config-output-dir=<配置输出目录> -jar <您的应用程序.jar>
示例
java -agentlib:native-image-agent=config-output-dir=E:\code\ParseJsonToXml\src\main\resources\META-INF\native-image  -jar  .\target\ParseJsonToXml-0.0.1-SNAPSHOT.jar

#打包成本地程序

mvn -Pnative -DskipTests clean native:compile

打包成docker镜像

搭梯子运行下面的语句
1、在终端输入指令:mvn -Pnative spring-boot:build-image
2、打开docker desktop
3、在终端输入指令:端口号注意是你自己项目的端口号
docker run -itd -p 8001:8001 --name graalvm graalvm:0.0.1-SNAPSHOT

文章不错,扫码支持一下吧~
上一篇 下一篇
评论
来首音乐
光阴似箭
今日已经过去小时
这周已经过去
本月已经过去
今年已经过去个月