当前位置: 首页 > news >正文

Mybatis执行sql流程(二)之加载Mapper

Mybatis加载Mapper

注册方式注册时机特点
@MapperScanBean定义阶段注册接口定义批量注册,推荐方式
@Mapper (接口注解)@MapperScan需每个接口单独标注
XML 配置 <mapper>MyBatis 初始化时传统方式,不依赖 Spring 容器
SqlSessionTemplate 直接获取调用时编程式获取,不自动注册为 Bean

@MapperScan注册mapper

// 使用:在启动类上添加注解
@MapperScan("com.zy.**.mapper")
@Import(MapperScannerRegistrar.class) // 此处使用了@Import注解,用于导入其他配置
@Repeatable(MapperScans.class)
public @interface MapperScan {...;
}
/**
1.指示要导入的一个或多个组件,通常是@Configuration类。
2.提供与Spring XML中的<import/>元素等效的功能。
3.允许导入@Configuration类,ImportSelector和ImportBeanDefinitionRegistrar实现,以及常规组件类(从4.2开始,类似于AnnotationConfigApplicationContext.register)。
4.导入的@Configuration类中声明的@Bean定义应该使用@Autowired注入来访问。也可以自动连接声明bean的配置类实例。
5.如果需要导入XML或其他非@Configuration bean定义资源,请改用@ImportResource注解。
*/public @interface Import {/*** {@link Configuration @Configuration}, {@link ImportSelector},* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.*/Class<?>[] value();}
// 实现了ImportBeanDefinitionRegistrar接口,此处涉及到Spring加载BeanDefinition的逻辑
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {private ResourceLoader resourceLoader;// 重写ImportBeanDefinitionRegistrar 接口中的registerBeanDefinitions 方法@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 1. 获取@MapperScan注解属性AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));// 2. 实际注册逻辑if (mapperScanAttrs != null) {registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,generateBaseBeanName(importingClassMetadata, 0));}}/**annoMeta 被注解类的元数据annoAttrs @MapperScan 注解的属性值registry : Spring bean 定义注册器beanName :要注册的 Bean 名称*/void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,BeanDefinitionRegistry registry, String beanName) {
// 创建MapperScannerConfigurer 的通用Bean定义构建器。此处还只是构造builder。BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);// 启用属性占位符处理(如 ${jdbc.url})builder.addPropertyValue("processPropertyPlaceHolders", true);// 注解类过滤:如果设置了annotationClass(如@Mapper),则只扫描带该注解的接口Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");if (!Annotation.class.equals(annotationClass)) {builder.addPropertyValue("annotationClass", annotationClass);}
// 标记接口过滤:如果设置了 markerInterface, 则只扫描实现该接口的MapperClass<?> markerInterface = annoAttrs.getClass("markerInterface");if (!Class.class.equals(markerInterface)) {builder.addPropertyValue("markerInterface", markerInterface);}
// Bean 名称生成器Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");if (!BeanNameGenerator.class.equals(generatorClass)) {builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));}
// 工厂类覆盖Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);}
// SQL 会话模板引用:支持指定特定的 SqlSessionTemplate BeanString sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");if (StringUtils.hasText(sqlSessionTemplateRef)) {builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));}
// SQL 会话工厂引用String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");if (StringUtils.hasText(sqlSessionFactoryRef)) {builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));}List<String> basePackages = new ArrayList<>();// 添加 value 属性值(包路径)basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
// 添加 basePackages 属性值basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
// 添加basePackageClasses的包路径basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
// 使用默认配置类所在包if (basePackages.isEmpty()) {basePackages.add(getDefaultBasePackage(annoMeta));}
// 延迟初始化String lazyInitialization = annoAttrs.getString("lazyInitialization");if (StringUtils.hasText(lazyInitialization)) {builder.addPropertyValue("lazyInitialization", lazyInitialization);}
// Bean 作用域String defaultScope = annoAttrs.getString("defaultScope");if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {builder.addPropertyValue("defaultScope", defaultScope);}
// 设置扫描包路径(逗号分割)builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));// for spring-nativebuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册Bean 定义。上述都是在配置builder, 此处才开始注册bean definitionregistry.registerBeanDefinition(beanName, builder.getBeanDefinition());}
}

