不學無數——Mybatis自動對映器Mapper原理分析
阿新 • • 發佈:2018-12-11
在使用MyBatis
時,有時候會想,為什麼只寫一個介面沒有編寫任何的實現類,但是就能返回介面的例項,並且呼叫介面的方法返回資料庫中的資料?此時腦海中浮現了寫動態代理時候的記憶,記得動態代理也是接管了介面,不需要實際的代理角色。然後經過原始碼的Debug發現果然是運用了動態代理的技術。如果對於動態代理技術不熟悉的同學可以看不學無數—動態代理
Mybatis自動對映器Mapper的原始碼分析
首先我們想Debug原始碼就得寫一個測試類如下:
@Autowired private SqlSessionFactory sqlSessionFactory; @Test public void testMybatis(){ SqlSession sqlSession = sqlSessionFactory.openSession(); TBapCheckPtsTranscdMapper mapper = sqlSession.getMapper(TBapCheckPtsTranscdMapper.class); TAmsAcPmtDtlPo tAmsAcPmtDtlPo= new TAmsAcPmtDtlPo(); mapper.queryTransCdByType(tAmsAcPmtDtlPo); }
Mapper
是這樣的
public interface TBapCheckPtsTranscdMapper {
List<Map<String,String>> queryTransCdByType(TAmsAcPmtDtlPo tAmsAcPmtDtlPo);
}
首先先弄明白如何得到的介面的實際物件,由此Debug進去。
TBapCheckPtsTranscdMapper mapper = sqlSession.getMapper(TBapCheckPtsTranscdMapper.class);
然後進行Debug原始碼,發現在MapperProxyFactory
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
然後發現在執行介面的方法的時候進入到了代理MapperProxy
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //諸如hashCode()、toString()、equals()等方法,將target指向當前物件this if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); }
自己寫一個小例子
首先自己定義個實體類
public class User {
private Integer id;
private String name;
private String age;
-----get,set方法
}
然後Mapper介面如下
public interface UserMapper {
public User findUserById(Integer id);
}
代理類如下
public class MapperProxy implements InvocationHandler {
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> clz) {
return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[] { clz }, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
// 諸如hashCode()、toString()、equals()等方法,將target指向當前物件this
return method.invoke(this, args);
} catch (Throwable t) {
}
}
//後面的xml解析之類的就先不模擬,在這直接返回資料
return new User((Integer) args[0], "zhangsan", "18");
}
}
測試如下
public static void main(String[] args) {
ClassLoader classLoader = UserMapper.class.getClassLoader();
MapperProxy mapperProxy = new MapperProxy();
//通過代理生成介面的例項物件
UserMapper mapper = (UserMapper) Proxy.newProxyInstance(classLoader, new Class[]{UserMapper.class},mapperProxy);
User user = mapper.findUserById(10000);
System.out.println("ID:" + user.getId());
System.out.println("Name:" + user.getName());
System.out.println("Age:" + user.getAge());
}
列印如下
ID:10000
Name:zhangsan
Age:18
[email protected]