不学无数—Mybatis自动映射器Mapper原理分析

Mybatis自动映射器Mapper原理分析

Posted by 不学无数 on September 19, 2018

在使用MyBatis时,有时候会想,为什么只写一个接口没有编写任何的实现类,但是就能返回接口的实例,并且调用接口的方法返回数据库中的数据?此时脑海中浮现了写动态代理时候的记忆,记得动态代理也是接管了接口,不需要实际的代理角色。然后经过源码的Debug发现果然是运用了动态代理的技术。如果对于动态代理技术不熟悉的同学可以看不学无数—动态代理

Mybatis自动映射器Mapper的源码分析

首先我们想Debug源码就得写一个测试类如下:

@Autowired
private SqlSessionFactory sqlSessionFactory;

@Test
public void testMybatis(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    TBapCheckPtsTranscdMapper mapper = sqlSession.getMapper(TBapCheckPtsTranscdMapper.class);
    TAmsAcPmtDtlPo tAmsAcPmtDtlPo= new TAmsAcPmtDtlPo();
    mapper.queryTransCdByType(tAmsAcPmtDtlPo);
}

Mapper是这样的

public interface TBapCheckPtsTranscdMapper {

	List<Map<String,String>> queryTransCdByType(TAmsAcPmtDtlPo tAmsAcPmtDtlPo);

}

首先先弄明白如何得到的接口的实际对象,由此Debug进去。

TBapCheckPtsTranscdMapper mapper = sqlSession.getMapper(TBapCheckPtsTranscdMapper.class);

然后进行Debug源码,发现在MapperProxyFactory中,返回了代理对象。

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

然后发现在执行接口的方法的时候进入到了代理MapperProxy

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
  //诸如hashCode()、toString()、equals()等方法,将target指向当前对象this
  if (Object.class.equals(method.getDeclaringClass())) {
    return method.invoke(this, args);
  } else if (isDefaultMethod(method)) {
    return invokeDefaultMethod(proxy, method, args);
  }
} catch (Throwable t) {
  throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}

自己写一个小例子

首先自己定义个实体类

public class User {
    private Integer id;
    private String name;
    private String age;
    -----get,set方法
}

然后Mapper接口如下

public interface UserMapper {
    public User findUserById(Integer id);
}

代理类如下

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) {
			}
		}
		//后面的xml解析之类的就先不模拟,在这直接返回数据
		return new User((Integer) args[0], "zhangsan", "18");
	}
}

测试如下

public static void main(String[] args) {
    ClassLoader classLoader = UserMapper.class.getClassLoader();
    MapperProxy mapperProxy = new MapperProxy();
    //通过代理生成接口的实例对象
    UserMapper mapper = (UserMapper) Proxy.newProxyInstance(classLoader, new Class[]{UserMapper.class},mapperProxy);
    User user = mapper.findUserById(10000);
    System.out.println("ID:" + user.getId());
    System.out.println("Name:" + user.getName());
    System.out.println("Age:" + user.getAge());
}

打印如下

ID:10000
Name:zhangsan
Age:18
Practice.Day09.MapperProxy@24d46ca6