Mybatis的Mapper底層原理
阿新 • • 發佈:2019-02-10
總的來說是通過動態代理。動態代理的功能就是通過攔截器方法回撥(invokeHandler),達到增強目標物件的目的。看下面程式碼,很關鍵一點就是InvocationHandler包含target物件。在invoke方法中會呼叫target的方法。
public class HelloWordProxy extends InvokeHandler{ // 真正的本體 private Object target; public Object bind(Object target) { this.target= target; return Proxy.newInstance(taget.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Overrride public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("before execute real target method"); result = method.invoke(target, args); System.out.println("after execute real target method"); return result; } }
在Mybatis裡面,會定義一個MapperProxy(實現InvocationHandler),先看MapperProxyFactory:
public class MapperProxyFactory<T> { ... 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, mapperInteface, methodCache); return newInstance(mapperProxy); } }
重點關注上面的public方法,可以看出MapperProxy是用sqlSession建立的,並且Proxy.newInstance()方法的第三個引數就是這個MapperProxy物件本身(這個符合動態代理的基本建立方法)。
再看MapperProxy的執行程式碼:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; 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) { } } MapperMethod mapperMethod = cachedMapperMethod(method); return mepperMethod.execute(sqlSession, args); } }
method.invoke(this, args) 第一個引數往往是target, 而這裡的target就是MapperProxy自己。所裡這裡起到的並不是增強target的功能,而是“取代”target的功能。而事實上,在Mybatis裡面,從來就沒出現target,target只是個佔位符。
還要注意:最後mapperMethod.execute(sqlSesison, args)也很有意思,他是後序執行sql語句的入口,它會呼叫sqlSession的各種資料庫操作方法,而這些方法就會去呼叫sqlSession的四大元件。這個會在後序的sql執行過程中詳細介紹。