.....................................................未完结,个人笔记,查资料、解惑者勿看...........................................................................
由于想弄懂一些事情,所以不得不翻出源码看看,又估计会比较麻烦,所以边找边记录下来。
先贴一段mybatis查询的一段代码,如下:
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
roleMapper.findRoles("role");
事实上能看出来用到了动态代理技术,但是并没有具体的实现类,mybatis是怎么让代理类对象执行相关的SQL呢?
这个是我想要知道的东西。
上面sqlSession是DefaultSqlSession类的实例,即sqlSession.getMapper(RoleMapper.class)调用的是DefaultSqlSession类中定义的方法,如下:
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
传入的是RoleMapper.class,接下来调用了Configuration类的getMapper(type, this)方法,定义如下:
configuration.getMapper(RoleMapper.class, sqlSession);
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
调用了MapperRegistry类的getMapper方法,定义如下:
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);
}
}
终于看到不太一样的方法了,在该方法里首先通过get(type)获取MapperProxyFactory实例,并判断其是否为空,
为空,则抛出异常;不为空,调用其newInstance(sqlSession)方法作为返回值。(这应该是判断type有没有被注册)
到了这里,我们陆续传进来的两个参数都被使用了。首先看看MapperProxyFactory实例是如何产生的。
private final Map, MapperProxyFactory>> knownMappers =
new HashMap, MapperProxyFactory>>();
有点头绪了吧,knownMappers是一个map,key是诸如RoleMapper.class的类对象,value是MapperProxyFactory实例。
很显然,这是在mybatis初始化时做的操作,具体的不谈,直接找出MapperProxyFactory存的是什么东东。
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
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.");
}
}
}
}
}
上面传入的parent是
1.首先判断是
2.三个getStringAttribute分别是三种注册mapper的方式;
3.接下来一长串的if语句,说明了三种注册mapper的方式只能存在一个;
我使用resource引入的,故只分析下面的一段代码。(resource是RoleMapper.xml文件的全路径名)
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);//加载Mapper的resource文件
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
到了这里,首先将保存SQL语句的xml文件作为输入流new了一个XMLMapperBuilder对象,然后执行其parse方法。
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
1.如果configuration没有加载文件,则解析RoleMapper.xml文件,configurationElement(parser.evalNode("/mapper")),
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
看到了什么?这不是对xml文件的配置吗?namespace, cache, cache-ref, parameterMap, resultMap, sql
及select|insert|update|delete子节点全都有对应操作,忍不住追溯源代码...
真是相当麻烦的,好了,这部分先过去,还是先看看MapperProxyFactory存的是什么东东
2.然后资源添加到configuration的Set
3.绑定Mapper的命名空间 ,bindMapperForNamespace();
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
直接看addMapper方法,发现其调用了MapperRegistry类的该方法
public void addMapper(Class 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(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
终于看到knownMappers.put(type, new MapperProxyFactory
ε=(´ο`*)))唉,忙活半天结果它是new出来的
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
回过头来
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);
}
}
这里得到的mapperProxyFactory的mapperInterface属性值是RoleMapper.class
调用newInstance(sqlSession)
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
到这里看到动态代理了,至于返回的代理对象如何能执行SQL还没弄清楚,
但是可以猜测,上面的methodCache,以及SQL语句的配置还没具体看,可能是和这两个部分有关,
额,以后有时间再补充o(╯□╰)o.............................