Mybatis的Mapper接口实现Jdk动态代理源码分析

    我们都知道Mybatis的Mapper接口可以直接来依赖注入,它本没有实现类,又是如何实例化的呢。先看看Mybatis没有集成Spring是如何实例化的。

<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>

上面的MapperFactoryBean就可以实例化出mapperInterface类型的Bean,因为MapperFactoryBean实现了FactoryBean接口的getObject方法,可以实例化出我们想要的Bean,实际上是通过Jdk动态代理得到的Bean。

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

调用方式:

SqlSession session = sqlSessionFactory.openSession();  
try {  
  User user= (User) session.selectOne("org.mybatis.example.UserMapper.selectBlog", 1);  
} finally {  
  session.close();  
}  

上面的UserMapper接口是一个个的配置来实例化的,每次需要openSession,close Session,重复工作太多,这样肯定比较麻烦,下面看看项目中常用的Mybatis与Spring集成的配置方式。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="mapperLocations" value="classpath*:com/**/mapper/**/*Mapper*.xml" />
    	<property name="dataSource" ref="dataSource" />
</bean>

这个SqlSessionFactoryBean会做哪些事呢,主要是把*Mapper*.xml文件与*Mapper*.java加载进来,根据namespace加载对应的接口类到MapperRegistry,把方法名与*Mapper*.xml里的Select id对应起来等等。MapperRegistry相当于是一个缓存,后面创建代理对象是会用到。

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="annotationClass" value="javax.annotation.Autowire"></property> //这里的值设置,只要是注解就行,不一定是Autowird,它代表只对有注解的接口类创建代理对象,否则,对basePackage下所有接口创建代理对象
        <property name="basePackage" value="com.***.mapper" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

MapperScannerConfigurer可以对basePackage下所有Mapper接口创建代理对象,而不是像上面一个个配置。因为他实现了接口BeanDefinitionRegistryPostProcessor,它就是用来自定义Bean的,看看源码它是如何实现的。


上面设置mapperInterface,BeanClass其实就是上面讲的Mybatis没有集成Spring的配置方式。

我们知道了代理对象是通过MapperFactoryBean创建的。具体看看是如何创建的。

根据接口类创建代理对象

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


这里就用到了之前说的MapperRegistry,只是用来校验该接口是否存在。这里又用到了MapperProxy,是一个代理类,实现了InvocationHandler接口。

 public static <T> T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
    ClassLoader classLoader = mapperInterface.getClassLoader();
    Class<?>[] interfaces = new Class[]{mapperInterface};
    MapperProxy proxy = new MapperProxy(sqlSession);
    return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
  }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (method.getDeclaringClass() == Object.class) {
      return method.invoke(this, args);
    }
    final Class<?> declaringInterface = findDeclaringInterface(proxy, method);
    final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
    final Object result = mapperMethod.execute(args);
    if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
      throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
当我们调用Mpper接口方法时,会被MapperProxy代理类拦截调用,主要调用实现就是mapperMethod.execute(args),看看

底层实现

public Object execute(Object[] args) {
    Object result = null;
    if (SqlCommandType.INSERT == type) {
      Object param = getParam(args);
      result = sqlSession.insert(commandName, param);
    } else if (SqlCommandType.UPDATE == type) {
      Object param = getParam(args);
      result = sqlSession.update(commandName, param);
    } else if (SqlCommandType.DELETE == type) {
      Object param = getParam(args);
      result = sqlSession.delete(commandName, param);
    } else if (SqlCommandType.SELECT == type) {
      if (returnsVoid && resultHandlerIndex != null) {
        executeWithResultHandler(args);
      } else if (returnsMany) {
        result = executeForMany(args);
      } else if (returnsMap) {
        result = executeForMap(args);
      } else {
        Object param = getParam(args);
        result = sqlSession.selectOne(commandName, param); //跟上面没有集成Spring调用方式一样的。
      }
    } else {
      throw new BindingException("Unknown execution method for: " + commandName);
    }
    return result;
  }

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值