前言
引入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
启动
SpringBoot自动配置原理
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类注入到容器
并通过addPropertyValue添加了很多属性,包括要扫描的注解@Mapper,传递yml文件中的配置,容器中SqlSessionTemplate示例的bean名称(不是传递的引用而是名称),如该类名字在扫描Mapper注解前做好各种配置。
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
// 在这里问过自己一个很呆的问题,手动注册一个类到容器,为啥不直接添加@Component然后初始化各种变量呢?😂
MapperScannerConfigurer传递配置与DefaultListableBeanFactory
可以看到配置都传递给了ClassPathMapperScanner,由该类进行真正的扫描和注册
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 {
}
}
可以看到两个实例都指向了Proxy代理,代理都由MyFactoryBean生成,且都指向同一个SqlSessionTemplate
ClassPathMapperScanner扫描@Mapper标记接口
ClassPathMapperScanner继承自ClassPathBeanDefinitionScanner,调用父类的doScan时会自动扫描包路径下具有Mapper注解的接口,生成BeanDefinition进行注册,processBeanDefinitions再对BeanDefinition设置新的beanClass,并对新的beanClass设置参数,beanClass已初始化为MapperFactoryBean
从这里可以看到,所有Mapper标记的接口都转由MapperFactoryBean生成对象,所有需要的参数都传递到了MapperFactoryBean
代理包装SqlSession相关对象--核心MapperFactoryBean
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
被@Mapper标记的接口在其他类中注入(生命中期Populate阶段讲过)或用上下文显示获取bean时getObject会被调用,以此为入口整理出相关类图ProcessOn
- 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)
}
- MapperFactoryBean调用getObject()实际是调用Configuration的getMapper,因为Configuration会扫描xml,以Class类型为key,MapperProxyFactory为value注入到map中
- MapperProxyFactory创建代理对象返回,代理类为MapperProxy
- MapperProxy内部存在Map<Method, MapperMethodInvoker>方法与方法调用的映射
- 非default方法的执行交给PlainMethodInvoker的MapperMethod
- MapperMethod内部通过类型分流交给SqlSession执行,此时的SqlSession为SqlSessionTemplate
- SqlSessionTemplate的语句执行会交给sqlSessionProxy由SqlSessionInterceptor代理
- SqlSessionInterceptor在执行指令之前创建DefaultSqlSession(内部配置了带有事务的执行器,事务负责管理连接,提交和回滚)
- SqlSessionInterceptor负责简单逻辑:交给DefaultSqlSession指令执行,提交,关闭SqlSession