我们在使用Spring+mybatis的时候,经常都是直接写一个接口和一个对应的 ***Mapper.xml文件,然后业务代码就可以直接注入这个接口了。它是如何做到的呢?
接口:
xml
想搞清楚这个问题,那还是要从Mybatis底层源码进行分析的。Mybatis是一个非常优先的框架,它大量的将所谓的设计模式运用到了底层源码当中。对于不太了解动态代理设计模式的,可以参考我的另外2篇博客
《大话设计模式》——读后感 (4)为别人做嫁衣?——动态代理模式(2)_chen_yao_kerr的博客-CSDN博客
Spring之AOP技术 (6)_chen_yao_kerr的博客-CSDN博客
了解了动态代理技术,在Mybatis中,我们需要关注一个类 MapperProxyFactory。说的简单点,这个类就是负责根据接口与sqlSession来创建MapperProxy对象的 。
/*
* Copyright ${license.git.copyrightYears} the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ibatis.session.SqlSession;
/**
*
* 用于生成mapper接口动态代理的实例对象;
* @author Lasse Voss
*/
public class MapperProxyFactory<T> {
//mapper接口的class对象
private final Class<T> mapperInterface;
//key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//创建实现了mapper接口的动态代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//每次调用都会创建新的MapperProxy对象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
而MapperProxy干嘛的呢?其实,它就是实现了 InvocationHandler 接口的一个动态代理类. 我们之前学习动态代理的时候,不仅要给动态代理类传递接口信息,还要传递这个业务接口的具体实现类。这样,动态代理就可以代理具体的业务实现类了。
但是,在MapperProxy中,我们并没有这样的实现类,反而是直接持有了接口信息,思考一下为什么呢?
ok,言归正传。我们知道了MapperProxyFactory就是负责创建MapperProxy对象的,而这个对象是一个动态代理类,也就是说它可以根据 业务接口与sqlSession信息 生成一个代理类。
此处是不是非常的精妙,动态代理设计模式 与 简单工厂设计模式 的典型运用。
那么,整个流程是什么样的呢?
1. 就是sqlSession对象调用getMapper方法,最终进入了 MapperRegistry类的getMapper方法。
2. 在这个方法中,我们直接获取到MapperProxyFactory对象,并且直接调用 newInstance方法
3. 而MapperProxyFactory的newInstance方法,就是负责创建动态代理类的。
因此,mybatis的接口是无法被实例化的。实例化的是一个持有该接口信息的动态代理类。