zoukankan      html  css  js  c++  java
  • mybatic MapperScannerConfigurer的原理

    原文地址:http://www.cnblogs.com/fangjian0423/p/spring-mybatis-MapperScannerConfigurer-analysis.html

    前言

    本文将分析mybatis与spring整合的MapperScannerConfigurer的底层原理,之前已经分析过java中实现动态,可以使用jdk自带api和cglib第三方库生成动态代理。本文分析的mybatis版本3.2.7,mybatis-spring版本1.2.2。

    MapperScannerConfigurer介绍

    MapperScannerConfigurer是spring和mybatis整合的mybatis-spring jar包中提供的一个类。

    想要了解该类的作用,就得先了解MapperFactoryBean

    MapperFactoryBean的出现为了代替手工使用SqlSessionDaoSupport或SqlSessionTemplate编写数据访问对象(DAO)的代码,使用动态代理实现。

    比如下面这个官方文档中的配置:

    <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">

    <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />

    <property name="sqlSessionFactory" ref="sqlSessionFactory" />

    </bean>

    org.mybatis.spring.sample.mapper.UserMapper是一个接口,我们创建一个MapperFactoryBean实例,然后注入这个接口和sqlSessionFactory(mybatis中提供的SqlSessionFactory接口,MapperFactoryBean会使用SqlSessionFactory创建SqlSession)这两个属性。

    之后想使用这个UserMapper接口的话,直接通过spring注入这个bean,然后就可以直接使用了,spring内部会创建一个这个接口的动态代理。

    当发现要使用多个MapperFactoryBean的时候,一个一个定义肯定非常麻烦,于是mybatis-spring提供了MapperScannerConfigurer这个类,它将会查找类路径下的映射器并自动将它们创建成MapperFactoryBean。

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

    <property name="basePackage" value="org.mybatis.spring.sample.mapper" />

    </bean>

    这段配置会扫描org.mybatis.spring.sample.mapper下的所有接口,然后创建各自接口的动态代理类。

    注意:这里会扫描下面所有的接口,但是经过我的实验,只会对在mapper.xml中的namespace中配置了的接口才会生成动态代理类。不会影响到basePackage下面的其他接口。

    MapperScannerConfigurer底层代码分析

    以以下代码为示例进行讲解(部分代码,其他代码及配置省略):

    package org.format.dynamicproxy.mybatis.dao;

    public interface UserDao {

    public User getById(int id);

    public int add(User user);

    public int update(User user);

    public int delete(User user);

    public List<User> getAll();

    }

     

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

    <property name="basePackage" value="org.format.dynamicproxy.mybatis.dao"/>

    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>

    </bean>

    我们先通过测试用例debug查看userDao的实现类到底是什么。

    我们可以看到,userDao是1个MapperProxy类的实例。
    看下MapperProxy的源码,没错,实现了InvocationHandler,说明使用了jdk自带的动态代理。

    public class MapperProxy<T> implements InvocationHandler, Serializable {

     

    private static final long serialVersionUID = -6424540398559729838L;

    private final SqlSession sqlSession;

    private final Class<T> mapperInterface;

    private final Map<Method, MapperMethod> methodCache;

     

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {

    this.sqlSession = sqlSession;

    this.mapperInterface = mapperInterface;

    this.methodCache = methodCache;

    }

     

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    if (Object.class.equals(method.getDeclaringClass())) {

    try {

    return method.invoke(this, args);

    } catch (Throwable t) {

    throw ExceptionUtil.unwrapThrowable(t);

    }

    }

    final MapperMethod mapperMethod = cachedMapperMethod(method);

    return mapperMethod.execute(sqlSession, args);

    }

     

    private MapperMethod cachedMapperMethod(Method method) {

    MapperMethod mapperMethod = methodCache.get(method);

    if (mapperMethod == null) {

    mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());

    methodCache.put(method, mapperMethod);

    }

    return mapperMethod;

    }

     

    }

     

    注:了解概念看到这里就可以了,如果需要详细研究可以继续看下面的内容。

     

    下面开始分析MapperScannerConfigurer的源码

    MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口是一个可以修改spring工长中已定义的bean的接口,该接口有个postProcessBeanDefinitionRegistry方法。

    然后我们看下ClassPathMapperScanner中的关键是如何扫描对应package下的接口的。

    其实MapperScannerConfigurer的作用也就是将对应的接口的类型改造为MapperFactoryBean,而这个MapperFactoryBean的属性mapperInterface是原类型。MapperFactoryBean本文开头已分析过。

    所以最终我们还是要分析MapperFactoryBean的实现原理!

    MapperFactoryBean继承了SqlSessionDaoSupport类,SqlSessionDaoSupport类继承DaoSupport抽象类,DaoSupport抽象类实现了InitializingBean接口,因此实例个MapperFactoryBean的时候,都会调用InitializingBean接口的afterPropertiesSet方法。

    DaoSupport的afterPropertiesSet方法:

    MapperFactoryBean重写了checkDaoConfig方法:

    然后通过spring工厂拿对应的bean的时候:

    这里的SqlSession是SqlSessionTemplate,SqlSessionTemplate的getMapper方法:

    Configuration的getMapper方法,会使用MapperRegistry的getMapper方法:

    MapperRegistry的getMapper方法:

    MapperProxyFactory构造MapperProxy:

    没错! MapperProxyFactory就是使用了jdk组带的Proxy完成动态代理。
    MapperProxy本来一开始已经提到。MapperProxy内部使用了MapperMethod类完成方法的调用:

    下面,我们以UserDao的getById方法来debug看看MapperMethod的execute方法是如何走的。

    @Test

    public void testGet() {

    int id = 1;

    System.out.println(userDao.getById(id));

    }

    <select id="getById" parameterType="int" resultType="org.format.dynamicproxy.mybatis.bean.User">

    SELECT * FROM users WHERE id = #{id}

    </select>


  • 相关阅读:
    io学习
    asp.net文件上传进度条研究
    asp.net页面中的Console.WriteLine结果如何查看
    谨慎跟随初始目的不被关联问题带偏
    android 按钮特效 波纹 Android button effects ripple
    安卓工作室 日志设置
    安卓工作室 文件浏览器 android studio File browser
    一个新的Android Studio 2.3.3可以在稳定的频道中使用。A new Android Studio 2.3.3 is available in the stable channel.
    新巴巴运动网上商城 项目 快速搭建 教程 The new babar sports online mall project quickly builds a tutorial
    码云,git使用 教程-便签
  • 原文地址:https://www.cnblogs.com/xiaolang8762400/p/7399274.html
Copyright © 2011-2022 走看看