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 {

...
  protected T newInstance(MapperProxy mapperProxy) {
    return (T)Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy(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 newInstance(Class 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执行过程中详细介绍。



你可能感兴趣的:(Mybatis)