diff --git a/docs/graalvm-native-image.md b/docs/graalvm-native-image.md index 8f00860..256fa4f 100644 --- a/docs/graalvm-native-image.md +++ b/docs/graalvm-native-image.md @@ -67,7 +67,10 @@ src/main/resources/META-INF/native-image/ - `@MapperScan` 显式声明 `sqlSessionFactoryRef`,否则 AOT 丢 `AUTOWIRE_BY_TYPE` 装配; - `spring-cloud-starter-bootstrap` 与 AOT 不兼容,改用 `spring.config.import`; - MyBatis mapper 代理与实体由官方 `MyBatisNativeConfiguration` 模板的 AOT 处理器注册, - bean 名勿与 MP 自带的 `MyBatisPlusNativeImageConfiguration` 重复; + bean 名勿与 MP 自带的 `MyBatisPlusNativeImageConfiguration` 重复;Boot 4 模块用的 + mybatis-spring 4.1.0 已内置构造参数修正与 AOT 运行时跳过重复扫描(mybatis/spring#1238、 + #1239),模板因此只剩该处理器;`springboot3-sample` 解析到的 mybatis-spring 3.0.5 + 尚无这两处修复,保留完整模板; - `MySQLContainer` 的挂载问题见 testcontainers-java#7954(子类跳过挂载或用 `GenericContainer`); - ShardingSphere 用 `config-native.yaml`(算法与行表达式限制见官方文档),另需显式依赖 `javax.transaction-api`;其自带元数据在 JDBC 用法下多不生效,以本仓库条目为准; diff --git a/docs/upstream-todo.md b/docs/upstream-todo.md index e5f7d28..ee2ed04 100644 --- a/docs/upstream-todo.md +++ b/docs/upstream-todo.md @@ -9,6 +9,11 @@ - TODO https://github.com/spring-projects/spring-framework/issues/37500 :Spring AOT 保留 `autowireMode` 后,去掉各模块 `@MapperScan` 的 `sqlSessionFactoryRef` (逐模块去属性并跑 nativeTest 验证)。 +- TODO https://github.com/mybatis/spring/issues/1309 :mybatis-spring 内置 mapper 的 AOT + 处理器(为 mapper 接口注册反射、JDK 代理与同名 XML 等 hints)后,升级父 POM 的 + `mybatis-spring.version`,删除 Boot 4 各模块的 `MyBatisNativeConfiguration` 并逐模块跑 + nativeTest 验证;`springboot3-sample` 不必等此项:MP 3.5.17 起自带 + `mybatis-plus-spring-boot-native-image`,升级 MP 后即可删除其副本。 - TODO https://github.com/baomidou/dynamic-datasource/issues/809 :DD 补上 `@NestedConfigurationProperty` 或自带元数据后,删除各模块 `dynamic-datasource-creator` 目录下的 `DatasourceInitProperties` 条目。 diff --git a/spring-boot-4/druid-sample/src/main/java/com/baomidou/samples/druid/config/MyBatisNativeConfiguration.java b/spring-boot-4/druid-sample/src/main/java/com/baomidou/samples/druid/config/MyBatisNativeConfiguration.java index 0b5d43b..f26fe7a 100644 --- a/spring-boot-4/druid-sample/src/main/java/com/baomidou/samples/druid/config/MyBatisNativeConfiguration.java +++ b/spring-boot-4/druid-sample/src/main/java/com/baomidou/samples/druid/config/MyBatisNativeConfiguration.java @@ -19,81 +19,52 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -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.reflection.TypeParameterResolver; 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.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.config.ConstructorArgumentValues; -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.core.ResolvableType; -import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** * MyBatis 的 Spring AOT 集成。 * - *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、 缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 - * {@code META-INF/native-image////reachability-metadata.json}, - * 这里只保留无法用静态元数据表达的部分: + *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 + * {@code META-INF/native-image////reachability-metadata.json}。 * - *

+ *

模板中的 {@code MyBatisMapperFactoryBeanPostProcessor} 与对 {@code MapperScannerConfigurer} 的 AOT + * 排除已删除:mybatis-spring 4.1.0 起扫描器直接以 {@code Class} 登记 mapper 构造参数(mybatis/spring#1238),并在使用 AOT + * 产物运行时跳过重复扫描(mybatis/spring#1239)。该后处理器补齐的泛型 targetType 随之不再生成,native 运行时按类型匹配时会先实例化 {@code + * MapperFactoryBean}(只调用构造器)以取得 mapper 类型,nativeTest 实测无影响。 + * + *

这里只保留 mybatis-spring 尚未内置、又无法用静态元数据表达的部分:{@code MyBatisBeanFactoryInitializationAotProcessor} + * 按 AOT 扫描结果为 mapper 接口注册反射、JDK 代理与同名 XML 资源,并为其方法的参数、返回值及 SQL Provider 类型注册反射。 */ @Configuration(proxyBeanMethods = false) public class MyBatisNativeConfiguration { - @Bean - static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() { - return new MyBatisMapperFactoryBeanPostProcessor(); - } - @Bean MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() { return new MyBatisBeanFactoryInitializationAotProcessor(); } 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()); - } + implements BeanFactoryInitializationAotProcessor { @Override public BeanFactoryInitializationAotContribution processAheadOfTime( @@ -228,63 +199,4 @@ private static Class typeToClass(Type src, Class 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); - } - } - - /** - * 为 {@code MapperFactoryBean} 的 BeanDefinition 补齐类型信息。 - * - *

向上下文暴露泛型类型信息以避免过早初始化;同时把构造参数重新注册为 {@code Class} 字面量——扫描器只会记录 mapper 类名字符串,这会使 AOT - * 代码生成退回到 自动装配 {@code MapperFactoryBean} 构造器,native 测试下会尝试自动装配 {@code Class}。 - */ - 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) { - beanDefinition.setTargetType( - ResolvableType.forClassWithGenerics( - beanDefinition.getBeanClass(), mapperInterface)); - ConstructorArgumentValues constructorArgumentValues = - new ConstructorArgumentValues(); - constructorArgumentValues.addGenericArgumentValue(mapperInterface); - beanDefinition.setConstructorArgumentValues(constructorArgumentValues); - } - } - } - - 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; - } - } - } } diff --git a/spring-boot-4/mybatis-sample/src/main/java/com/baomidou/samples/mybatis/config/MyBatisNativeConfiguration.java b/spring-boot-4/mybatis-sample/src/main/java/com/baomidou/samples/mybatis/config/MyBatisNativeConfiguration.java index 97ea16e..e9cff99 100644 --- a/spring-boot-4/mybatis-sample/src/main/java/com/baomidou/samples/mybatis/config/MyBatisNativeConfiguration.java +++ b/spring-boot-4/mybatis-sample/src/main/java/com/baomidou/samples/mybatis/config/MyBatisNativeConfiguration.java @@ -19,81 +19,52 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -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.reflection.TypeParameterResolver; 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.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.config.ConstructorArgumentValues; -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.core.ResolvableType; -import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** * MyBatis 的 Spring AOT 集成。 * - *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、 缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 - * {@code META-INF/native-image////reachability-metadata.json}, - * 这里只保留无法用静态元数据表达的部分: + *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 + * {@code META-INF/native-image////reachability-metadata.json}。 * - *

+ *

模板中的 {@code MyBatisMapperFactoryBeanPostProcessor} 与对 {@code MapperScannerConfigurer} 的 AOT + * 排除已删除:mybatis-spring 4.1.0 起扫描器直接以 {@code Class} 登记 mapper 构造参数(mybatis/spring#1238),并在使用 AOT + * 产物运行时跳过重复扫描(mybatis/spring#1239)。该后处理器补齐的泛型 targetType 随之不再生成,native 运行时按类型匹配时会先实例化 {@code + * MapperFactoryBean}(只调用构造器)以取得 mapper 类型,nativeTest 实测无影响。 + * + *

这里只保留 mybatis-spring 尚未内置、又无法用静态元数据表达的部分:{@code MyBatisBeanFactoryInitializationAotProcessor} + * 按 AOT 扫描结果为 mapper 接口注册反射、JDK 代理与同名 XML 资源,并为其方法的参数、返回值及 SQL Provider 类型注册反射。 */ @Configuration(proxyBeanMethods = false) public class MyBatisNativeConfiguration { - @Bean - static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() { - return new MyBatisMapperFactoryBeanPostProcessor(); - } - @Bean MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() { return new MyBatisBeanFactoryInitializationAotProcessor(); } 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()); - } + implements BeanFactoryInitializationAotProcessor { @Override public BeanFactoryInitializationAotContribution processAheadOfTime( @@ -228,63 +199,4 @@ private static Class typeToClass(Type src, Class 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); - } - } - - /** - * 为 {@code MapperFactoryBean} 的 BeanDefinition 补齐类型信息。 - * - *

向上下文暴露泛型类型信息以避免过早初始化;同时把构造参数重新注册为 {@code Class} 字面量——扫描器只会记录 mapper 类名字符串,这会使 AOT - * 代码生成退回到 自动装配 {@code MapperFactoryBean} 构造器,native 测试下会尝试自动装配 {@code Class}。 - */ - 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) { - beanDefinition.setTargetType( - ResolvableType.forClassWithGenerics( - beanDefinition.getBeanClass(), mapperInterface)); - ConstructorArgumentValues constructorArgumentValues = - new ConstructorArgumentValues(); - constructorArgumentValues.addGenericArgumentValue(mapperInterface); - beanDefinition.setConstructorArgumentValues(constructorArgumentValues); - } - } - } - - 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; - } - } - } } diff --git a/spring-boot-4/name-pattern-sample/src/main/java/com/baomidou/samples/pattern/config/MyBatisNativeConfiguration.java b/spring-boot-4/name-pattern-sample/src/main/java/com/baomidou/samples/pattern/config/MyBatisNativeConfiguration.java index 8006a80..6efcbf4 100644 --- a/spring-boot-4/name-pattern-sample/src/main/java/com/baomidou/samples/pattern/config/MyBatisNativeConfiguration.java +++ b/spring-boot-4/name-pattern-sample/src/main/java/com/baomidou/samples/pattern/config/MyBatisNativeConfiguration.java @@ -19,81 +19,52 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -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.reflection.TypeParameterResolver; 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.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.config.ConstructorArgumentValues; -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.core.ResolvableType; -import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** * MyBatis 的 Spring AOT 集成。 * - *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、 缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 - * {@code META-INF/native-image////reachability-metadata.json}, - * 这里只保留无法用静态元数据表达的部分: + *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 + * {@code META-INF/native-image////reachability-metadata.json}。 * - *

+ *

模板中的 {@code MyBatisMapperFactoryBeanPostProcessor} 与对 {@code MapperScannerConfigurer} 的 AOT + * 排除已删除:mybatis-spring 4.1.0 起扫描器直接以 {@code Class} 登记 mapper 构造参数(mybatis/spring#1238),并在使用 AOT + * 产物运行时跳过重复扫描(mybatis/spring#1239)。该后处理器补齐的泛型 targetType 随之不再生成,native 运行时按类型匹配时会先实例化 {@code + * MapperFactoryBean}(只调用构造器)以取得 mapper 类型,nativeTest 实测无影响。 + * + *

这里只保留 mybatis-spring 尚未内置、又无法用静态元数据表达的部分:{@code MyBatisBeanFactoryInitializationAotProcessor} + * 按 AOT 扫描结果为 mapper 接口注册反射、JDK 代理与同名 XML 资源,并为其方法的参数、返回值及 SQL Provider 类型注册反射。 */ @Configuration(proxyBeanMethods = false) public class MyBatisNativeConfiguration { - @Bean - static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() { - return new MyBatisMapperFactoryBeanPostProcessor(); - } - @Bean MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() { return new MyBatisBeanFactoryInitializationAotProcessor(); } 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()); - } + implements BeanFactoryInitializationAotProcessor { @Override public BeanFactoryInitializationAotContribution processAheadOfTime( @@ -228,63 +199,4 @@ private static Class typeToClass(Type src, Class 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); - } - } - - /** - * 为 {@code MapperFactoryBean} 的 BeanDefinition 补齐类型信息。 - * - *

向上下文暴露泛型类型信息以避免过早初始化;同时把构造参数重新注册为 {@code Class} 字面量——扫描器只会记录 mapper 类名字符串,这会使 AOT - * 代码生成退回到 自动装配 {@code MapperFactoryBean} 构造器,native 测试下会尝试自动装配 {@code Class}。 - */ - 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) { - beanDefinition.setTargetType( - ResolvableType.forClassWithGenerics( - beanDefinition.getBeanClass(), mapperInterface)); - ConstructorArgumentValues constructorArgumentValues = - new ConstructorArgumentValues(); - constructorArgumentValues.addGenericArgumentValue(mapperInterface); - beanDefinition.setConstructorArgumentValues(constructorArgumentValues); - } - } - } - - 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; - } - } - } } diff --git a/spring-boot-4/shardingsphere-jdbc-5.x-core-sample/src/main/java/com/baomidou/samples/shardingsphere/jdbc/v5/core/config/MyBatisNativeConfiguration.java b/spring-boot-4/shardingsphere-jdbc-5.x-core-sample/src/main/java/com/baomidou/samples/shardingsphere/jdbc/v5/core/config/MyBatisNativeConfiguration.java index 3f031ea..61e93ba 100644 --- a/spring-boot-4/shardingsphere-jdbc-5.x-core-sample/src/main/java/com/baomidou/samples/shardingsphere/jdbc/v5/core/config/MyBatisNativeConfiguration.java +++ b/spring-boot-4/shardingsphere-jdbc-5.x-core-sample/src/main/java/com/baomidou/samples/shardingsphere/jdbc/v5/core/config/MyBatisNativeConfiguration.java @@ -19,81 +19,52 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -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.reflection.TypeParameterResolver; 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.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.config.ConstructorArgumentValues; -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.core.ResolvableType; -import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** * MyBatis 的 Spring AOT 集成。 * - *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、 缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 - * {@code META-INF/native-image////reachability-metadata.json}, - * 这里只保留无法用静态元数据表达的部分: + *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 + * {@code META-INF/native-image////reachability-metadata.json}。 * - *

+ *

模板中的 {@code MyBatisMapperFactoryBeanPostProcessor} 与对 {@code MapperScannerConfigurer} 的 AOT + * 排除已删除:mybatis-spring 4.1.0 起扫描器直接以 {@code Class} 登记 mapper 构造参数(mybatis/spring#1238),并在使用 AOT + * 产物运行时跳过重复扫描(mybatis/spring#1239)。该后处理器补齐的泛型 targetType 随之不再生成,native 运行时按类型匹配时会先实例化 {@code + * MapperFactoryBean}(只调用构造器)以取得 mapper 类型,nativeTest 实测无影响。 + * + *

这里只保留 mybatis-spring 尚未内置、又无法用静态元数据表达的部分:{@code MyBatisBeanFactoryInitializationAotProcessor} + * 按 AOT 扫描结果为 mapper 接口注册反射、JDK 代理与同名 XML 资源,并为其方法的参数、返回值及 SQL Provider 类型注册反射。 */ @Configuration(proxyBeanMethods = false) public class MyBatisNativeConfiguration { - @Bean - static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() { - return new MyBatisMapperFactoryBeanPostProcessor(); - } - @Bean MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() { return new MyBatisBeanFactoryInitializationAotProcessor(); } 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()); - } + implements BeanFactoryInitializationAotProcessor { @Override public BeanFactoryInitializationAotContribution processAheadOfTime( @@ -228,63 +199,4 @@ private static Class typeToClass(Type src, Class 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); - } - } - - /** - * 为 {@code MapperFactoryBean} 的 BeanDefinition 补齐类型信息。 - * - *

向上下文暴露泛型类型信息以避免过早初始化;同时把构造参数重新注册为 {@code Class} 字面量——扫描器只会记录 mapper 类名字符串,这会使 AOT - * 代码生成退回到 自动装配 {@code MapperFactoryBean} 构造器,native 测试下会尝试自动装配 {@code Class}。 - */ - 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) { - beanDefinition.setTargetType( - ResolvableType.forClassWithGenerics( - beanDefinition.getBeanClass(), mapperInterface)); - ConstructorArgumentValues constructorArgumentValues = - new ConstructorArgumentValues(); - constructorArgumentValues.addGenericArgumentValue(mapperInterface); - beanDefinition.setConstructorArgumentValues(constructorArgumentValues); - } - } - } - - 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; - } - } - } } diff --git a/spring-boot-4/spel-sample/src/main/java/com/baomidou/samples/spel/config/MyBatisNativeConfiguration.java b/spring-boot-4/spel-sample/src/main/java/com/baomidou/samples/spel/config/MyBatisNativeConfiguration.java index e7322a3..88cdb89 100644 --- a/spring-boot-4/spel-sample/src/main/java/com/baomidou/samples/spel/config/MyBatisNativeConfiguration.java +++ b/spring-boot-4/spel-sample/src/main/java/com/baomidou/samples/spel/config/MyBatisNativeConfiguration.java @@ -19,81 +19,52 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -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.reflection.TypeParameterResolver; 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.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.config.ConstructorArgumentValues; -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.core.ResolvableType; -import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** * MyBatis 的 Spring AOT 集成。 * - *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、 缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 - * {@code META-INF/native-image////reachability-metadata.json}, - * 这里只保留无法用静态元数据表达的部分: + *

取自 MyBatis 官方的 MyBatisNativeConfiguration 模板;静态的 GraalVM 元数据(日志实现、缓存装饰器、dtd/xsd 资源等)已按依赖分目录改为 + * {@code META-INF/native-image////reachability-metadata.json}。 * - *

+ *

模板中的 {@code MyBatisMapperFactoryBeanPostProcessor} 与对 {@code MapperScannerConfigurer} 的 AOT + * 排除已删除:mybatis-spring 4.1.0 起扫描器直接以 {@code Class} 登记 mapper 构造参数(mybatis/spring#1238),并在使用 AOT + * 产物运行时跳过重复扫描(mybatis/spring#1239)。该后处理器补齐的泛型 targetType 随之不再生成,native 运行时按类型匹配时会先实例化 {@code + * MapperFactoryBean}(只调用构造器)以取得 mapper 类型,nativeTest 实测无影响。 + * + *

这里只保留 mybatis-spring 尚未内置、又无法用静态元数据表达的部分:{@code MyBatisBeanFactoryInitializationAotProcessor} + * 按 AOT 扫描结果为 mapper 接口注册反射、JDK 代理与同名 XML 资源,并为其方法的参数、返回值及 SQL Provider 类型注册反射。 */ @Configuration(proxyBeanMethods = false) public class MyBatisNativeConfiguration { - @Bean - static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() { - return new MyBatisMapperFactoryBeanPostProcessor(); - } - @Bean MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() { return new MyBatisBeanFactoryInitializationAotProcessor(); } 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()); - } + implements BeanFactoryInitializationAotProcessor { @Override public BeanFactoryInitializationAotContribution processAheadOfTime( @@ -228,63 +199,4 @@ private static Class typeToClass(Type src, Class 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); - } - } - - /** - * 为 {@code MapperFactoryBean} 的 BeanDefinition 补齐类型信息。 - * - *

向上下文暴露泛型类型信息以避免过早初始化;同时把构造参数重新注册为 {@code Class} 字面量——扫描器只会记录 mapper 类名字符串,这会使 AOT - * 代码生成退回到 自动装配 {@code MapperFactoryBean} 构造器,native 测试下会尝试自动装配 {@code Class}。 - */ - 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) { - beanDefinition.setTargetType( - ResolvableType.forClassWithGenerics( - beanDefinition.getBeanClass(), mapperInterface)); - ConstructorArgumentValues constructorArgumentValues = - new ConstructorArgumentValues(); - constructorArgumentValues.addGenericArgumentValue(mapperInterface); - beanDefinition.setConstructorArgumentValues(constructorArgumentValues); - } - } - } - - 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; - } - } - } }