Mybatis中mapper是怎么和XMl关联起来的
从源码来分析,通过Mybatis的都知道,必须指定nameSpace为Mapper的全限定类名。这样就能关联起来。Mapper的实现肯定是动态dialing,在InvocationHandler
中做增强。这里就来分析分析具体是怎么做的? 分析的时候设计的东西多,容易走偏。我尽量回归主题。
1. XML文件解析
这里的xml解析比较繁琐,如果逐行来分析的话,很多很多,这里就挑主线来分析了。之后会分块来分话题来做分析。
解析总的配置文件
如果从经典的Mybatis创建SqlSessionFactory
开始,那肯定能看到下面的代码
代码里面的有的注释,是我看源码的时候写的,有的写的比较离谱。有的记录我之前看的时候的困惑。之后看的时候又看懂了。所以就保留在这里了。
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties")); //解析properties标签,并把他放在 parser和config的 Variables 里面
Properties settings = settingsAsProperties(root.evalNode("settings"));//加载setting标签
loadCustomVfs(settings); //lcnote 这里的vfs是啥?怎么用 我知道这个
//我现在知道他的vfs是什么了,vfs(virtual file system)他抽象出了几个api,通过这些api就可以访问文件系统上的资源;比如在
// 在解析 mapperElement(root.evalNode("mappers"));的时候,如果指定package,就可以通过VFS来获取包路径下面所有的class文件。
// 并且会将他添加到mappe里面,和spring中的classPathSacnner一样差不多,可以指定过滤器。
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
//从这里开始,都是解析具体的标签,new出对象,将标签下面的属性设置进去,
// 从解析的这里基本也能看出mybatis里面重要的几个点,首先是objectFactory,objectFactory。objectFactory,plugins
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectFactory"));
reflectorFactoryElement(root.evalNode("objectFactory"));
//这里就具体设置setting标签了
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//lcnote 这里是重点,解析mapper文件,
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
复制代码
直接看是怎么解析mapper文件的。
从这个代码里面可以看到,mappers
标签下面是可以写两种标签。package
和mapper
标签。对于两种有不同的解析方法。
解析package标签
这里只是截取了部分的源码。还会将好几个源码都拼接在一块,便于看
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
}
//下面是 configuration.addMappers(mapperPackage)的方法
public void addMappers(String packageName) {
//mapperRegistry是一个注册mapper的注册器,并且里面维护了很多的所有的mapper组成的对象。
mapperRegistry.addMappers(packageName);
}
//下面是mapperRegistry.addMappers(packageName);
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
// addMappers(packageName, Object.class); 方法,
//packageName表示要扫描的包的路径
//superType表示要找的类是这个类的子类。
public void addMappers(String packageName, Class<?> superType)
{
//resolverUtil就是一个在指定包下,找指定的类的子类集合的一个工具类。
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
//找到了合适的Class,将他添加到mapper里面。
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
//resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
//扫描给定包下(包括子路径下面的所有的类。调用Test方法来匹配,匹配到的class调用getClasses就可以获取的到。)
public ResolverUtil<T> find(Test test, String packageName) {
String path = getPackagePath(packageName);
try {
List<String> children = VFS.getInstance().list(path);
for (String child : children) {
if (child.endsWith(".class")) {
//加载class对象,调用ResolverUtil里面的静态内部类IsA(实现了Test接口)做匹配。
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
//addIfMatching(test, child);
protected void addIfMatching(Test test, String fqn) {
try {
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
ClassLoader loader = getClassLoader();
if (log.isDebugEnabled()) {
log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
}
Class<?> type = loader.loadClass(externalName);
if (test.matches(type)) {
matches.add((Class<T>) type);
}
} catch (Throwable t) {
log.warn("Could not examine class '" + fqn + "'" + " due to a "
+ t.getClass().getName() + " with message: " + t.getMessage());
}
}
//****************************重点***********************************
// public <T> void addMapper(Class<T> type) 方法,将上面找的,合适的class实例化之后要加载到mapperRegistry里面去。
// 并且这个方法是mapperRegistry里面的。
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {//mapper只能注册一次
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 用mapper new出MapperProxyFactory,放在knownMappers里面
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is adde