摘自《Mybatis從入門到精通》(劉增輝)
通過上面的學習,大家可能會有一個疑問,爲什麼 Mapper 接口沒有實現類卻能被正常調用呢?
這是因爲 MyBaits 在 Mapper 接口上使用了動態代理的一種非常規的用法,熟悉這種動態代理的用法不僅有利於理解 MyBatis 接口和 XML 的關係,還能開闊思路 。 接下來提取出這種動態代理的主要思路,用代碼來爲大家說明。
假設有一個如下的 Mapper 接口。
public interface UserMapper {
List<SysUser> selectAll();
}
這裏使用 Java 動態代理方式創建一個代理類,代碼如下 。
public class MyMapperProxy<T> implements InvocationHandler {
private Class<T> mapperInterface;
private SqlSession sqlSession;
public MyMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
this.mapperInterface = mapperInterface;
this.sqlSession = sqlSession;
}
@Override
public Object invoke(Object proxy , Method method , Object[] args)
throws Throwable {
//針對不同的 sql 類型,需要調用sqlSession不同的方法
//接口方法中的參數也有很多情況 ,這裏只考慮沒有有參數的情況
List<T> list= sqlSession.selectList(
mapperInterface.getCanonicalName() + ”.” + method.getName());
//返回位也有很多情況,這裏不做處理直接返回
return list;
}
}
測試代碼如下:
//獲取sqlSession
SqlSession sqlSession = getSqlSession();
//獲取 UserMapper 接口
MyMapperProxy userMapperProxy = new MyMapperProxy(
UserMapper.class , sqlSession) ;
UserMapper userMapper = (UserMapper) Proxy.newProxyinstance (
Thread.currentThread().getContextClassLoader(),
new Class[ ] {UserMapper.class},
userMapperProxy) ;
//調 用 selectAll 方 法
List<SysUser> user= userMapper.selectAll();
從代理類中可以看到,當調用一個接口的方法時,會先通過接口的全限定名稱和當前調用的方法名的組合得到一個方法 id,這個 id 的值就是映射 XML 中 namespace 和具體方法 id 的組合。所以可以在代理方法中使用 sqlSession 以命名空間的方式調用方法。通過這種方式可以將接口和 XML 文件中的方法關聯起來。這種代理方式和常規代理的不同之處在於,這裏沒有對某個具體類進行代理,而是通過代理轉化成了對其他代碼的調用。
由於方法參數和返回值存在很多種情況,因此 MyBatis 的內部實現會比上面的邏輯複雜得多,正是因爲 MyBatis 對接口動態代理的實現,我們在使用接口方式的時候纔會如此容易。