MyBatis-Spring 如何通过巧妙的 BeanDefinition 替换和 FactoryBean 机制,将 MyBatis 的 Mapper 接口无缝集成到 Spring 容器中
好的!我来整合分析完整的源码流程,并给出详细的流程图。
1. 完整源码流程分析
阶段一:Spring 启动扫描阶段
位置:org.mybatis.spring.annotation.MapperScannerRegistrar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 1. 解析 @MapperScan 注解
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
// 2. 创建扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 3. 开始扫描包路径
scanner.scan(basePackages.toArray(new String[0]));
}
}
位置:org.mybatis.spring.mapper.ClassPathMapperScanner
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 【关键】Bean Class 替换魔法
String originalMapperClass = definition.getBeanClassName();
// 替换为 MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(originalMapperClass);
definition.setBeanClass(MapperFactoryBean.class);
// 设置属性
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
definition.getPropertyValues().add("mapperInterface", originalMapperClass);
// 此时 BeanDefinition 变为:
// beanClass: MapperFactoryBean
// constructorArgs: [UserMapper.class]
// properties: {sqlSessionFactory: xxx, mapperInterface: UserMapper.class}
}
}
}
阶段二:Spring Bean 创建触发阶段
位置:org.springframework.beans.factory.support.DefaultListableBeanFactory
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory {
// Spring 启动时预实例化单例 Bean
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 【触发】获取 Bean 实例
getBean(beanName);
}
}
}
}
位置:org.springframework.beans.factory.support.AbstractBeanFactory
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport {
protected <T> T doGetBean(String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) {
// 【核心逻辑】处理 FactoryBean
return getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
// 检查是否是 FactoryBean
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
// 【关键】FactoryBean 特殊处理
Object object = getCachedObjectForFactoryBean(beanName);
if (object == null) {
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
}
位置:org.springframework.beans.factory.support.FactoryBeanRegistrySupport
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) {
try {
// 【最终触发点】调用 FactoryBean.getObject()
return factory.getObject();
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception", ex);
}
}
}
阶段三:Mapper 代理创建阶段
位置:org.mybatis.spring.mapper.MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
// 【委托给 MyBatis 创建代理】
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public SqlSession getSqlSession() {
// 返回 SqlSessionTemplate
return this.sqlSessionTemplate;
}
}
位置:org.mybatis.spring.SqlSessionTemplate
public class SqlSessionTemplate implements SqlSession {
public <T> T getMapper(Class<T> type) {
// 获取 Configuration
return getConfiguration().getMapper(type, this);
}
}
位置:org.apache.ibatis.session.Configuration
public class Configuration {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 从 MapperRegistry 获取
return mapperRegistry.getMapper(type, sqlSession);
}
}
位置:org.apache.ibatis.binding.MapperRegistry
public class MapperRegistry {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 获取 MapperProxyFactory
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 创建 MapperProxy
return mapperProxyFactory.newInstance(sqlSession);
}
}
位置:org.apache.ibatis.binding.MapperProxyFactory
public class MapperProxyFactory<T> {
public T newInstance(SqlSession sqlSession) {
// 创建 MapperProxy(InvocationHandler)
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// JDK 动态代理创建代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface },
mapperProxy);
}
}
阶段四:方法调用拦截阶段
位置:org.apache.ibatis.binding.MapperProxy
public class MapperProxy<T> implements InvocationHandler {
@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);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
return methodCache.computeIfAbsent(method, m -> {
// 创建 MapperMethod
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
});
}
}
位置:org.apache.ibatis.binding.MapperMethod
public class MapperMethod {
public Object execute(SqlSession sqlSession, Object[] args) {
// 根据 SQL 类型执行不同操作
switch (command.getType()) {
case SELECT:
if (method.returnsMany()) {
// 调用 SqlSessionTemplate.selectList()
result = executeForMany(sqlSession, args);
}
// ... 其他操作类型
}
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
// 【委托给 SqlSessionTemplate】
return sqlSession.selectList(command.getName(), param);
}
}
2. 完整流程图
graph TD
A[Spring容器启动] --> B[@MapperScan扫描]
B --> C[ClassPathMapperScanner]
C --> D{扫描Mapper接口}
D --> E[找到UserMapper接口]
E --> F[创建BeanDefinition]
F --> G[Bean Class替换]
G --> H[BeanClass: MapperFactoryBean<br>构造参数: UserMapper.class]
H --> I[Spring初始化单例Bean]
I --> J{需要UserMapper Bean?}
J -->|依赖注入| K[AbstractBeanFactory.getBean]
J -->|显式调用| K
J -->|@Autowired| K
K --> L[创建MapperFactoryBean实例]
L --> M[FactoryBeanRegistrySupport.getObjectFromFactoryBean]
M --> N[调用MapperFactoryBean.getObject]
N --> O[SqlSessionTemplate.getMapper]
O --> P[Configuration.getMapper]
P --> Q[MapperRegistry.getMapper]
Q --> R[MapperProxyFactory.newInstance]
R --> S[创建MapperProxy动态代理]
S --> T[JDK Proxy.newProxyInstance]
T --> U[返回MapperProxy给Spring]
U --> V[Spring注册为UserMapper Bean]
V --> W[业务代码调用userMapper.method]
W --> X[MapperProxy.invoke拦截]
X --> Y[MapperMethod.execute]
Y --> Z[SqlSessionTemplate.xxx]
Z --> AA[SqlSessionInterceptor事务管理]
AA --> AB[真实数据库操作]
style A fill:#e1f5fe
style N fill:#fff3e0
style S fill:#e8f5e8
style X fill:#fce4ec
style AB fill:#ffebee
3. 核心触发时机总结
3.1 初始创建时机
// 时机1: Spring 启动预实例化
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args); // ← 这里触发
}
}
// 时机2: 懒加载首次访问
@Lazy
@Autowired
private UserMapper userMapper; // ← 首次访问时触发
3.2 运行时触发时机
// 时机3: 依赖注入
@Service
public class UserService {
@Autowired // ← 创建 UserService 时触发
private UserMapper userMapper;
}
// 时机4: 显式获取
@Bean
public SomeBean someBean(ApplicationContext context) {
UserMapper mapper = context.getBean(UserMapper.class); // ← 调用时触发
return new SomeBean(mapper);
}
// 时机5: 方法参数注入
@EventListener
public void handleEvent(SomeEvent event, UserMapper userMapper) { // ← 事件触发时
userMapper.doSomething();
}
4. 关键设计模式总结
- 工厂模式:
MapperFactoryBean作为 Mapper 的工厂 - 代理模式:
MapperProxy拦截方法调用 - 装饰器模式:多层代理增强功能
- 模板方法:
SqlSessionTemplate统一处理事务 - 注册表模式:
MapperRegistry管理 Mapper 注册
这个完整流程展示了 MyBatis-Spring 如何通过巧妙的 BeanDefinition 替换和 FactoryBean 机制,将 MyBatis 的 Mapper 接口无缝集成到 Spring 容器中,让开发者可以像使用普通 Spring Bean 一样使用 Mapper 接口。
更多推荐


所有评论(0)