MapperScannerConfigurer 作为BeanDefinitionRegistryPostProcessor执行, 中重写了 postProcessBeanDefinitionRegistry 方法,最终调用doScan(basePackages);扫描Mapper

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {@Overridepublic Set<BeanDefinitionHolder> doScan(String... basePackages) {// 1. 调用父类扫描方法获取Bean定义Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {logger.warn("No MyBatis mapper was found...");} else {// 2. 处理扫描到的Bean定义processBeanDefinitions(beanDefinitions);}return beanDefinitions;}
// 省略部分代码private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {for (BeanDefinitionHolder holder : beanDefinitions) {GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();// 3. 获取Mapper接口名String mapperClassName = definition.getBeanClassName();// 4. BeanClass 转换为 MapperFactoryBean// 关键步骤1:添加构造参数(原始Mapper接口)definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClassName);try {// 关键步骤2:设置 MapperInterface 属性(Spring Native 兼容)definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));} catch (ClassNotFoundException ignore) {// ignore}// 关键步骤3:替换 Bean 类为 MapperFactoryBeandefinition.setBeanClass(this.mapperFactoryBeanClass);// 省略部分SqlSessionFactory 、sqlSessionTemplate相关内容。优先级:SqlSessionTemplate > SqlSessionFactory// 5. 设置自动装配模式:当未显示配置工厂/模板时,启用按类型自动装配if (!explicitFactoryUsed) {LOGGER.debug("Enabling autowire by type...");definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}}}
}

转换前Bean定义:

BeanDefinition {beanClass = com.example.UserMapper // 原始接口scope = singleton...
}

转换后 Bean 定义

BeanDefinition {beanClass = MapperFactoryBean // 替换为工厂类constructorArgs = [com.example.UserMapper] // 原始接口作为构造参数properties = {sqlSessionFactory = ref("sqlSessionFactory") // 自动装配addToConfig = true}...
}

实际效果:

// 用户代码
@Autowired
UserMapper userMapper; // 实际发生
MapperFactoryBean factoryBean = new MapperFactoryBean(UserMapper.class);
factoryBean.setSqlSessionFactory(sqlSessionFactory);
UserMapper proxy = factoryBean.getObject(); // 返回 MyBatis 代理
// MapperFactoryBean 的 getObject() 
@Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface); // 此处就是获取的代理类对象 }
// 从 @Autowired 到factoryBean.getObject(); 的过程:
@Autowired-> AutowiredAnnotationBeanPostProcessor.postProcessProperties()-> InjectionMetadata.inject()-> DefaultListableBeanFactory.doGetBean()-> AbstractBeanFactory.getObjectForBeanInstance(),发现 MapperFacoryBeanUserMapper 类型的工厂-> AbstractBeanFactory..getObjectFromFactoryBean()-> FactoryBeanRegistrySupport.doGetObjectFromFactoryBean()-> FactoryBean<?>.getObject()
@AutowiredSpring容器DefaultListableBeanFactoryFactoryBeanRegistrySupportMapperFactoryBeanMyBatis代理需要注入UserMappergetBean("userMapper")doGetBean("userMapper")getObjectForBeanInstance()doGetObjectFromFactoryBean()getObject()sqlSession.getMapper()MapperProxy返回代理返回代理返回代理注入代理对象@AutowiredSpring容器DefaultListableBeanFactoryFactoryBeanRegistrySupportMapperFactoryBeanMyBatis代理
http://www.dtcms.com/a/337639.html

相关文章:

  • SQL详细语法教程(六)存储+索引
  • 高效长尾关键词SEO优化
  • 汽车企业顾客满意度调查:全周期反馈解码方案(市场调研实践)
  • Redis 哨兵模式与主从架构对比
  • 江苏单电感M401A-晶晨S905L3A_频率1.5ghz赫兹_2+16G_安卓9_线刷固件包
  • iOS 应用上架全流程实践,从开发内测到正式发布的多工具组合方案
  • 决策树-信息增益(第二十三节课内容总结)
  • 上网行为安全管理与组网方案
  • 第四十天(Vue)
  • 【VUE】解决Vue路由重复导航报错
  • 嵌入式软件开发笔试题练习
  • 微算法科技(NASDAQ: MLGO)研究分片技术:重塑区块链可扩展性新范式
  • 智能代码助手革新开发效率
  • 【VUE】用EmailJS自动发送邮件到网易邮箱
  • JVM学习笔记-----类加载
  • 【深度学习4】神经网络-激活函数
  • 最短路spfa和多层图(P1073 [NOIP 2009 提高组] 最优贸易)题解
  • Linux系统等保三级安全加固执行手册(ReahtCentosKylin)
  • mq存量消息如何处理
  • STM32G4 Park及反Park变换(一)matlab建模
  • Spark 运行流程核心组件(三)任务执行
  • C语言基础:变量与进制详解
  • 直播美颜SDK架构揭秘:动态贴纸功能的实现原理与性能优化
  • 计算机网络技术-交换机配置(Day.2)
  • 戴尔易安信 PowerEdge R540服务器系统安装教程
  • 深度学习篇---卷积
  • 远程访问公司内网电脑怎么操作?3个简单通用的跨网异地连接管理计算机方法
  • IoT/透过oc_lwm2m和at源码,分析NB-IoT通信模组和主板MCU之间的通信过程
  • 自建K8s集群无缝集成阿里云RAM完整指南
  • 重温 K8s 基础概念知识系列五(存储、配置、安全和策略)