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

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


  
  

上面的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集成的配置方式。


	
    	

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


	 //这里的值设置,只要是注解就行,不一定是Autowird,它代表只对有注解的接口类创建代理对象,否则,对basePackage下所有接口创建代理对象
        
        

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

Mybatis的Mapper接口实现Jdk动态代理源码分析_第1张图片

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

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

根据接口类创建代理对象

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

Mybatis的Mapper接口实现Jdk动态代理源码分析_第2张图片

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

 public static  T newMapperProxy(Class 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;
  }

你可能感兴趣的:(动态代理)