字數:2434,閱讀耗時:3分40秒。
老規矩,先上案例代碼,這樣你們能夠更加熟悉是如何使用的,看過Mybatis系列的小夥伴,對這段代碼差很少均可以背下來了。java
哈哈~,有點誇張嗎?不誇張的,就這行代碼。mysql
public class MybatisApplication {
public static final String URL = "jdbc:mysql://localhost:3306/mblog";
public static final String USER = "root";
public static final String PASSWORD = "123456";
public static void main(String[] args) {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
SqlSession sqlSession = null;
try {
inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
//今天主要這行代碼
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println(userMapper.selectById(1));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
sqlSession.close();
}
}
看源碼有什麼用?
sql
經過源碼的學習,咱們能夠收穫Mybatis的核心思想和框架設計,另外還能夠收穫設計模式的應用。
前兩篇文章咱們已經Mybatis配置文件解析到獲取SqlSession,下面咱們來分析從SqlSession到userMapper:設計模式
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
前面那篇文章已經知道了這裏的sqlSession使用的是默認實現類DefaultSqlSession。因此咱們直接進入DefaultSqlSession的getMapper方法。session
//DefaultSqlSession中
private final Configuration configuration;
//type=UserMapper.class
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
這裏有三個問題:mybatis
上面能夠看出,getMapper方法調用的是Configuration中的getMapper方法。而後咱們進入Configuration中app
//Configuration中
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
////type=UserMapper.class
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
這裏也沒作什麼,繼續調用MapperRegistry中的getMapper:框架
//MapperRegistry中
public class MapperRegistry {
//主要是存放配置信息
private final Configuration config;
//MapperProxyFactory 的映射
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
//得到 Mapper Proxy 對象
//type=UserMapper.class,session爲當前會話
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//這裏是get,那就有add或者put
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);
}
}
//解析配置文件的時候就會調用這個方法,
//type=UserMapper.class
public <T> void addMapper(Class<T> type) {
// 判斷 type 必須是接口,也就是說 Mapper 接口。
if (type.isInterface()) {
//已經添加過,則拋出 BindingException 異常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//添加到 knownMappers 中
knownMappers.put(type, new MapperProxyFactory<>(type));
//建立 MapperAnnotationBuilder 對象,解析 Mapper 的註解配置
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
//標記加載完成
loadCompleted = true;
} finally {
//若加載未完成,從 knownMappers 中移除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
MapperProxyFactory對象裏保存了mapper接口的class對象,就是一個普通的類,沒有什麼邏輯。ide
在MapperProxyFactory類中使用了兩種設計模式:學習
單例模式methodCache(註冊式單例模式)。
工廠模式getMapper()。
繼續看MapperProxyFactory中的newInstance方法。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public T newInstance(SqlSession sqlSession) {
//建立MapperProxy對象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
//最終以JDK動態代理建立對象並返回
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
}
從代碼中能夠看出,依然是穩穩的基於 JDK Proxy 實現的,而 InvocationHandler 參數是 MapperProxy 對象。
//UserMapper 的類加載器
//接口是UserMapper
//h是mapperProxy對象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
}
上面調用newInstance方法時候建立了MapperProxy對象,而且是當作newProxyInstance的第三個參數,因此MapperProxy類確定實現了InvocationHandler。
進入MapperProxy類中:
//果真實現了InvocationHandler接口
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;
}
//調用userMapper.selectById()實質上是調用這個invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//若是是Object的方法toString()、hashCode()等方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
//JDK8之後的接口默認實現方法
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//建立MapperMethod對象
final MapperMethod mapperMethod = cachedMapperMethod(method);
//下一篇再聊
return mapperMethod.execute(sqlSession, args);
}
}
也就是說,getMapper方法返回的是一個JDK動態代理對象(類型是$Proxy+數字)。這個代理對象會繼承Proxy類,實現被代理的接口UserMpper,裏面持有了一個MapperProxy類型的觸發管理類。
當咱們調用UserMpper的方法時候,實質上調用的是MapperProxy的invoke方法。
userMapper=$Proxy6@2355。
爲何要在MapperRegistry中保存一個工廠類?
原來他是用來建立並返回代理類的。這裏是代理模式的一個很是經典的應用。
MapperProxy如何實現對接口的代理?
咱們知道,JDK動態代理有三個核心角色:
被代理類必須實現接口,由於要經過接口獲取方法,並且代理類也要實現這個接口。
而Mybatis中並無Mapper接口的實現類,怎麼被代理呢?它忽略了實現類,直接對Mapper接口進行代理。
在Mybatis中,JDK動態代理爲何不須要實現類呢?
這裏咱們的目的其實就是根據一個能夠執行的方法,直接找到Mapper.xml中statement ID ,方便調用。
最後返回的userMapper就是MapperProxyFactory的建立的代理對象,而後這個對象中包含了MapperProxy對象,
最後咱們調用userMapper.selectUserById(),本質上調用的是MapperProxy的invoke()方法。
請看下面這張圖:
若是根據(接口+方法名找到Statement ID ),這個邏輯在InvocationHandler子類(MapperProxy類)中就能夠完成了,其實也就沒有必要在用實現類了。
本文中主要是講getMapper方法,該方法實質上是獲取一個JDK動態代理對象(類型是Proxy+數字),這個代理類會繼承MapperProxy類,實現被代理的接口UserMapper,而且裏面持有一個MapperProxy類型的觸發管理類。這裏咱們就拿到代理類了,後面咱們就可使用這個代理對象進行方法調用。
問題涉及到的設計模式:
代理模式。
工廠模式。
單例模式。
整個流程圖:
冰凍三尺,非一日之寒表面意義是冰凍了三尺,並非一天的寒冷所能達到的效果。學習亦如此,你每一天的一點點努力,都是爲你之後的成功作鋪墊。