1. 程式人生 > >不學無數——Mybatis自動對映器Mapper原理分析

不學無數——Mybatis自動對映器Mapper原理分析

在使用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
[email protected]