mybatis运行原理03-mapper代理对象的获取

2021-03-07  本文已影响0人  布拉德老瓜

上一篇文章中讲述了DefaultSqlSession的创建过程。它可以利用executor来完成crud操作、管理数据库连接和事务,也可以根据mapper类型来获取mapper代理对象。sql的执行放到后面讲,本文先记录一下mapper代理对象是如何生成的

    MemberDao mapper = sqlSession.getMapper(MemberDao.class);

当我们写下getMapper(mapper.class)的时候,框架在后台做了些什么事?实际上,sqlSession本身是对mapper无感知的,所有关于mapper的信息,都在configuration属性中。所以getMapper先交给了configuration来完成。

    public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }

在第一篇文章中说过,对mapper的解析完成后,以type:MapperProxyFactory(type)的形式将mapper的类型和产生对应代理对象的工厂存放到了mapperRegistry中的knowMapppers内,并将mapper的parameterMap、resultMap、statement等属性以namespace: val的形式存到了对应的HashMap里。然后在这里configuration.getMapper就将工作交给了mapperRegistry.同时,为了完成mapper与sqlSession的绑定,还将sqlSession作为参数传递了进来。

    //委派给mapperRegistry完成
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }

看看mapperRegistry是如何获取到mapper代理对象的吧。

// mapperRegistry.getMapper(...)
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

实际上,mapperRegistry并不承担创建代理对象的职责,它的责任就是完成mapper的注册。创建代理对象交给mapperProxyFactory来完成。mapperProxyFactory内部mapperInterface用于封装mapper的类型对象,methodCache则存放方法缓存。

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();
    // ······
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }

重点关注代理对象的创建,在这里首先调用的是 newInstance(SqlSession sqlSession)方法,然后调newInstance(MapperProxy<T> mapperProxy),最后由Proxy.newProxyInstance()生成代理对象。至于这中间做了什么,我们想想动态代理Proxy.newProxyInstance(...)是如何创建代理对象的吧。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

Proxy.newProxyInstance(loader, interfaces, ih)需要三个参数: 被代理对象的类加载器loader、被代理对象的接口类型数组Class<?> [] interfaces、定义了方法调用逻辑的InvocationHandler h.

先思考一下:假设现在我们有一个类对象,需要创建一个能够对它的方法进行增强的代理实例对象,该对象进行方法调用的逻辑在invocationHandler中,该如何创建这个代理对象呢?
咱也不知道哇,咱只知道动态代理...这里的invocationHandler不就有方法的实现逻辑吗?我们只需要一个对象,把该对象方法的调用关联给这个invocationHandler去做不就行了吗?
//todo : 动态代理ref:https://zhuanlan.zhihu.com/p/60805342

//invocationHandler.invoke(proxy, method, args)
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

所以Proxy类就实现了这么一个功能:动态创建一个代理对象proxyObj,将对象与InvocationHandler ih关联。然后当我们在程序中使用proxyObj.method(args)的时候,实际上就交给了ih.invoke(proxyObj, method, args)去处理。

我们现在有了前两个参数,但是当方法被调用时怎么进行处理的逻辑还没有啊。因此需要先创建一个InvocationHandler对象来,这个对象就是mapperProxy.他的构造方法很简单,就是将sqlSession, 接口和方法缓存关联进来。没什么好讲的

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -4724728412955527868L;
    private static final int ALLOWED_MODES = 15;
    private static final Constructor<Lookup> lookupConstructor;
    private static final Method privateLookupInMethod;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperProxy.MapperMethodInvoker> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperProxy.MapperMethodInvoker> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }
  //......
}

在newInstance(SqlSession sqlSession)时创建了mapperProxy,然后再使用Proxy.newInstance(interface.getClassLoader, new Class[]{interface}, mapperProxy)创建了一个对象,并将该对象与mapperProxy进行了关联,最后返回代理对象。如下。

public class MapperProxyFactory<T> {
    // ······
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }

至此,xxxMapper = getMapper(xxxMapper.class)就结束了。我们拿到了代理对象,接下来的问题就是如何使用这个代理对象了,也就是当我们调用xxxMapper .xxxMethod()时,框架在后台做了些什么。
to be continued.

上一篇下一篇

猜你喜欢

热点阅读