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

网站建网站网上服务平台官网

网站建网站,网上服务平台官网,wordpress 电商,自适应平台网站最开始学ssm一套的时候,其实有几个问题: 1.为啥mybatis可以写个Mapper就能注入接口了,我们实际调用的不是对象吗? 2.为啥mybatis有些甚至Mapper都不用写,通过MapperScan就行? 下面按 问题 1 & 2 拆成两…

最开始学ssm一套的时候,其实有几个问题:
1.为啥mybatis可以写个@Mapper就能注入接口了,我们实际调用的不是对象吗?
2.为啥mybatis有些甚至@Mapper都不用写,通过@MapperScan就行?
下面按 问题 1 & 2 拆成两条时间线,用 Spring 源码级链路“@Mapper 怎么注册成 Bean”“@MapperScan 怎么显式扫描” 说透。


源码流程分析:

注册钩子路径

第一步@EnableAutoConfiguration

@EnableAutoConfiguration 内部是:@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector 是一个 DeferredImportSelector,在 Spring Boot 启动早期 被调用,它负责把 spring.factories 里的所有 EnableAutoConfiguration 键值读进来。

第二步:读取 mybatis实现的spring.factories。(jar 内自带,非用户手写)
mybatis-spring-boot-autoconfigure-2.2.x.jar
└─ META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

因此 MybatisAutoConfiguration BeanDefinition 自动被加载,无需用户写 @Import。

第三步:MybatisAutoConfiguration 注册扫描器
@Configuration(proxyBeanMethods = false) // 👈 这个空类被扫描原因,在 Spring 的 ConfigurationClassPostProcessor 阶段,
任何带有 @Configuration 的类都会进入 ConfigurationClassParser@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
@Import(AutoConfiguredMapperScannerRegistrar.class)   // 👈 关键钩子,实现了ImportBeanDefinitionRegistrar的registerBeanDefinitions
public static class MapperScannerRegistrarNotFoundConfiguration {}

@ConditionalOnMissingBean 只在容器里找不到 MapperFactoryBean/MapperScannerConfigurer 时生效。
@Import(AutoConfiguredMapperScannerRegistrar.class) 把 AutoConfiguredMapperScannerRegistrar 注册进来。
其中AutoConfiguredMapperScannerRegistrar s实现了ImportBeanDefinitionRegistrar的registerBeanDefinitions,扫描时候会判断是否实现该接口,然后将其添加进回调里面。

spring run调用链路:

SpringApplication.run└ AbstractApplicationContext.refresh()ConfigurationClassPostProcessor.processConfigBeanDefinitions()ConfigurationClassParser.parse(...)ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(...)loadBeanDefinitionsFromRegistrars(...)└ registrar.registerBeanDefinitions(...)
//ConfigurationClassParser.class的processImports方法,处理Import注解
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);//添加进importBeanDefinitionRegistrars,ConfigurationClassBeanDefinitionReader 调用configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}//ConfigurationClassBeanDefinitionReader 加载源码
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {registrars.forEach((registrar, metadata) ->registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));//调用mybatis的钩子
}
第四步:扫描器何时被添加到 Spring
Spring Boot 阶段触发方法源码位置
SpringApplication.runrefresh()AbstractApplicationContext.refresh()容器启动
BeanDefinitionRegistryPostProcessorPostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()早于普通 Bean 创建
ImportBeanDefinitionRegistrar 回调AutoConfiguredMapperScannerRegistrar.registerBeanDefinitions(...)立即执行

✅ 问题1:@Mapper 如何被注册到 Spring(钩子)

1.1 入口钩子:@Import(AutoConfiguredMapperScannerRegistrar.class)

Spring Boot 2.6+ 的 starter 里:

@Configuration(proxyBeanMethods = false)
@Import(AutoConfiguredMapperScannerRegistrar.class)   // 👈 关键钩子
@ConditionalOnMissingBean(MapperFactoryBean.class)
static class MapperScannerRegistrarNotFoundConfiguration {}
  • AutoConfiguredMapperScannerRegistrar 实现了 ImportBeanDefinitionRegistrar
    因此 Spring 会在 BeanDefinitionRegistryPostProcessor 阶段 回调:
public void registerBeanDefinitions(AnnotationMetadata meta, BeanDefinitionRegistry registry) {MybatisProperties props = beanFactory.getBean(MybatisProperties.class);ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);scanner.setAnnotationClass(Mapper.class);   // 只扫描带 @Mapper 的接口scanner.registerFilters();                  // 过滤条件scanner.doScan(StringUtils.toStringArray(props.getMapperLocations()));
}

1.2 扫描器:ClassPathMapperScanner#doScan

