mybatis源码学习

143 阅读3分钟

前言

引入依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
</dependency>

image.png image.png

启动

SpringBoot自动配置原理 image.png MybatisAutoConfiguration启动时生成SqlSessionFactory、SqlSessionTemplate bean对象,将会注入到代理中作为成员

接口扫描到容器

自测最小示例

源码

public interface MyClass {
}
public class MyFactoryBean<T> implements FactoryBean<T> {
    private SqlSessionTemplate sqlSessionTemplate;
    private final Class<T> interfaceType;
    public MyFactoryBean(Class<T> interfaceType) {
        this.interfaceType = interfaceType;
    }
    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate){
        this.sqlSessionTemplate = sqlSessionTemplate;
    }
    @Override
    public T getObject() throws Exception {
        return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(), new Class[]{interfaceType}, new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (Object.class.equals(method.getDeclaringClass())) {
                    return method.invoke(this, args);
                } else {
                    return "hello world";
                }
            }
        });
    }
    @Override
    public Class<?> getObjectType() {
        return this.interfaceType;
    }
}
@Configuration
@Import({MyClassRegistrar.class})
public class AppConfig {
    public AppConfig() {
        System.out.println("app config");
    }
}
public class MyClassRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyClass.class);
        AbstractBeanDefinition definition = builder.getBeanDefinition();
        String beanClassName = definition.getBeanClassName();
        definition.setBeanClass(MyFactoryBean.class);
        definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
        definition.getPropertyValues().add("sqlSessionTemplate",
                new RuntimeBeanReference("sqlSessionTemplate"));
        registry.registerBeanDefinition(beanClassName,definition);
    }
}

核心就几行代码

// 获取MyClass的Bean定义
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyClass.class); 
AbstractBeanDefinition definition = builder.getBeanDefinition(); 
String beanClassName = definition.getBeanClassName(); 
// 将MyClass的Class类型改为MyFactoryBean
definition.setBeanClass(MyFactoryBean.class); 
// 给MyFactoryBean构造函数添加参数
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); 
// 给MyFactoryBean属性设置sqlSessionTemplate引用,调用MyFactoryBean的set接口
definition.getPropertyValues().add("sqlSessionTemplate", new 
RuntimeBeanReference("sqlSessionTemplate")); 
// 注册BeanDefinition
registry.registerBeanDefinition(beanClassName,definition);
// 这里的registry是DefaultListableBeanFactory类型的实例,就是bean生命周期中操作bean的类

MyClass接口的所有调用都从代理进入invoke,且MyFactoryBean已拥有SqlSessionTemplate进行后续sql操作

mybatis扫描流程

入口AutoConfiguredMapperScannerRegistrar手动将MapperScannerConfigurer类注入到容器

image.png 并通过addPropertyValue添加了很多属性,包括要扫描的注解@Mapper,传递yml文件中的配置,容器中SqlSessionTemplate示例的bean名称(不是传递的引用而是名称),如该类名字在扫描Mapper注解前做好各种配置。

registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
// 在这里问过自己一个很呆的问题,手动注册一个类到容器,为啥不直接添加@Component然后初始化各种变量呢?😂

MapperScannerConfigurer传递配置与DefaultListableBeanFactory

可以看到配置都传递给了ClassPathMapperScanner,由该类进行真正的扫描和注册 image.png MapperScannerConfigurer实现自BeanDefinitionRegistryPostProcessor,目的都是为了得到DefaultListableBeanFactory参数,如同AutoConfiguredMapperScannerRegistrar实现自ImportBeanDefinitionRegistrar 于是仿照MapperScannerConfigurer得到一种新的类或接口注册到容器的方式

public interface MyClassAnother {
}
@Component
public class MyClassRegistrarAnother implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyClassAnother.class);
        AbstractBeanDefinition definition = builder.getBeanDefinition();
        String beanClassName = definition.getBeanClassName();
        definition.setBeanClass(MyFactoryBean.class);
        definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
        definition.getPropertyValues().add("sqlSessionTemplate",
                new RuntimeBeanReference("sqlSessionTemplate"));
        registry.registerBeanDefinition(beanClassName,definition);
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

image.png 可以看到两个实例都指向了Proxy代理,代理都由MyFactoryBean生成,且都指向同一个SqlSessionTemplate

ClassPathMapperScanner扫描@Mapper标记接口

ClassPathMapperScanner继承自ClassPathBeanDefinitionScanner,调用父类的doScan时会自动扫描包路径下具有Mapper注解的接口,生成BeanDefinition进行注册,processBeanDefinitions再对BeanDefinition设置新的beanClass,并对新的beanClass设置参数,beanClass已初始化为MapperFactoryBean image.png image.png image.png 从这里可以看到,所有Mapper标记的接口都转由MapperFactoryBean生成对象,所有需要的参数都传递到了MapperFactoryBean

代理包装SqlSession相关对象--核心MapperFactoryBean

@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

被@Mapper标记的接口在其他类中注入(生命中期Populate阶段讲过)或用上下文显示获取bean时getObject会被调用,以此为入口整理出相关类图ProcessOn mybatis-1 (2).png

  1. SqlSessionTemplate、SqlSessionFactory、Configuration初始化时机,MybatisAutoConfiguration自启动时
@Bean
@ConditionalOnMissingBean
SqlSessionFactory sqlSessionFactory(DataSource dataSource){
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    Configuration configuration = this.properties.getConfiguration();
    factory.setConfiguration(configuration);
    return factory;
}
@Bean
@ConditionalOnMissingBean
SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
    return new SqlSessionTemplate(sqlSessionFactory)
}
  1. MapperFactoryBean调用getObject()实际是调用Configuration的getMapper,因为Configuration会扫描xml,以Class类型为key,MapperProxyFactory为value注入到map中
  2. MapperProxyFactory创建代理对象返回,代理类为MapperProxy
  3. MapperProxy内部存在Map<Method, MapperMethodInvoker>方法与方法调用的映射
  4. 非default方法的执行交给PlainMethodInvoker的MapperMethod
  5. MapperMethod内部通过类型分流交给SqlSession执行,此时的SqlSession为SqlSessionTemplate
  6. SqlSessionTemplate的语句执行会交给sqlSessionProxy由SqlSessionInterceptor代理
  7. SqlSessionInterceptor在执行指令之前创建DefaultSqlSession(内部配置了带有事务的执行器,事务负责管理连接,提交和回滚)
  8. SqlSessionInterceptor负责简单逻辑:交给DefaultSqlSession指令执行,提交,关闭SqlSession

sql语句执行流程

参数解析与结果封装