资讯动态

Spring Boot与MyBatis自动装配原理:从SqlSessionFactory到Mapper扫描

发布时间:2026/10/15 12:06:03 来源:尧图企业网站定制
Spring Boot 加 MyBatis 大概是Java后端最常见的组合之一但大多数时候我们只是在写Mapper接口、XML文件然后Service里直接注入。真正驱动这一切的是包里那几个不起眼的类其中两个最关键MybatisAutoConfiguration和AutoConfiguredMapperScannerRegistrar。我之前很长一段时间也只停留在“会用”的层面直到有一次遇到Mapper扫描失效的问题被逼着把源码翻了一遍才把这套自动装配的逻辑完整串起来。这篇文章想把这两条主线的原理讲清楚包括 Spring Boot 是怎么知道要加载MyBatis配置的、SqlSessionFactory在哪一步被创建、Mapper接口又是在什么时机变成可注入的Bean。适合已经写过 Spring Boot MyBatis 项目、但想深入理解底层逻辑的开发者也适合正在准备面试、想把这个经典机制讲明白的同学。1. 从自动配置到MyBatis的装配主线1.1 Spring Boot 是怎么把第三方组件拉进来的Spring Boot 最核心的魔法是EnableAutoConfiguration。它不是一个简单的注解背后是一个ImportSelector会去classpath下寻找所有自动配置类的名字然后交给Spring容器去实例化。老版本里这些名字写在META-INF/spring.factories文件里Key是org.springframework.boot.autoconfigure.EnableAutoConfiguration新版本比如Spring Boot 3.x则改成了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件每一行一个自动配置类的全限定名。这个机制决定了只要你的classpath里有某个starter的jar包Spring Boot启动的时候就会自动把这些配置类当成普通Configuration类处理。注意这里说的是“当成普通配置类处理”并不意味着里面所有Bean都会创建。每个自动配置类内部通常都有大量条件注解比如ConditionalOnClass、ConditionalOnMissingBean、ConditionalOnProperty等。这些条件才是真正的闸门。所以当你引入mybatis-spring-boot-starter时真正起作用的并不是starter本身而是它依赖的mybatis-spring-boot-autoconfigure包。这个包里的MybatisAutoConfiguration会被Spring Boot通过上述机制加载再根据当前应用的条件决定要不要创建SqlSessionFactory和SqlSessionTemplate。1.2 为什么MyBatis需要单独做自动配置在Spring里使用MyBatis原本需要你手动做几件事创建SqlSessionFactoryBean、传入DataSource、设置 XML 位置、注册SqlSessionTemplate、再配置 Mapper 扫描器。这一套操作每个项目几乎都是重复的而且很容易出错。如果没有自动配置你在每个Spring Boot项目里都要写一段类似下面这样的代码Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(classpath:mapper/*.xml)); return factoryBean.getObject(); } Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); }这还只是最基础的两个Bean。如果你要配置驼峰映射、类型别名、插件拦截器代码会更多。自动配置的价值就在于此它把整个MyBatis容器化过程标准化同时通过条件注解给用户留下覆盖口子。你想要的做得更精细可以替换掉它创建的Bean你没有特殊需求它直接用一套合理默认值把事情做完。1.3 Starter包结构带来的依赖顺序mybatis-spring-boot-starter本身几乎不写代码它的核心作用就是传递依赖。它同时依赖mybatis-spring-boot-autoconfigure、mybatis和mybatis-spring让你无需手动管理版本。有一点容易踩坑mybatis-spring-boot-starter的版本必须和Spring Boot版本兼容。比如Spring Boot 3.x对应mybatis-spring-boot-starter3.x它基于Jakarta EE规范Spring Boot 2.x则对应2.x。如果你把2.x的starter塞进3.x工程会碰到javax和jakarta包名冲突启动时直接NoClassDefFoundError。我见过不少人项目里同时出现多个版本的mybatis相关jar最后SqlSessionFactory构造器里报ClassNotFoundException。排包时的原则很简单只保留一个starter让它统一管理版本不要额外引入零散的mybatis-spring包。2. MybatisAutoConfiguration条件装配的艺术2.1 生效条件逐个看打开MybatisAutoConfiguration源码类上挂着好几个注解很多人都没仔细看过但每一个都在回答同一个问题当前应用适不适合让MyBatis自动配置接管。首先是ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })。这个条件要求classpath下必须同时存在MyBatis核心类SqlSessionFactory和Spring整合类SqlSessionFactoryBean。如果只是引入了mybatis但没有引入mybatis-spring这个条件不成立自动配置直接跳过。然后是ConditionalOnSingleCandidate(DataSource.class)。这个条件比较有意思它要求容器中DataSource类型的候选Bean只有一个或者有且只有一个被Primary标记。为什么这么设计因为SqlSessionFactory必须绑定一个数据源如果容器里有多个DataSource且没有明确主选自动配置就不知道该用哪个。很多多数据源项目启动时报DataSource相关错误根源往往在这里。接着是EnableConfigurationProperties(MybatisProperties.class)它把配置文件中mybatis.*前缀的内容绑定到属性类。例如mybatis.mapper-locations、mybatis.type-aliases-package、mybatis.configuration.map-underscore-to-camel-case等。这个机制让你可以不写一行Java配置完全靠配置文件驱动MyBatis。最后还有AutoConfigureAfter(DataSourceAutoConfiguration.class)确保MybatisAutoConfiguration在数据源自动配置完之后再执行。如果数据源都还没有创建SqlSessionFactory就无从谈起。这些条件组合在一起形成的效果是只有当Spring Boot认为MyBatis基础设施完整、数据源唯一且可用、没有其他Bean需要优先接管时自动配置才真正生效。2.2 SqlSessionFactory的创建过程当条件全部满足后MybatisAutoConfiguration会执行一个核心方法方法名一般就叫sqlSessionFactory。它接收DataSource参数这个参数由Spring容器自动注入注入的就是前面单候选条件筛选出的数据源。方法内部第一件事是new SqlSessionFactoryBean()。这个类并不是MyBatis核心库里的而是mybatis-spring提供的桥接类。它实现了Spring的FactoryBean和InitializingBean接口能够在Spring Bean生命周期中构建真正的SqlSessionFactory。随后自动配置会把属性对象里的值一条条设置进去setDataSource(dataSource)绑定数据源。setTypeAliasesPackage(properties.getTypeAliasesPackage())设置实体类别名扫描包。setTypeHandlersPackage(properties.getTypeHandlersPackage())设置类型处理器扫描包。setMapperLocations(properties.resolveMapperLocations())加载XML映射文件路径。setConfiguration(properties.getConfiguration())如果用户通过mybatis.configuration设置了大量全局参数直接设置整个Configuration对象。还会把容器中收集到的Interceptor数组、DatabaseIdProvider、ConfigurationCustomizer等扩展点注入。设置完成后调用factoryBean.afterPropertiesSet()让SqlSessionFactoryBean内部完成XML解析、插件加载、类型别名注册等动作。最后通过factoryBean.getObject()返回真正的SqlSessionFactory。这里有个细节很多人不知道mybatis.mapper-locations如果不配置自动配置并不会帮你加载任何XML。意味着如果你的SQL写在XML里就会在运行时报Invalid bound statement (not found)。常见的默认路径classpath:mapper/*.xml也需要你显式写出来。所以一个合格的项目里mybatis.mapper-locations通常是必须配置的。不过MyBatis也支持注解SQL那种情况可以不用XML。2.3 SqlSessionTemplate 与 Mapper 执行SqlSessionTemplate是SqlSessionFactory的同胞兄弟。MybatisAutoConfiguration中会继续创建它Bean ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); }SqlSessionTemplate实现了MyBatis的SqlSession接口但它是线程安全的。原生DefaultSqlSession不是线程安全的它代表一次数据库连接会话不能在多线程环境共享。Spring辅助包用一个动态代理包裹了真实的SqlSession创建逻辑每次调用SQL操作时都会从Spring容器或事务同步管理器里取得一个真正的SqlSession执行完提交或关闭。正是这个SqlSessionTemplate在后面支撑了Mapper代理的运行。当你从Spring容器中取出Mapper接口时实际执行逻辑都会经过它。理解了这条链路再回头看为什么MyBatis和Spring整合后不需要手动管理SqlSession就顺理成章了。2.4 外部扩展点与自定义覆盖MybatisAutoConfiguration不是一堵密不透风的墙它预留了几个扩展口子。第一SqlSessionFactory和SqlSessionTemplate的方法都标注了ConditionalOnMissingBean。这意味着你只要在某个配置类里自己定义同名Bean自动配置就会退让。比如你想把SqlSessionFactoryBean换成基于MyBatis-Plus的实现或者想给Configuration加自定义对象工厂就可以自己新建一个SqlSessionFactory自动配置不会再覆盖。第二拦截器可以被自动收集。MybatisAutoConfiguration的构造器会接收一个ObjectProviderInterceptor[]然后把这些拦截器统一设置到SqlSessionFactoryBean的plugins属性中。所以你只需要在Spring容器中声明Bean返回一个Interceptor实现类MyBatis在执行SQL时就会自动应用这个插件。这种自动收集的体验非常Spring Boot但同样有陷阱如果同一个插件被配置成多个实例或者拦截器本身有状态可能会出现重复执行的问题。第三ConfigurationCustomizer是专门留给配置后处理的接口。实现它可以拿到已经初始化好的org.apache.ibatis.session.Configuration对象在里面补充自定义逻辑比如注册TypeHandler、修改缓存策略等。这个API解决了很多“我想在自动配置基础上微调”的场景。还有一类覆盖场景需要注意配置文件里的mybatis.configuration和mybatis.config-location不能同时使用。config-location是让你指定一个独立的mybatis-config.xml配置文件configuration是让你用Spring Boot的properties直接配置。两者都会尝试设置Configuration对象同时配置会引发冲突启动阶段MybatisAutoConfiguration会直接抛异常提醒你。3. AutoConfiguredMapperScannerRegistrarMapper接口如何变成Bean3.1 注册入口与内部类结构MybatisAutoConfiguration里面还藏着一个静态内部类MapperScannerRegistrarNotFoundConfiguration它的定义大致如下Configuration Import(AutoConfiguredMapperScannerRegistrar.class) ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class}) public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { Override public void afterPropertiesSet() { // 如果这里没有被跳过说明没有手动配置Mapper扫描 } }这个内部类的名字很有意思MapperScannerRegistrarNotFoundConfiguration翻译过来就是“当没找到Mapper扫描注册器时用一个后备配置顶上”。它是一个条件配置类当Spring容器中不存在MapperFactoryBean或MapperScannerConfigurer时才导入AutoConfiguredMapperScannerRegistrar。如果用户已经通过MapperScan或XML方式配置了扫描器这个内部类就不会导入自动扫描逻辑从而避免重复注册Mapper。AutoConfiguredMapperScannerRegistrar的父类是MapperScannerRegistrar这是一个实现了ImportBeanDefinitionRegistrar的类。Spring在处理Import注解时如果发现导入的是一个ImportBeanDefinitionRegistrar就会调用它的registerBeanDefinitions方法让它在运行时主动向容器注册新的BeanDefinition。这是Spring一个非常重要的扩展入口MyBatis的自动扫描就是从这里开始的。3.2 拿到启动类所在包AutoConfiguredMapperScannerRegistrar要扫描Mapper接口第一步必须知道自己该扫哪个包。它的默认策略是启动类所在的包及其子包。这个信息从哪来答案是AutoConfigurationPackages。当你使用SpringBootApplication注解启动应用时这个组合注解中包含了AutoConfigurationPackageSpring Boot会注册一个AutoConfigurationPackages.Registrar把当前注解所在类也就是启动类的包名记录下来。这个包名会以一个内部Bean的形式存在容器中后续任何组件都可以通过AutoConfigurationPackages.get(beanFactory)拿到。AutoConfiguredMapperScannerRegistrar实现了BeanFactoryAware接口所以它能拿到BeanFactory实例然后调用AutoConfigurationPackages.get(this.beanFactory)返回的是一个String数组通常只有启动类所在包这一个元素。拿到这个包名后它再创建一个MapperScannerConfigurer的BeanDefinition并把该包名作为属性传进去。这样默认扫描范围就被确定下来了。这里有个非常常见的坑如果你的Mapper接口放在启动类所在包之外比如启动类在com.example.app而Mapper在com.example.mapper自动扫描是扫不到的。因为自动扫描只盯着com.example.app及其子包。很多人遇到这种情况的第一反应是给启动类加MapperScan(com.example.mapper)这确实能解决但背后真正的原因是自动扫描范围已经被启动类位置锁死了。3.3 MapperScannerConfigurer 的扫描动作MapperScannerConfigurer是mybatis-spring包里的核心类它实现了BeanDefinitionRegistryPostProcessor。这个接口允许它在Spring容器已经注册了用户自定义BeanDefinition之后、Bean实例化之前再向容器注册一批新的BeanDefinition。它的执行过程大致是Spring容器启动前期ConfigurationClassPostProcessor处理完所有配置类后会触发所有BeanDefinitionRegistryPostProcessor。MapperScannerConfigurer.postProcessBeanDefinitionRegistry()被调用。它构建一个ClassPathMapperScanner设置好basePackage、annotationClass、markerInterface等属性。调用scanner.scan(basePackage)扫描指定包下所有符合条件的接口。对每个接口生成一个BeanDefinitionbeanClass 设置为MapperFactoryBean同时在构造器参数或属性中记录该接口类型。最后把这个BeanDefinition注册到容器beanName默认是接口名首字母小写。这里有一步值得展开为什么扫描器扫描到的是接口而不是普通类因为MyBatis的Mapper接口不能直接实例化它需要被MapperFactoryBean包装。MapperFactoryBean是Spring的FactoryBean容器在获取该Bean时调用它的getObject()方法返回一个真正的Mapper代理对象。所以容器里每个Mapper对应的是一个MapperFactoryBean而不是Mapper本身。你注入Mapper接口时Spring通过工厂Bean的泛型信息知道生产出来的对象适合哪个接口。3.4 动态代理生成过程MapperFactoryBean继承了SqlSessionDaoSupport在它的afterPropertiesSet方法里会强制要求SqlSession非空并在getObject()方法中执行return getSqlSession().getMapper(this.mapperInterface);这里的SqlSession实际就是前面创建的SqlSessionTemplate。SqlSessionTemplate.getMapper()最终会调用Configuration.getMapper(Class, SqlSession)而Configuration内部维护了一个MapperRegistry里面保存了每个Mapper接口对应的MapperProxyFactory。MapperProxyFactory.newInstance()会利用JDK动态代理创建代理对象Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, new MapperProxy(sqlSession, mapperInterface, methodCache));MapperProxy实现了InvocationHandler。当你调用Mapper接口的任意一个方法时invoke()方法会被触发。它先把接口方法转换为MappedStatementId查找MyBatisConfiguration中对应的MappedStatement然后根据SQL操作类型调用SqlSession的selectOne、selectList、insert、update、delete等方法。这也是为什么MyBatis的Mapper必须是接口而不能是普通类。JDK动态代理只能代理接口无法代理没有抽象方法的普通类。如果你用CGLIB确实可以代理类但MyBatis的整个设计都围绕接口展开强行改成类会让你失去很多便利。3.5 MapperScan 和默认扫描器如何共存有读者会问MapperScan也是做同样的事情那它和AutoConfiguredMapperScannerRegistrar会不会冲突答案是通常情况下不会这正是条件注解发挥的作用。当你使用MapperScan时该注解内部会Import(MapperScannerRegistrar.class)而MapperScannerRegistrar会在容器中注册一个MapperScannerConfigurer。既然容器里已经有MapperScannerConfigurer了那上面提到的ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})就会生效自动扫描器被跳过。所以这两个机制是互斥的要么用系统默认的自动扫描要么用你手动指定的MapperScan不会同时扫描两遍。从代码维护角度看你也没必要同时使用这两种方式显式指定反而更可控。4. 排查与实战让原理落地4.1 启动自动配置报告原理说得再多最后还是要落到实际项目里。遇到MyBatis相关启动问题时我建议先看Spring Boot的自动配置报告。在application.properties或application.yml中加一行debugtrue启动后控制台会打印类似Positive matches: MybatisAutoConfiguration matched: - ConditionalOnClass found required classes org.apache.ibatis.session.SqlSessionFactory, org.mybatis.spring.SqlSessionFactoryBean - ConditionalOnSingleCandidate found a primary candidate dataSource - ConditionalOnMissingBean found no SqlSessionFactory bean Negative matches: MybatisAutoConfiguration: - ConditionalOnClass did not find required class org.apache.ibatis.session.SqlSessionFactory这个报告会把所有自动配置类的匹配和未匹配原因逐条列出来。你一眼就能看出MybatisAutoConfiguration是生效了还是被跳过了以及是哪一环条件没满足。这个信息远比你在代码里加日志排查要快。另外Spring Boot还提供了autoconfigure端点可以通过Actuator查看不过对日常排障来说debugtrue足够了。4.2 Mapper扫描不到的典型场景我当年踩过的一个坑现在总结下来就是因为启动类包位置导致默认扫描失败。项目结构是这样的启动类com.xxx.web.ApplicationMapper接口com.xxx.dal.mapper.UserMapperServicecom.xxx.biz.service.UserService启动类在web包下默认Mapper扫描只会扫com.xxx.web以及它的子包。com.xxx.dal.mapper不在扫描范围内容器里根本不存在UserMapper对应的Bean。当时项目里Service注入UserMapper一直报NoSuchBeanDefinitionException我第一反应是加MapperScan(com.xxx.dal.mapper)加了确实就好了。但当时没想明白为什么默认扫描不生效直到后来看了源码。还有另一种容易搞混的场景启动类上的SpringBootApplication(scanBasePackages com.xxx)看起来把扫描范围扩大了但scanBasePackages只影响Spring组件扫描不影响AutoConfigurationPackages记录的包。所以如果Mapper接口恰好在scanBasePackages范围内、启动类包之外依然可能扫不到。遇到这种情况最清晰的做法就是显式给启动类加上MapperScan指定你实际的Mapper包路径。这也是许多项目规范里要求每个工程必须显式配置MapperScan的原因。4.3 多数据源场景的自动配置失效多数据源是MybatisAutoConfiguration最常见的失效场景。因为ConditionalOnSingleCandidate(DataSource.class)要求容器中只有一个候选DataSource。如果你配置了两个数据源且都没有用Primary标记自动配置认为无法决定直接跳过项目启动时连SqlSessionFactory都找不到。解决办法有三种指定一个主数据源加上Primary。这样ConditionalOnSingleCandidate看到的是唯一主候选自动配置继续生效。手动创建SqlSessionFactory把两个数据源分别配置成两个SqlSessionFactoryBean并分别指定扫描器。如果你只需要MyBatis操作其中一个库而另一个库仅是辅助就只让自动配置使用主数据源辅助库用其他框架访问。我这里更推荐第一种和第二种结合。如果两个库都需要MyBatis那自动配置已经不适合你了你必须手动接管。手动接管的模范写法是为每个数据源写一套独立的SqlSessionFactoryBean、SqlSessionTemplate和MapperScannerConfigurer各自指定SQL SessionTemplate和Mapper包。这样虽然代码多一点但逻辑清晰不会出现“明明配了但MySQL和Oracle的SQL乱串”的诡异问题。4.4 常见问题速查表下面整理了一张速查表基本覆盖日常开发常见问题都是我实际调试中遇到过的异常现象常见原因解决办法启动报No qualifying bean of type SqlSessionFactory多数据源导致自动配置过期为数据源加Primary或手动创建SqlSessionFactoryService注入Mapper报NoSuchBeanDefinitionExceptionMapper接口不在默认扫描包内显式配置MapperScan并指向Mapper包启动正常调用Mapper时报Invalid bound statementXML路径没配置设置mybatis.mapper-locations: classpath:mapper/**/*.xml出现BindingException: Mapper method ... has no SQL注解SQL写错位置或XMLStatement未加载检查方法名与StatementId是否一致、XML路径是否匹配java.lang.ClassCastException代理转接口失败Mapper定义成了普通类改回接口同一个Mapper方法被拦截器执行两次自定义拦截器注册了多个实例检查Spring容器中InterceptorBean数量configuration和config-location同时配置启动报错两种方式冲突只保留一种mybatis-plus和mybatis-spring版本不兼容包冲突统一用官方starter管理依赖排除多余传递依赖5. 扩展从零复刻一个精简的Mapper扫描器5.1 自定义一个开启扫描的注解理解了上面的原理后可以动手写一个极简版扫描器来验证思路。我们不引入MyBatis只用Spring的ClassPathBeanDefinitionScanner和JDK动态代理模拟“扫描接口→注册FactoryBean→生成代理”的完整过程。先定义一个注解Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Import(MyMapperScannerRegistrar.class) public interface EnableMyMapperScan { String[] basePackages() default {}; }这个注解的作用类似MapperScan它通过Import引入注册器。5.2 实现 ImportBeanDefinitionRegistrar接下来实现一个ImportBeanDefinitionRegistrar在类中获取EnableMyMapperScan的属性值。如果用户没指定包名就自动取启动类所在包。这个逻辑可以通过AutoConfigurationPackages获得可以顺便体会一下Spring Boot在这方面的便利。public class MyMapperScannerRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware { Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { MapString, Object attributes importingClassMetadata .getAnnotationAttributes(EnableMyMapperScan.class.getName()); String[] basePackages (String[]) attributes.get(basePackages); if (basePackages.length 0 registry instanceof BeanDefinitionRegistry) { // 获取AutoConfigurationPackages取第一个包名 ... } // 创建一个扫描器在指定包下扫出所有接口 ... // 为每个接口注册一个MyMapperFactoryBean的BeanDefinition } }关键在于怎么为接口注册BeanDefinition。正常注册接口会被Spring拒绝因为接口无法实例化。但如果给它一个FactoryBean让FactoryBean来生产对象Spring就不关心目标对象到底是不是接口了。5.3 用FactoryBean和JDK代理完成动态代理定义MyMapperFactoryBeanT实现FactoryBeanTpublic class MyMapperFactoryBeanT implements FactoryBeanT { private ClassT mapperInterface; Override public T getObject() { return (T) Proxy.newProxyInstance( mapperInterface.getClassLoader(), new Class[]{mapperInterface}, (proxy, method, args) - { System.out.println(执行Mapper方法: method.getName()); return null; }); } Override public Class? getObjectType() { return mapperInterface; } Override public boolean isSingleton() { return true; } }然后把这个MyMapperFactoryBean注册到BeanDefinition中并通过constructorArgs设置mapperInterface。这样容器中每个Mapper接口都对应一个MyMapperFactoryBean你注入接口时得到的就是JDK代理对象。亲手跑一遍这个Demo后你会对MapperFactoryBean、ImportBeanDefinitionRegistrar和FactoryBean这三者的配合有非常直观的认识。比如你会看到Spring在启动时如何打印“执行Mapper方法: xxx”也会发现在代理对象的invoke方法里返回null并不会导致接口调用失败除非调用方真的拿返回值做业务操作。5.4 这个Demo能帮你理清什么这个简化版没有实现SQL解析和执行但它复刻了MyBatis-Spring整合中最核心的“接口即Bean”魔法。很多面试题其实就在考这个链路Spring容器怎么把一个接口注册成一个BeanFactoryBean在Spring里扮演什么角色条件注解和ImportBeanDefinitionRegistrar的优先级和触发时机JDK动态代理和CGLIB在这个场景中为什么必须选前者真正读懂这段链路后再去看AutoConfiguredMapperScannerRegistrar的源码你就能把每一行代码和Spring容器生命周期对应起来而不是死记硬背。我自己在实际调试中最深的体会是不要怕读源码也不要想着一次把所有细节都读完。先关注两条主线一条是SqlSessionFactory怎么来的一条是Mapper接口怎么变成代理对象。把这两条主线串起来遇到大多数MyBatis集成问题你已经能很快定位方向。建议你在自己的项目里试一下debugtrue看自动配置报告下次再遇到Mapper相关报错先看报告再动手效率会高很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