MyBatis学习——动态代理(源码)

1,动态代理

MyBatis使用Proxy动态代理实现

2,MyBatis怎么实现动态代理的呢?

我们获取到SQLSession后,会调用getMapper()方法来返回对象实例,那么这块到底是干了什么?

我们跟进源码看一看:

DefaultSqlSession类:

可以看到它是调用了configuration类的getMapper方法。configuration类主要是存放了所有有关的配置信息

它又调用了mapperRegistry的getMapper()方法,我们现在还不知道mapperRegistry这是干什么的,现在我们继续跟进

MyBatis学习——动态代理(源码)_第1张图片

knownMappers是一个字典类型。从Key的类型上我们可以判断出来是一个类一个动态代理工厂。

可以看到这个方法刚开始就是从这个集合中获取mapper,那么我们什么时候将mapper注册进去的呢?

在解析xml文件的时候就已经处理了标签。使用XMLMapperBuilder类解析的时候有一个方法parse()进行处理。会将所有的mapper注册到MapperRegistry里面。代码逻辑如下。实现方式很简单就是将mapper标签里面的namespace属性添加到集合里面

 public void parse() {
        if (!configuration.isResourceLoaded(resource)) {
            //
            //解析mapper节点
            configurationElement(parser.evalNode("/mapper"));
            configuration.addLoadedResource(resource);
            //这里就是绑定mapper和class的地方
            bindMapperForNamespace();
        }

        parsePendingResultMaps();
        parsePendingChacheRefs();
        parsePendingStatements();
    }
    private void bindMapperForNamespace() {
        String namespace = builderAssistant.getCurrentNamespace();
        if (namespace != null) {
            Class boundType = null;
            try {
                boundType = Resources.classForName(namespace);
            } catch (ClassNotFoundException e) {
                //ignore, bound type is not required
            }
            if (boundType != null) {
                if (!configuration.hasMapper(boundType)) {
                    // Spring may not know the real resource name so we set a flag
                    // to prevent loading again this resource from the mapper interface
                    // look at MapperAnnotationBuilder#loadXmlResource
                    configuration.addLoadedResource("namespace:" + namespace);
                    configuration.addMapper(boundType);
                }
            }
        }
    }    

通过前面注册了class的全路径,使用mapperProxyFactory来创建mapper接口实现类。这里的mapperProxyFactory会为每一个mapper都对应一个mapperProxyFactory。因为configuration.addMapper(boundType);里面使用map接口为每一个mapper都创建了一个工厂

MyBatis学习——动态代理(源码)_第2张图片

向knownMappers这个集合添加了这个Mapper

我们再回到getMapper()方法:

MyBatis学习——动态代理(源码)_第3张图片

进入这个newInstance方法

可以看到它创建了一个mapper ProXy对象,看到这个是不是就能想到我们的动态代理那个继承Invocation Handler类的那个XXXProXy类是吧。

MyBatis学习——动态代理(源码)_第4张图片

调用它 的invoke方法

上面代码中可以看到如果是执行Object类的方式那么直接调用method.invoke。如果不是Object的方法,那么执行的是MapperMethod方法。我们知道一个接口没有实现是不能够被实例化的,并且我们在写接口时,确实没有给任何实现,那么Mybatis是怎么帮我们做事的呢?就是上面这段代码。首先通过代理类生成代理对象。当执行接口中的方法时,都是调用这个invoke方法,当你不是调用Object下面的方法,那么统一都执行MapperMethod方法来执行。

从缓存中获得执行方法对应的MapperMethod类实例。如果MapperMethod类实例不存在的情况,创建加入缓存并返回相关的实例。最后调用MapperMethod类的execute方法。

 

MyBatis学习——动态代理(源码)_第5张图片

mapperMethod持有接口名,方法名,configuration。而接口名对应mapper标签的namespace;方法名对应