Spring集成Mybatis源码分析


准备示例代码

代码

@Configuration
@MapperScan(basePackages = {"com.tuling.mapper"})
@ComponentScan(basePackages = {"com.tuling"})
@Repository
public class MyBatisConfig {    // =====>   spring.xml
	/**
	 *   <mapper-scan basePackage=""/>
	 *   SqlSessionFactoryBean   
	 *  创建的过程会初始化  sqlsession 数据源  事务  mapperProxyFactory
	 * @return
	 */
	@Bean    // =====  >    <bean  class="org.mybatis.spring.SqlSessionFactoryBean">
	public SqlSessionFactoryBean sqlSessionFactory( ) throws IOException {
		SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
		factoryBean.setDataSource(dataSource());
		// 设置 MyBatis 配置文件路径
		factoryBean.setConfigLocation(new ClassPathResource("mybatis/mybatis-config.xml"));
		// 设置 SQL 映射文件路径
		factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/*.xml"));
		factoryBean.setTypeAliases(User.class);
		return factoryBean;
	}

	public DataSource dataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setUsername("root");
		dataSource.setPassword("123456");
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
	    dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_example");
		return dataSource;
	}
}
@Component
public class User {

    private Long id ;
    private String userName ;
    private Date createTime;

	public User() {
		System.out.println("user加载");
	}

	public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

UserMapper接口

public interface UserMapper {
    User selectById(Long id);
    List<User> selectAllUser();
}

UserMapper.Xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tuling.mapper.UserMapper">
    <!-- Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?-->
    <resultMap id="result" type="user" >
        <id column="id" jdbcType="BIGINT" property="id" />
        <result column="user_name" jdbcType="VARCHAR" property="userName" />
        <result column="create_time" jdbcType="DATE" property="createTime" />
    </resultMap>


    <select id="selectById"  resultMap="result" >
        select id,user_name,create_time from t_user where id=#{param1}
    </select>
</mapper>

启动类:

public class MainStarter {

    public static void main(String[] args) {
    	// 记载spring上下文
        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MyBatisConfig.class);
		UserMapper bean = (UserMapper) ioc.getBean("userMapper");
        System.out.println(bean.selectById(1L));
	}
}

运行结果:
在这里插入图片描述

一、Spring集成了mybatis的哪些东西?

集成了SqlSessionFactory

我们看下怎么集成的?

在这里插入图片描述
实现了这个接口InitializingBean的,会在初始化时候,调用afterPropertiesSet
在这里插入图片描述
在这里插入图片描述
那么这里就相当于mybatis的
在这里插入图片描述
首先声明了个Configuration
在这里插入图片描述
相当于解析mybatis的全局配置文件

/**
       * 判断configurationProperties不为空,
       * 那么就调用targetConfiguration.set方法
       *  把configurationProperties注入到Configuration对象中
       */
      Optional.ofNullable(this.configurationProperties).
          ifPresent(targetConfiguration::setVariables);

JDK8的新特性,相当于

if(this.configurationProperties!=null){
   targetConfiguration.setVariables(this.configurationProperties)
}

真正的解析我们的配置(mybatis-config.xml)的document对象

  xmlConfigBuilder.parse();

保存到configuration对象里去

最终利用简单工厂模式返回了

在这里插入图片描述

在这里插入图片描述
其实就是把mybatis的信息存到Configuration对象里去,后续执行数据库操作的东西都有了。

与Spring声明式事务的集成

Spring声明式事务在开启的时候,会获得connection对象,会存到事务同步管理器的在这里插入图片描述
mybatis要用事务,去这里拿到connection就可以了。我们来看源码:还是之前的SqlSessionFactoryBean
在这里插入图片描述
这里会new SpringManagedTransactionFactory()
在这里插入图片描述
在这里插入图片描述
可以看见就是去事务同步管理器去拿Connection,这样mybatis和spring的声明式事务就集成了。

二、Mapper接口使用的jdk动态代理

UserMapper bean = (UserMapper) ioc.getBean("userMapper");
bean.selectById(1L)
@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      /**
       * 判断我们的方法是不是我们的Object类定义的方法,若是直接通过反射调用
       */
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (method.isDefault()) {   //是否接口的默认方法
        /**
         * 调用我们的接口中的默认方法
         */
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    /**
     * 真正的进行调用,做了二个事情
     * 第一步:把我们的方法对象封装成一个MapperMethod对象(带有缓存作用的)
     */
    final MapperMethod mapperMethod = cachedMapperMethod(method);

    return mapperMethod.execute(sqlSession, args);
  }

最终

 result = sqlSession.selectOne(command.getName(), param);
@Override
  public <T> T selectOne(String statement, Object parameter) {
    /**
     * 因为在sqlSessionTemplate中的属性 sqlSessionproxy是sqlSession一个代理对象,所以会调用代理对象 SqlSessionInterceptor.invoke方法
     */
    return this.sqlSessionProxy.selectOne(statement, parameter);
  }

来到DefaultSqlSession调用mybatis的去进行数据库操作
在这里插入图片描述


三、怎么让Spring去管理Mapper动态代理?

源码跟踪
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
接下来我们看这个方法

 processBeanDefinitions(beanDefinitions)
for (BeanDefinitionHolder holder : beanDefinitions) {
      // 获取我们的bean定义
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      // 获取我们的bean定义的名称
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");

      /**
       * 进行真的偷天换日操作,也就是这二行代码是最最最最最重要的, 关乎我们 spring整合mybaits的整合 definition.setBeanClass(this.mapperFactoryBeanClass);
       */
      // 设置ConstructorArgumentValues 会通过构造器初始化对象
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      // 设置成factoryBean
      definition.setBeanClass(this.mapperFactoryBeanClass);

      definition.getPropertyValues().add("addToConfig", this.addToConfig);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值