好的,我来补充完整的调用链,包括从 Spring 容器启动到最终创建 MapperProxy 的完整流程。

完整调用链

Phase 1: 配置解析和扫描器注册阶段

Spring容器启动
    ↓
解析@MapperScan注解
    ↓
MapperScannerRegistrar.registerBeanDefinitions()
    ↓
创建MapperScannerConfigurer的BeanDefinition
    ↓
注册到Spring容器

Phase 2: BeanFactory后处理阶段

Spring容器refresh()
    ↓
invokeBeanFactoryPostProcessors()
    ↓
MapperScannerConfigurer.postProcessBeanDefinitionRegistry()  // 作为BeanFactoryPostProcessor
    ↓
创建ClassPathMapperScanner并配置
    ↓
ClassPathMapperScanner.scan()
    ↓
ClassPathMapperScanner.doScan()
    ↓
ClassPathBeanDefinitionScanner.doScan()  // 父类方法
    │   ↓
    │   forEach basePackage:
    │       ↓
    │       findCandidateComponents(basePackage)  // 扫描类路径
    │       ↓
    │       使用ASM读取class文件元数据
    │       ↓
    │       应用include/exclude过滤器
    │       ↓
    │       返回候选BeanDefinition集合
    │   ↓
    │   forEach BeanDefinition candidate:
    │       ↓
    │       解析Scope元数据
    │       ↓
    │       生成BeanName
    │       ↓
    │       postProcessBeanDefinition(candidate, beanName)  // Spring默认处理
    │       ↓
    │       处理通用注解(@Lazy, @Primary等)
    │       ↓
    │       checkCandidate()  // 检查Bean是否可注册
    │       ↓
    │       创建BeanDefinitionHolder
    │       ↓
    │       应用ScopedProxy模式
    │       ↓
    │       注册BeanDefinition到容器
    ↓
ClassPathMapperScanner.processBeanDefinitions()  // MyBatis关键处理!
    ↓
forEach BeanDefinitionHolder:
    ↓
    获取原始Mapper接口类名
    ↓
    将BeanClass替换为MapperFactoryBean
    ↓
    设置构造器参数(原始Mapper接口类名)
    ↓
    设置属性(addToConfig, sqlSessionFactory等)
    ↓
    设置自动装配模式

Phase 3: Bean实例化阶段

Spring容器getBean("userMapper")
    ↓
发现BeanDefinition的beanClass是MapperFactoryBean
    ↓
实例化MapperFactoryBean
    ↓
调用MapperFactoryBean.getObject()方法
    ↓
getSqlSession().getMapper(mapperInterface)
    ↓
SqlSessionTemplate.getMapper()
    ↓
Configuration.getMapper()
    ↓
MapperRegistry.getMapper()
    ↓
MapperProxyFactory.newInstance()
    ↓
创建MapperProxy实例
    ↓
Proxy.newProxyInstance()创建JDK动态代理
    ↓
返回MapperProxy代理对象

Phase 4: 方法调用阶段

业务代码调用userMapper.findById(1)
    ↓
JDK动态代理调用MapperProxy.invoke()
    ↓
MapperMethod.execute()
    ↓
SqlSessionTemplate.selectOne()
    ↓
SqlSessionInterceptor.invoke()  // 管理SqlSession生命周期
    ↓
DefaultSqlSession.selectOne()
    ↓
执行SQL,返回结果

关键代码细节

1. findCandidateComponents 扫描过程

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    
    // 构建包路径模式:classpath*:com/example/mapper/**/*.class
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    
    // 获取资源文件
    Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
    
    for (Resource resource : resources) {
        if (resource.isReadable()) {
            try {
                // 使用ASM读取类元数据,避免加载类
                MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                
                // 检查是否匹配过滤器
                if (isCandidateComponent(metadataReader)) {
                    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                    sbd.setResource(resource);
                    sbd.setSource(resource);
                    
                    if (isCandidateComponent(sbd)) {
                        candidates.add(sbd);
                    }
                }
            } catch (Throwable ex) {
                // 处理异常...
            }
        }
    }
    return candidates;
}

2. MapperFactoryBean.getObject() 创建代理

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    private Class<T> mapperInterface;
    
    @Override
    public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
    }
    
    @Override
    public Class<T> getObjectType() {
        return this.mapperInterface;
    }
}

3. MapperProxyFactory 创建代理实例

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
    
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), 
                new Class[] { mapperInterface }, mapperProxy);
    }
}

4. MapperProxy 方法调用处理

public class MapperProxy<T> implements InvocationHandler, Serializable {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else {
                // 缓存MapperMethod以提高性能
                return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }
}

总结

整个流程可以概括为四个主要阶段:

  1. 配置注册阶段:通过 @MapperScan 注册扫描器
  2. Bean定义扫描阶段:扫描接口并转换为 MapperFactoryBean 定义
  3. 代理创建阶段:Spring 实例化时通过 MapperFactoryBean 创建 MapperProxy
  4. 方法执行阶段:通过动态代理将方法调用转换为 SQL 执行

这个完整流程确保了 MyBatis Mapper 接口能够无缝集成到 Spring 容器中,并提供了类型安全的数据库操作接口。

更多推荐