SpringBoot整合Mybatis原理分析
共 7785字,需浏览 16分钟
·
2021-03-03 11:00
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
Mybatis核心概念
Configuration 管理mysql-config.xml全局配置关系类
SqlSessionFactorySession管理工厂接口
Session SqlSession是一个面向用户(程序员)的接口。SqlSession中提供了很多操作数据库的方法
Executor 执行器是一个接口(基本执行器、缓存执行器)作用:SqlSession 内部通过执行器操作数据库
MappedStatement 底层封装对象作用:对操作数据库存储封装,包括 sql 语句、输入输出参数
StatementHandler 具体操作数据库相关的 handler 接口
Mybatis编程式用例
public void testSelect() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession(); // ExecutorType.BATCH
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}
配置扫描解析过程
1、springboot使用自动装配原理注入SqlSessionFactory,对应方法路径为org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration#sqlSessionFactory。如果使用的是mybatis-config.xml进行配置,那么在上述方法中会使用XMLConfigBuilder对mybatis-config.xml文件进行解析,并且会把mybatis-config.xml文件中配置的一个个mapper bean生成相应的mapperProxyFactory并且放入mapperRegistry中.MapperRegistry里面维护的其实是一个Map容器,存储接口和代理工厂的映射关系。
2、
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan
@MapperScan是一个复合注解,其中@Import注解便是起到了扫描注册第三方组件到springIOC的作用。MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,实现了registerBeanDefinitions方法,主要功能用于动态的注册某一些具有相同特征的一批类到Spring IoC
3.
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
}
super.doScan()方法扫描@MapperScan注解中对应的Mapper路径,把该路径下所有符合候选条件的接口mapper放入ioc容器中。
processBeanDefinitions(beanDefinitions);该方法在doScan()之后进行调用,将容器中mapper bean的真正类型修改为MapperFactoryBean类型。
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // 构造函数传参,传入初始mapper类型
definition.setBeanClass(this.mapperFactoryBeanClass);
}
获得Mapper对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>
1、SqlSessionDaoSupport内部持有SqlSession的实现类SqlSessionTemplate;
2、SqlSessionDaoSupport类重写了checkDaoConfig()方法,这个方法主要用于非mybatis-config.xml方式来注册mapper的情况,这个方法会确保把所有的mapper bean生成相应的mapperProxyFactory并且放入mapperRegistry中。调用时机:在Bean的属性值设置完成时被调用(InitializingBean接口)
3、上述说到所有的mapper接口在放入spring容器之后,真正的bean类型被修改为MapperFactoryBean,而MapperFactoryBean实现了FactoryBean接口,所以程序中从ioc容器在获取对应的Mapper实例的话,走的是getObject()方法,最终调用的又是org.apache.ibatis.binding.MapperRegistry#getMapper方法;
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
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、可以看到获取Mapper的代理对象,实际上是从map中获取对应的工厂类后,最终通过JDK动态代理创建的。实质上是获取了一个MapperProxy的代理对象,MapperProxy中有SqlSession、mapperInterface、methodCache。
执行sql
@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 if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
1、由于所有的Mapper都是MapperProxy的代理对象,所以任意的方法都是执行MapperProxy的invoke()方法。
2、通过Executor、MappedStatement、SqlSession进行sql语句处理,参数填充等等,最终还是通过JDBC包中的PreparedStatement、ResultSet进行最终的调用和结果集处理;
————————————————
版权声明:本文为CSDN博主「澎仔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://blog.csdn.net/ab1024249403/article/details/113931500
粉丝福利:Java从入门到入土学习路线图
👇👇👇
👆长按上方微信二维码 2 秒
感谢点赞支持下哈