重写了父类 ClassPathBeanDefinitionScanner,核心在 processBeanDefinitions

@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> holders = super.doScan(basePackages); // 正常扫描for (BeanDefinitionHolder holder : holders) {GenericBeanDefinition bd = (GenericBeanDefinition) holder.getBeanDefinition();// 👇 狸猫换太子:把接口类名替换成 MapperFactoryBeanbd.setBeanClass(MapperFactoryBean.class);bd.getConstructorArgumentValues().addGenericArgumentValue(holder.getBeanDefinition().getBeanClassName());}return holders;
}

1.3 FactoryBean:MapperFactoryBean#getObject

  • FactoryBean 由 Spring 实例化并调用 getObject()
public T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);
}

返回的正是 MyBatis 生成的 JDK 动态代理,被注册成 单例 Bean,于是:

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;   // ← 注入的就是这个代理
}

✅ 问题2:@MapperScan 显式指定包的钩子

2.1 钩子:@Import(MapperScannerRegistrar.class)

@Retention(RetentionPolicy.RUNTIME)
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {String[] basePackages() default {};
}
  • MapperScannerRegistrar 同样实现 ImportBeanDefinitionRegistrar
    BeanDefinitionRegistryPostProcessor 阶段执行:
public void registerBeanDefinitions(AnnotationMetadata meta, BeanDefinitionRegistry registry) {AnnotationAttributes attrs = AnnotationAttributes.fromMap(meta.getAnnotationAttributes(MapperScan.class.getName()));if (attrs != null) {registerBeanDefinitions(meta, attrs, registry, ...);}
}

2.2 构造并注册 MapperScannerConfigurer

ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setBasePackage("com.xxx.mapper");   // 显式包路径
scanner.setAnnotationClass(null);           // 不限注解,扫描所有接口
scanner.registerFilters();
scanner.doScan(...);                        // 同 1.2 过程

✅ 为什么能拿到所有 Mapper 接口?

步骤关键类作用
过滤条件ClassPathMapperScanner重写 isCandidateComponent:允许 接口 通过
扫描范围用户指定包递归扫描 .class
狸猫换太子processBeanDefinitionsbeanClass 改成 MapperFactoryBean
代理生成MapperFactoryBean#getObjectsqlSession.getMapper(interface)

✅ 一句话总结

  • @MapperAutoConfiguredMapperScannerRegistrar → 扫描 @Mapper → 替换为 MapperFactoryBean → 代理注入。
  • @MapperScanMapperScannerRegistrar → 扫描指定包 → 同上链路。
  • 核心钩子ImportBeanDefinitionRegistrarBeanDefinitionRegistryPostProcessor 阶段完成 接口 → FactoryBean → 代理 Bean 的转换。
http://www.dtcms.com/a/468010.html

相关文章:

  • wordpress分类法多重筛选并排序seo网站源码
  • 垣宝建设工程集团网站iis怎么创建网站
  • 网站建设crm建筑设计单位有哪些公司
  • 织梦安装网站后图片网站建设做网站可以吗
  • 江苏省建设考试网站准考证打印wordpress个人博客建站
  • 做蛋糕的英文网站电子商务网站建设可运用的技术
  • 承接网站开发 app开发宣传链接用什么软件
  • 万网个人网站建设教程wordpress 企业网站模板
  • 什么网站可以做平面赚钱外贸网站建设电话
  • 房地产开发资质上海seo优化推广
  • 城乡与住房建设部网站办事大厅烘焙培训
  • 怎么建立公司网站seo关键词排名软件流量词
  • 动漫网站建设规划书模板公司一个人做网站
  • 承德网站开发区地税重庆市住房和城乡建设人才促进网
  • 网站建设后续需要维护网站建设的税收编码
  • 北京市建设教育协会网站查询系统我的页面设计
  • 东莞市公共资源交易中心官网推广学院seo教程
  • 乌尔禾区做网站哪里好自己做商城网站 哪种好
  • 网站设计模板 英文翻译太原集团网站建设
  • 外国网站后台设计网站的版式设计
  • 保定企业网站的建设北京网络公司建站
  • 网站后台数据分析怎么做wordpress腾讯分析
  • 河南郑州广告公司网站建设郑州新感觉会所网站哪里做的
  • 生产做网站表带的制造厂家国外企业网安全问题
  • 流媒体视频网站开发网站在线咨询怎么做
  • 页面设计的要求南通网站关键字优化
  • h5模板网站有哪些湖南住建云网站
  • 农林网站建设做网站的最终目的
  • 沈阳地区精神文明建设网站贵州互联网公司
  • wordpress is ssl网络优化首先要有网站