一:sqlSession

在上一篇文章写到了sqlSession创建

SqlSession可以说是整个MyBatis的重中之重,在SqlSession中涉及到前一篇四大对象:Executor、StatementHandler、ParameterHandler、ResultHandler,所以在此先只对SqlSession有一个大概的了解。

SqlSession sqlSession = SessionFactory.getSqlSession(resource); 
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

在代码中我们可以看到当我们构造出一个SqlSession实例过后,可以通过SqlSession构造出Mappper映射器。UserMapper是一个接口,那么我们可以肯定的是,它一定是用了Java的动态代理生成了一个代理类。

通过打断点调试我们可以发现确实产生了一个叫MapperProxy的代理类。

二:MapperProxy的代理类

下面是DefaultSqlSession的getMapper方法:

//org.apache.ibatis.session.default.DefaultSqlSession
public  T getMapper(Class type) {
    return configuration.getMapper(type, this);
}

这是一个泛型方法。看来是调用了Configuration的getMapper方法,还不是DefaultSqlSession实现了getMapper。接着再看Configuration的getMapper方法:

//org.apache.ibatis.session.Configuration
public  T getMapper(Class type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

Configuration.getMapper一共两个参数,一个是Class类型,一个是SqlSession,在DefaultSqlSession.getMapper调用Configuration.getMapper时,将传递进来的Class类型参数和其本身传递给了Configuration.getMapper。此时还不是在Configuration中实现了getMapper,看来还是一个叫做mapperRegistry的变量。this 这里是指sqlSession

//org.apache.ibatis.session.Configuration
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

看着名字好像是注册Mapper映射器的地方,想来也是,既然要得到Mapper的映射,那么所有的Mapper都要一个地方去注册(在我们的mybytis-config.xml里),注册好过后需要的时候再去查找是否已经注册,那么就是MapperRegistry,所以取一个好的变量名是非常重要的。

//org.apache.ibatis.binding.MapperRegistry
public  T getMapper(Class type, SqlSession sqlSession) {
  final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

一个叫knownMappers的变量取出MapperProxyFactory。这个knownMapper的定义:

private final Map, MapperProxyFactory> knownMappers = new HashMap, MapperProxyFactory>();

既然能用get方法取,那说明就有add方法咯?果不其然我们在MapperRegistry类中发现了public void addMapper(Class type)方法,那么是在哪里调用的这个方法呢?

三:我们来重新理一理。

使用MyBatis的第一步是配置mybatis-config.xml,配置好过后,mybatis-config跑起来的第一步也一定是首先解析xml配置文件,将解析好的配置文件各个配置参数放入Configuration对象中,包括Mapper的配置,所以应该是在解析xml文件的某个类中解析过来后调用Configuration的方法将mapper放置到MapperRegister中。事实也的确如此,有兴趣可以跟踪下代码看看。回到MapperRegistry.getMapper的方法中。

当我们一切正确时,我们就能获取到一个MapperProxyFactory实例。想必MapperProxy代理类的生成正是通过MapperProxyFactory工厂类构建的,即第8行代码。进入MapperProxyFactory类。

//org.apache.ibatis.binding.MapperProxyFactory
public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

在这里终于看到了MapperProxy代理类,是通过sqlSession、mapperInterface、mechodCache三个参数构造的。

newInstance有一个重载方法:

protected T newInstance(MapperProxy mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

四:MapperProxy

终于是走到头了,这里就是返回的一个代理类实例。最后来看看MapperProxy。

MapperProxy是一个重要的类,所以我们将其代码全部贴出:

//org.apache.ibatis.binding.MapperProxy
public class MapperProxy implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class mapperInterface;
  private final Map methodCache;

  public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  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;
  }

}

要使用Java的动态代理就必须得实现InvocationHandler接口,从下面代码中首先判断代理对象是一个接口还是一个类,显然我们没有对mapper接口进行任何实现,那么它将生成一个MapperMethod对象,接着调用其execute方法,把sqlSession和参数传递进去。

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);
  }

参考:https://www.cnblogs.com/yulinfeng/p/6063974.html

分类: Java

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注