1. 程式人生 > >Mybatis的Mapper底層原理

Mybatis的Mapper底層原理

總的來說是通過動態代理。動態代理的功能就是通過攔截器方法回撥(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執行過程中詳細介紹。