水煮MyBatis(六)- 动态代理

140 阅读2分钟

前言

怎么说呢,这是个不算大也不是很核心的知识点,认真思考了几秒钟,还是单独介绍一下比较好。
下面这段代码是我们平时使用Mybatis时的一个常见场景,而ImageInfoMapper是一个接口,众所周知,java执行一个非静态方法,一般需要一个对象实体,毕竟java是面向对象的,所以问题就来了,ImageInfoMapper的实体是怎么创建出来的呢?

    public void select() {
        ImageInfo info = imageInfoMapper.byMd5("6e705a7733ac5gbwopmp02");
        log.info(">>>>>>>>>>>>>>>>>>>=>,{}", info);
    }

时序图

image.png

源码

从时序图中可以看出,Mapper接口的java对象是通过动态代理生成的,关键代码在MapperRegistry类中,如下:

  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 {
      // 这里,通过factory生成代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

当然,核心任然是我们熟悉的Java内置Proxy类。

  
  // 对外暴露的方法
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  
  protected T newInstance(MapperProxy<T> mapperProxy) {
    // 核心是通过java的Proxy类生成动态代理
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

小细节

在前言中看到的例子,我们还可以通过代码创建sqlSession的方式来调用,执行效果是一样的。区别在于getMapper方法,这里每次调用都会创建一个代理对象,而前言中通过注入的方式获取imageInfoMapper代理对象是通用的,单例模式下,全局唯一,不会重复创建代理对象,所以我们一般不直接使用sqlSession。

    @Test
    public void sqlSession() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ImageInfoMapper mapper = sqlSession.getMapper(ImageInfoMapper.class);
        ImageInfo info = mapper.byMd5("6e705a7733ac5gbwopmp02");
        log.info(">>>>>>>>>>>>>>>>>>>=>,{}", info);
    }

sqlSession中getMapper方法调用路线,与注入的方式也有略微差别,不过疏通同归,最终都是通过MapperProxyFactory获取代理对象。

  1. sqlSession.getMapper()
  2. Configuration.getMapper()
  3. MapperRegistry.getMapper()
  4. MapperProxyFactory.newInstance()