[上一篇]:Mybatis源碼解析之配置解析
從菜菜的Mybatis源碼解析之Spring獲取Mapper過程中知道了Spring與MyBatis如何連接起來的,這篇菜菜將介紹MyBatis的重中之重MapperProxy
問題
為什么MyBatis只用寫接口不用寫實(shí)現(xiàn)就可以運(yùn)行起來?
思考
既然Spring與MyBatis連接起來了,那么通過MyBatis中的這些Mapper接口注入的類都是由Spring進(jìn)行管理的,所以需要知道Spring中這些Mapper接口對(duì)應(yīng)的Bean到底是什么。
揭秘答案-源碼
Mybatis源碼解析之Spring獲取Mapper過程這篇里說了設(shè)置了這些Mapper接口的class對(duì)象或是類的全限定名為MapperFactoryBean ,這里再貼出源碼
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
下面需要看下MapperFactoryBean的源碼
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
//intentionally empty
}
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
//主要看這個(gè)方法
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
@Override
public boolean isSingleton() {
return true;
}
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
}
public boolean isAddToConfig() {
return addToConfig;
}
}
從MapperFactoryBean源碼中看出,它繼承了SqlSessionDaoSupport并實(shí)現(xiàn)了Spring中的FactoryBean,SqlSessionDaoSupport是一個(gè)抽象的支持類,用來為你提供SqlSession,將在Mybatis源碼解析之SqlSession來自何方
中講解。因?yàn)閷?shí)現(xiàn)了FactoryBean接口所以MapperFactoryBean是一個(gè)FactoryBean。
我們知道Spring實(shí)例化一個(gè)類有兩個(gè)時(shí)機(jī),一個(gè)是在初始化的時(shí)候getBean實(shí)例化,第二個(gè)是對(duì)設(shè)置了lazy-init屬性的在第一次向Spring索要Bean的時(shí)候getBean實(shí)例化,最終都是調(diào)用Spring中的getBean方法
MyBatis中設(shè)置Mapper接口的class對(duì)象為MapperFactoryBean,而MapperFactoryBean又是一個(gè)FactoryBean所以在實(shí)例化調(diào)用getBean最終會(huì)調(diào)用MapperFactoryBean中的getObject()方法,將改方法返回的對(duì)象作為實(shí)例化Bean,我們先看下getObeject的調(diào)用鏈(初始化是從refresh中的finishBeanFactoryInitialization開始進(jìn)入preInstantiateSingletons再開始的,需要一層一層看),再看getObject()的方法

public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
這里的getSqlSession()會(huì)得到SqlSessionTemplate,為什么是SqlSessionTemplate,詳見Mybatis源碼解析之SqlSession來自何方
//SqlSessionDaoSupport里的方法,返回的是SqlSessionTemplate
public SqlSession getSqlSession() {
return this.sqlSession;
}
查看SqlSessionTemplate中的getMapper源碼,是從Configuration中g(shù)etMapper
//SqlSessionTemplate中部分方法
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
@Override
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
}
Configuration中獲取Mapper源碼,又是從mapperRegistry獲取的,mapperRegistry相當(dāng)于mapper的注冊(cè)中心,這里能獲取到所有的mapper信息
//Configuration 中g(shù)etMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry中g(shù)etMapper,看到這里答案快浮出水面了,通過MapperProxy的工廠MapperProxyFactory創(chuàng)建代理對(duì)象
//MapperRegistry 中的getMapper
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 {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory的具體實(shí)現(xiàn)源碼
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
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) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
答案至此終于揭開,最終在Spring中這些接口對(duì)應(yīng)的對(duì)象為JDK動(dòng)態(tài)代理生成的Mapper代理類MapperProxy。我們?cè)诔绦蛑心玫降腗apper對(duì)象也是MapperProxy。我們繼續(xù)看下MapperProxy的源代碼
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> 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;
}
}
在我們利用Mapper進(jìn)行數(shù)據(jù)庫的相關(guān)操作時(shí)會(huì)調(diào)用MapperProxy的invoke()方法,該方法先判斷接口方法是否有實(shí)現(xiàn)類,如果有,調(diào)用實(shí)現(xiàn)類的方法,如果沒有(MyBatis的Mapper沒有實(shí)現(xiàn)類)就調(diào)用MapperMethod的execute方法
通過看MapperMethod源碼發(fā)現(xiàn)最終還是調(diào)用sqlSession中的相關(guān)方法,sqlSession再委托給Excutor去執(zhí)行,關(guān)于Excutor菜菜還沒寫相關(guān)文章,請(qǐng)自行搜索
如有錯(cuò)誤,歡迎各位大佬斧正!
[下一篇]:Mybatis源碼解析之SqlSession來自何方