當前位置:編程學習大全網 - 源碼下載 - 如何通過接口查找對應的mapper.xml及方

如何通過接口查找對應的mapper.xml及方

在使用mybatis的時候,有壹種方式是

BookMapper bookMapper = SqlSession().getMapper(BookMapper.class)

獲取接口,然後調用接口的方法。只要方法名和對應的mapper.xml中的id名字相同,就可以執行sql。

那麽接口是如何與mapper.xml對應的呢?

首先看下,在getMapper()方法是如何操作的。

在DefaultSqlSession.java中調用了configuration.getMapper()

public <T> T getMapper(Class<T> type) {

return configuration.<T>getMapper(type, this);

}

在Configuration.java中調用了mapperRegistry.getMapper(type, sqlSession);

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {

return mapperRegistry.getMapper(type, sqlSession);

}

下面重點來了,在MapperRegistry.java中實現了動態代理

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {

final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);

if (mapperProxyFactory == null)

throw new BindingException("Type " + type + " is not known to the MapperRegistry.");

try {

return mapperProxyFactory.newInstance(sqlSession);

} catch (Exception e) {

throw new BindingException("Error getting mapper instance. Cause: " + e, e);

}

}

這個函數分兩部分來看,首先是從map集合中獲取接口代理,map集合的來源,第二部分獲取代理後實例化,獲取接口的方法,執行sql。

對於第壹部分:集合的來源。

這個MapperRegistry.java中有個方法是addMappers();***有兩個重載。

public void addMappers(String packageName, Class<?> superType) {

ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();

//通過包名,查找該包下所有的接口進行遍歷,放入集合中

resolverUtil.find(new ResolverUtil.IsA(superType), packageName);

Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();

for (Class<?> mapperClass : mapperSet) {

addMapper(mapperClass);

}

}

//解析包名下的接口

public void addMappers(String packageName) {

addMappers(packageName, Object.class);

}

往上追溯該方法的調用是在SqlSessionFactory.build();時對配置文件的解析,其中對節點mappers的解析,這裏先不贅述,

mapperElement(root.evalNode("mappers"));

private void mapperElement(XNode parent) throws Exception {

if (parent != null) {

for (XNode child : parent.getChildren()) {

//使用package節點進行解析配置

if ("package".equals(child.getName())) {

String mapperPackage = child.getStringAttribute("name");

//註冊包下的接口

configuration.addMappers(mapperPackage);

} else {

//使用mapper節點

String resource = child.getStringAttribute("resource");

String url = child.getStringAttribute("url");

String mapperClass = child.getStringAttribute("class");

if (resource != null && url == null && mapperClass == null) {

ErrorContext.instance().resource(resource);

InputStream inputStream = Resources.getResourceAsStream(resource);

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());

mapperParser.parse();

} else if (resource == null && url != null && mapperClass == null) {

ErrorContext.instance().resource(url);

InputStream inputStream = Resources.getUrlAsStream(url);

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());

mapperParser.parse();

} else if (resource == null && url == null && mapperClass != null) {

Class<?> mapperInterface = Resources.classForName(mapperClass);

configuration.addMapper(mapperInterface);

} else {

throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");

}

}

}

}

}

這是調用addMapper()的順序。

同時在改方法中還有壹個方法很重要

public <T> void addMapper(Class<T> type) {

if (type.isInterface()) {

if (hasMapper(type)) {

throw new BindingException("Type " + type + " is already known to the MapperRegistry.");

}

boolean loadCompleted = false;

try {

knownMappers.put(type, new MapperProxyFactory<T>(type));

//根據接口名尋找同包下同名的xml或者mapper的namespace是該接口的xml

//找到對用的xml後進行解析mapper節點裏面的節點

MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);

parser.parse();

loadCompleted = true;

} finally {

if (!loadCompleted) {

knownMappers.remove(type);

}

}

}

}

這是通過接口的全路徑來查找對應的xml。這裏有兩種方式解析,也就是我們平常xml文件放置位置的兩種寫法。

第壹種是不加namespace,把xml文件放在和接口相同的路徑下,同時xml的名字與接口名字相同,如接口名為Student.java,xml文件為Student.xml。在相同的包下。這種當時可以不加namespace.

第二種是加namespace,通過namespace來查找對應的xml.

到這就是接口名和xml的全部註冊流程。

下面再說下第二部分就是通過動態代理獲取接口名字來對應xml中的id。

主要有兩個類MapperProxyFactory.java和MapperProxy.java

對於MapperProxyFactory.java

public class MapperProxyFactory<T> {

private final Class<T> mapperInterface;

private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

//構造函數,獲取接口類

public MapperProxyFactory(Class<T> mapperInterface) {

this.mapperInterface = mapperInterface;

}

public Class<T> getMapperInterface() {

return mapperInterface;

}

public Map<Method, MapperMethod> getMethodCache() {

return methodCache;

}

@SuppressWarnings("unchecked")

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, mapperInterface, methodCache);

return newInstance(mapperProxy);

}

}

在MapperProxy.java中進行方法的執行

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method);

//方法的執行

return mapperMethod.execute(sqlSession, args);

}

private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod;

}

至此,就是mybatis所有接口和xml的加載,以及通過動態代理來進行接口的執行的過程。

  • 上一篇:求JSP代碼實現翻頁功能(最好是品紅項目的源代碼)
  • 下一篇:linux和Ubuntu有什麽區別
  • copyright 2024編程學習大全網