1. 目的
介绍MyBatis工具类,包括资源文件读取,反射器功能,解析工具类,了解了这些基础工具类,对后面阅读其他源码非常有帮助。
2. 资源文件读取
资源文件读取工具类 位于项目 org.apache.ibatis.io 参考MyBatis源码结构
2.1 Resources 功能介绍
类通过类加载器简化对资源的访问。主要实现读取classpath包下的资源文件
通过搜索Resources被引用的地方,有2个用途:
- 加载资源文件,基本上测试类中都有用到
- 通过类名,加载Class
如图:
2.1.1 Resources 源码解析
/**
* Returns a resource on the classpath as a Reader object
* 将classpath下的资源文件以字符流对象返回
*
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static Reader getResourceAsReader(String resource) throws IOException {
Reader reader;
if (charset == null) {
reader = new InputStreamReader(getResourceAsStream(resource));
} else {
reader = new InputStreamReader(getResourceAsStream(resource), charset);
}
return reader;
}
/**
* Returns a resource on the classpath as a Stream object
* 资源文件以文件流返回
*
* @param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}
/**
* Loads a class
* 加载类
*
* @param className - the class to fetch
* @return The loaded class
* @throws ClassNotFoundException If the class cannot be found (duh!)
*/
public static Class<?> classForName(String className) throws ClassNotFoundException {
return classLoaderWrapper.classForName(className);
}
Resources中实际读取资源文件都是通过 ClassLoaderWrapper 实现
2.1.2 ClassLoaderWrapper 源码解析
/***
* A class to wrap access to multiple class loaders making them work as one
* 多个ClassLoader包装在一起,当做一个使用
* @author Clinton Begin
*/
public class ClassLoaderWrapper {
// 省略部分代码
URL getResourceAsURL(String resource, ClassLoader[] classLoader) {
URL url;
//遍历ClassLoader执行资源文件加载,找到了就直接返回
for (ClassLoader cl : classLoader) {
if (null != cl) {
// look for the resource as passed in...
url = cl.getResource(resource);
// ...but some class loaders want this leading "/", so we'll add it
// and try again if we didn't find the resource
if (null == url) {
url = cl.getResource("/" + resource);
}
// "It's always in the last place I look for it!"
// ... because only an idiot would keep looking for it after finding it, so stop looking already.
if (null != url) {
return url;
}
}
}
// didn't find it anywhere.
return null;
}
/**
* 构造一个classLoader数组
* @param classLoader
* @return
*/
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader,
defaultClassLoader,
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),
systemClassLoader};
}
}
2.2 VFS 功能介绍
在应用服务中提供非常简单API访问资源文件。不同的应用服务器有不同的文件结构,有些特殊的需要进行适配,例如:JBoss6VFS。 主要是实现package这种目录式文件加载 <package name="org.apache.ibatis.builder.mapper"/>
配置文件事例:
<mappers>
<package name="org.apache.ibatis.builder.mapper"/>
</mappers>
VFS 为抽象类,定义了模板方法 List<String> list(URL url, String forPath) 由子类实现, VFS是一个静态单例模式
2.2.1 VFS源码解析
public abstract class VFS {
private static final Log log = LogFactory.getLog(VFS.class);
/** Singleton instance holder. 单例模式 */
private static class VFSHolder {
static final VFS INSTANCE = createVFS();
/**
* 通过静态方法实例化
* @return
*/
@SuppressWarnings("unchecked")
static VFS createVFS() {
// Try the user implementations first, then the built-ins
List<Class<? extends VFS>> impls = new ArrayList<Class<? extends VFS>>();
impls.addAll(USER_IMPLEMENTATIONS);
impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));
// Try each implementation class until a valid one is found
VFS vfs = null;
for (int i = 0; vfs == null || !vfs.isValid(); i++) {
Class<? extends VFS> impl = impls.get(i);
try {
vfs = impl.newInstance();
if (vfs == null || !vfs.isValid()) {
if (log.isDebugEnabled()) {
log.debug("VFS implementation " + impl.getName() +
" is not valid in this environment.");
}
}
} catch (InstantiationException e) {
log.error("Failed to instantiate " + impl, e);
return null;
} catch (IllegalAccessException e) {
log.error("Failed to instantiate " + impl, e);
return null;
}
}
if (log.isDebugEnabled()) {
log.debug("Using VFS adapter " + vfs.getClass().getName());
}
return vfs;
}
}
/**
* 获取VFS单例实例
* Get the singleton {@link VFS} instance. If no {@link VFS} implementation can be found for the
* current environment, then this method returns null.
*/
public static VFS getInstance() {
return VFSHolder.INSTANCE;
}
/** Return true if the {@link VFS} implementation is valid for the current environment.
* 模板方法由子类实现
* */
public abstract boolean isValid();
/**
* Recursively list the full resource path of all the resources that are children of the
* resource identified by a URL.
* 模板方法由子类实现
*
* @param url The URL that identifies the resource to list.
* @param forPath The path to the resource that is identified by the URL. Generally, this is the
* value passed to {@link #getResources(String)} to get the resource URL.
* @return A list containing the names of the child resources.
* @throws IOException If I/O errors occur
*/
protected abstract List<String> list(URL url, String forPath) throws IOException;
/**
* Recursively list the full resource path of all the resources that are children of all the
* resources found at the specified path.
* 递归获取目录下的资源文件
*
* @param path The path of the resource(s) to list.
* @return A list containing the names of the child resources.
* @throws IOException If I/O errors occur
*/
public List<String> list(String path) throws IOException {
List<String> names = new ArrayList<String>();
for (URL url : getResources(path)) {
names.addAll(list(url, path));
}
return names;
}
}
2.3 ResolverUtil 功能介绍
ResoulverUtil 主要用于解析给定package目录下满足特定条件的class,从源码中可以看到,实际是调用VFS.getInstance().list(path) 解析
2.3.1 ResolverUtil 源码解析
/**
* Scans for classes starting at the package provided and descending into subpackages.
* Each class is offered up to the Test as it is discovered, and if the Test returns
* true the class is retained. Accumulated classes can be fetched by calling
* {@link #getClasses()}.
* 从提供的package开始扫描classes,并且递归扫描所有子package。
* 每一个class被发现时都会提供一个Test(验证器),如果验证返回true,这个class会被保存起来。通过通过{@link #getClasses()}获取累计的classes。
*
* @param test an instance of {@link Test} that will be used to filter classes
* @param packageName the name of the package from which to start scanning for
* classes, e.g. {@code net.sourceforge.stripes}
*/
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
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
3. 反射器工具类
反射器功能位于项目 org.apache.ibatis.reflection 参考MyBatis源码结构
3.1 ObjectFactory 功能介绍
MyBatis使用ObjectFactory创建所有需要的新对象。
3.1.1 ObjectFactory 源码解析
JDK 反射构造器的简单封装
/**
* 对象创建工厂,根据Class创建真实对象
* @author Clinton Begin
*/
public class DefaultObjectFactory implements ObjectFactory, Serializable {
private static final long serialVersionUID = -8855120656740914948L;
@Override
public <T> T create(Class<T> type) {
return create(type, null, null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
//解析出Type的具体类型
Class<?> classToCreate = resolveInterface(type);
// we know types are assignable
//实例化class
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
@Override
public void setProperties(Properties properties) {
// no props for default
}
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
//无参构造
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance();
}
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
//含参数构造
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (Exception e) {
//构造异常,输出详细错误参数
StringBuilder argTypes = new StringBuilder();
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
for (Class<?> argType : constructorArgTypes) {
argTypes.append(argType.getSimpleName());
argTypes.append(",");
}
argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
}
StringBuilder argValues = new StringBuilder();
if (constructorArgs != null && !constructorArgs.isEmpty()) {
for (Object argValue : constructorArgs) {
argValues.append(String.valueOf(argValue));
argValues.append(",");
}
argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
}
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}
/**
* 解析对象类型,模板方法子类可以重写
*/
protected Class<?> resolveInterface(Class<?> type) {
Class<?> classToCreate;
if (type == List.class || type == Collection.class || type == Iterable.class) {
classToCreate = ArrayList.class;
} else if (type == Map.class) {
classToCreate = HashMap.class;
} else if (type == SortedSet.class) { // issue #510 Collections Support
classToCreate = TreeSet.class;
} else if (type == Set.class) {
classToCreate = HashSet.class;
} else {
classToCreate = type;
}
return classToCreate;
}
@Override
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
测试用例:
//无参构造
DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory();
Set set = defaultObjectFactory.create(Set.class);
Assert.assertTrue(" set should be HashSet", set instanceof HashSet);
//有参构造
DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory();
TestClass testClass = defaultObjectFactory.create(TestClass.class,
Arrays.<Class<?>>asList(String.class, Integer.class), Arrays.<Object>asList("foo", 0));
你也可以自定义ObjectFactory,通过以下配置使用,ExampleObjectFactory在MyBatis源码包
<configuration>
<objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory">
<property name="objectFactoryProperty" value="100"/>
</objectFactory>
</configuration>
3.2 Reflector 功能介绍
这个类缓存了类定义信息集合,允许在属性名和getter/setter方法之间进行简单的映射
通过调用Reflector解刨对象,将属性和方法全部分离,通过属性名可以找到对应的getter/setter方法
3.2.1 Reflector 源码解析
public class Reflector {
/**对象Class类型*/
private final Class<?> type;
/**可读的属性名数组*/
private final String[] readablePropertyNames;
/**可写的属性名数组*/
private final String[] writeablePropertyNames;
/**对象的所有set方法*/
private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
/**对象的所有get方法*/
private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
/**对象的所有set方法参数类型*/
private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
/**对象的所有get方法返回类型*/
private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
/**对象构造器*/
private Constructor<?> defaultConstructor;
/**忽略大小写的方式存储属性名,key转大写后存储,value为真实属性名,用于快速查找*/
private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();
/***
* #mark 反射构造对象
* @param clazz
*/
public Reflector(Class<?> clazz) {
type = clazz;
//构造方法
addDefaultConstructor(clazz);
//所有get方法
addGetMethods(clazz);
//所有set方法
addSetMethods(clazz);
//成员变量
addFields(clazz);
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
//省略其他代码
}
从类图可以看出DefaultReflectorFactory依赖Reflector,那我们来看看DefaultReflectorFactory具体实现
3.2.2 DefaultReflectorFactory 源码解析
/**
* 反射器工厂
*/
public class DefaultReflectorFactory implements ReflectorFactory {
/**反射器是否需要缓存,默认需要*/
private boolean classCacheEnabled = true;
/**类与反射器对象映射*/
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();
public DefaultReflectorFactory() {
}
@Override
public boolean isClassCacheEnabled() {
return classCacheEnabled;
}
@Override
public void setClassCacheEnabled(boolean classCacheEnabled) {
this.classCacheEnabled = classCacheEnabled;
}
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
//类反射器是否存在,不存在实例化,存在则返回
Reflector cached = reflectorMap.get(type);
if (cached == null) {
cached = new Reflector(type);
reflectorMap.put(type, cached);
}
return cached;
} else {
return new Reflector(type);
}
}
}
测试用例:
@Test
public void testGetSetterType() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
//获取Section反射器对象
Reflector reflector = reflectorFactory.findForClass(Section.class);
//验证属性id的set方法为Long.class类型
Assert.assertEquals(Long.class, reflector.getSetterType("id"));
}
@Test
public void testGetGetterType() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Section.class);
//验证属性id的get方法为Long.class类型
Assert.assertEquals(Long.class, reflector.getGetterType("id"));
}
@Test
public void shouldNotGetClass() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Section.class);
//验证是否存在getClass方法
Assert.assertFalse(reflector.hasGetter("class"));
}
static interface Entity<T> {
T getId();
void setId(T id);
}
static abstract class AbstractEntity implements Entity<Long> {
private Long id;
@Override
public Long getId() {
return id;
}
@Override
public void setId(Long id) {
this.id = id;
}
}
static class Section extends AbstractEntity implements Entity<Long> {
}
更多的调用 查看源码 ReflectorTest
4. 反射器元编程
4.1 MetaObject 功能介绍
元对象,操作对象的对象
为什么会叫做操作对象的对象呢?
MetaObject从字面翻译为元对象。将对象转换为MetaObject对象后,就可以通过MetaObject操作源对象的所有操作。可以理解MetaObject为操作对象的对象。有点类似元编程,关与元编程可以参考知乎回答
4.1.1 MetaObject 源码解析
/**
* 元对象,操作对象的对象
* @author Clinton Begin
*/
public class MetaObject {
/**原始对象*/
private final Object originalObject;
/**对象包装器*/
private final ObjectWrapper objectWrapper;
/**对象工厂*/
private final ObjectFactory objectFactory;
/**对象包装器工厂*/
private final ObjectWrapperFactory objectWrapperFactory;
/**反射器工厂*/
private final ReflectorFactory reflectorFactory;
/**
* 私有的元对象构造方法
* @param object
* @param objectFactory
* @param objectWrapperFactory
* @param reflectorFactory
*/
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
//通过原始对象类型获取对象包装器
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
/**
* 获取对象的元对象
* @param object
* @param objectFactory
* @param objectWrapperFactory
* @param reflectorFactory
* @return
*/
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
//省略部分代码
/**
* 获取属性值
* @param name
* @return
*/
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else {
return objectWrapper.get(prop);
}
}
/**
* 设置属性值
* @param name
* @param value
*/
public void setValue(String name, Object value) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
if (value == null && prop.getChildren() != null) {
// don't instantiate child path if value is null
return;
} else {
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
metaValue.setValue(prop.getChildren(), value);
} else {
objectWrapper.set(prop, value);
}
}
/**
* 获取属性的元对象,因为对象属性也是对象
* @param name
* @return
*/
public MetaObject metaObjectForProperty(String name) {
Object value = getValue(name);
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
测试用例:
/**
* 测试对象
*/
public class RichType {
/** 嵌套属性 */
private RichType richType;
private String richField;
private String richProperty;
private Map richMap = new HashMap();
private List richList = new ArrayList() {
{
add("bar");
}
};
//省略getter/setter
}
/**
* 元对象测试
*/
public class MetaObjectTest {
/**
* 属性赋值和取值
*/
@Test
public void shouldGetAndSetField() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richField", "foo");
assertEquals("foo", meta.getValue("richField"));
}
/**
* 嵌套属性赋值和取值
*/
@Test
public void shouldGetAndSetNestedField() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richType.richField", "foo");
assertEquals("foo", meta.getValue("richType.richField"));
}
/**
* 嵌套对象的Map属性赋值和取值
*/
@Test
public void shouldGetAndSetNestedMapPair() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richType.richMap.key", "foo");
assertEquals("foo", meta.getValue("richType.richMap.key"));
}
/**
* 属性集合赋值和取值
*/
@Test
public void shouldGetAndSetListItem() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richList[0]", "foo");
assertEquals("foo", meta.getValue("richList[0]"));
}
//省略很多代码
}
SystemMetaObject 是MetaObject的工具类,这里就不列出来了。具体功能请下载源码查看
4.1.2 MetaObject 应用
- 设置查询参数值
DefaultParameterHandler 代码
/**
* #mark 设置执行参数
*
* @param ps
*/
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
//参数非输出参数
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
//参数为外部参数
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
//参数默认处理器可以处理
value = parameterObject;
} else {
//获取元数据对象
MetaObject metaObject = configuration.newMetaObject(parameterObject);
//通过对象获值
value = metaObject.getValue(propertyName);
}
//获取参数类型处理器,不同类型参数调用不同setXXX(i,value)
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
- 遍历查询结果集赋值到对象
DefaultResultSetHandler 代码
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) {
// 省略部分代码
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
//通过元对象为属性设置值
metaObject.setValue(property, value);
}
}
4.2 MetaClass 功能介绍
元类,操作类的类。
MetaClass和MetaObject类似,但侧重点是对类的操作,MetaClass封装了Reflector的功能
4.2.1 MetaClass 源码解析
/**
* 元类,操作类的类
* @author Clinton Begin
*/
public class MetaClass {
/**反射器工厂*/
private final ReflectorFactory reflectorFactory;
/**反射器*/
private final Reflector reflector;
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
return new MetaClass(type, reflectorFactory);
}
//省略其他代码
}
测试用例:
/**
* 元类测试
*/
public class MetaClassTest {
@Test
public void shouldTestDataTypeOfGenericMethod() {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
//获取元类对象
MetaClass meta = MetaClass.forClass(GenericConcrete.class, reflectorFactory);
//属性id的返回值为Long类型
assertEquals(Long.class, meta.getGetterType("id"));
assertEquals(Long.class, meta.getSetterType("id"));
}
/**
* 验证所有属性getter返回参数类型
*/
@Test
public void shouldCheckTypeForEachGetter() {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
//验证对象成员变量
assertEquals(String.class, meta.getGetterType("richField"));
//验证对象属性
assertEquals(String.class, meta.getGetterType("richProperty"));
//集合属性
assertEquals(List.class, meta.getGetterType("richList"));
assertEquals(Map.class, meta.getGetterType("richMap"));
assertEquals(List.class, meta.getGetterType("richList[0]"));
assertEquals(RichType.class, meta.getGetterType("richType"));
//验证嵌套属性对象的成员变量
assertEquals(String.class, meta.getGetterType("richType.richField"));
assertEquals(String.class, meta.getGetterType("richType.richProperty"));
assertEquals(List.class, meta.getGetterType("richType.richList"));
assertEquals(Map.class, meta.getGetterType("richType.richMap"));
assertEquals(List.class, meta.getGetterType("richType.richList[0]"));
}
//省略其他代码
}
4.2.2 MetaClass 应用
- 解析配置属性对应对的Java类型
MapperBuilderAssistant 代码
/**
* 解析结果属性对应的Java类型
* @param resultType
* @param property
* @param javaType
* @return
*/
private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
if (javaType == null && property != null) {
try {
MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory());
javaType = metaResultType.getSetterType(property);
} catch (Exception e) {
//ignore, following null check statement will deal with the situation
}
}
if (javaType == null) {
javaType = Object.class;
}
return javaType;
}
/**
* 解析参数对应的Java类型
* @param resultType
* @param property
* @param javaType
* @param jdbcType
* @return
*/
private Class<?> resolveParameterJavaType(Class<?> resultType, String property, Class<?> javaType, JdbcType jdbcType) {
if (javaType == null) {
if (JdbcType.CURSOR.equals(jdbcType)) {
javaType = java.sql.ResultSet.class;
} else if (Map.class.isAssignableFrom(resultType)) {
javaType = Object.class;
} else {
MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory());
javaType = metaResultType.getGetterType(property);
}
}
if (javaType == null) {
javaType = Object.class;
}
return javaType;
}
- 辅助构造查询结果集的真实对象。
DefaultResultSetHandler 代码
//#mark 创建结果对象,结果集对应Java实体对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
//真实对象类型
final Class<?> resultType = resultMap.getType();
//获取反射器元数据类
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
//是否有类型处理器
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
//使用构造方法参数,构建结果对象
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
//接口或有默认构造
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
5. 解析工具类
解析工具类 位于项目 org.apache.ibatis.parsing
5.1 GenericTokenParser 功能介绍
通用的占位符解析器,基于GenericTokenParser可以实现SQL占位符#{}、${} 参数的替换
SqlSourceBuilder 代码实例
/**
* #mark 解析出真实的SQL,将id=#{id} 转换成 id=?
* @param originalSql
* @param parameterType
* @param additionalParameters
* @return
*/
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql = parser.parse(originalSql);
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
5.1.1 GenericTokenParser 源码解析
/**
* #mark 通用的占位符解析
* @author Clinton Begin
*/
public class GenericTokenParser {
/**占位符起始符号 如: ${ */
private final String openToken;
/**占位符结束符号 如: } */
private final String closeToken;
/**占位符处理器*/
private final TokenHandler handler;
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
/**
* 解析
* @param text
* @return
*/
public String parse(String text) {
//省略功能实现
}
}
测试用例:
/**
* 通用的占位符解析测试
*/
public class GenericTokenParserTest {
/**
* 占位符处理器,实现简单的从Map中获取值
*/
public static class VariableTokenHandler implements TokenHandler {
private Map<String, String> variables = new HashMap<String, String>();
/**
* 处理器初始化,传入map变量
* @param variables
*/
public VariableTokenHandler(Map<String, String> variables) {
this.variables = variables;
}
/**
* 占位符处理
* @param content
* @return
*/
@Override
public String handleToken(String content) {
//返回map值
return variables.get(content);
}
}
@Test
public void shouldDemonstrateGenericTokenReplacement() {
GenericTokenParser parser = new GenericTokenParser("${", "}", new VariableTokenHandler(new HashMap<String, String>() {
{
//初始化占位符数据字典
put("first_name", "James");
put("initial", "T");
put("last_name", "Kirk");
put("var{with}brace", "Hiya");
put("", "");
}
}));
assertEquals("James T Kirk reporting.", parser.parse("${first_name} ${initial} ${last_name} reporting."));
}
//省略其他代码
}
5.2 PropertyParser 功能介绍
解析配置文件 ${} 变量
我们常使用这种占位符方式配置数据库连接池
5.2.1 PropertyParser 源码解析
/**
* 解析配置文件 ${} 变量
*
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class PropertyParser {
private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
/**
* 特别的属性key,用于是否显示默认值的占位符
* @since 3.4.2
*/
public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";
/**
* #mark 特别的属性key,用于分割 key 和 默认值的占位符
* @since 3.4.2
*/
public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";
/**
* 是否开启默认值
*/
private static final String ENABLE_DEFAULT_VALUE = "false";
/**
* 默认值分隔符
*/
private static final String DEFAULT_VALUE_SEPARATOR = ":";
private PropertyParser() {
// Prevent Instantiation
}
/**
* 解析
* @param string 解析的字符串
* @param variables
* @return
*/
public static String parse(String string, Properties variables) {
VariableTokenHandler handler = new VariableTokenHandler(variables);
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
return parser.parse(string);
}
/**
* 占位符替换处理器
*/
private static class VariableTokenHandler implements TokenHandler {
private final Properties variables;
/**是否开启默认值*/
private final boolean enableDefaultValue;
/**默认分隔符*/
private final String defaultValueSeparator;
private VariableTokenHandler(Properties variables) {
this.variables = variables;
this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
}
private String getPropertyValue(String key, String defaultValue) {
return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
}
@Override
public String handleToken(String content) {
if (variables != null) {
String key = content;
//开启默认值
if (enableDefaultValue) {
final int separatorIndex = content.indexOf(defaultValueSeparator);
String defaultValue = null;
//分隔符存在
if (separatorIndex >= 0) {
//截取分隔符前面的key
key = content.substring(0, separatorIndex);
//截取默认值
defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
}
//存在默认值
if (defaultValue != null) {
//获取properties对应的值,值不存在则返回默认值
return variables.getProperty(key, defaultValue);
}
}
//未开启默认值
if (variables.containsKey(key)) {
return variables.getProperty(key);
}
}
//以上都未匹配返回
return "${" + content + "}";
}
}
}
测试用例
配置文件:
<configuration>
<settings>
<setting name="jdbcTypeForNull" value="${settings:jdbcTypeForNull?:NULL}"/>
</settings>
<objectFactory type="org.apache.ibatis.submitted.global_variables_defaults.SupportClasses$CustomObjectFactory">
<property name="name" value="${objectFactory:name?:default}"/>
</objectFactory>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:${db:name?:global_variables_defaults}" />
<property name="username" value="sa" />
</dataSource>
</environment>
</environments>
<databaseIdProvider type="DB_VENDOR">
<property name="${productName:hsql?:HSQL Database Engine}" value="hsql"/>
</databaseIdProvider>
</configuration>
代码:
public class CustomizationTest {
/**
* 注解式的Mapper对象
*/
@CacheNamespace(implementation = SupportClasses.CustomCache.class, properties = {
@Property(name = "name", value = "${cache:name?:default}")
})
private interface CustomDefaultValueSeparatorMapper {
@Select("SELECT '${val != null ? val : 'default'}' FROM INFORMATION_SCHEMA.SYSTEM_USERS")
String selectValue(@Param("val") String val);
}
@Test
public void applyDefaultValueWhenCustomizeDefaultValueSeparator() throws IOException {
//定义properties对象值
Properties props = new Properties();
//开启默认值配置
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
//分隔符使用 ?:
props.setProperty(PropertyParser.KEY_DEFAULT_VALUE_SEPARATOR, "?:");
Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/global_variables_defaults/mybatis-config-custom-separator.xml");
//实例化SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
Configuration configuration = factory.getConfiguration();
//添加Mapper对象
configuration.addMapper(CustomDefaultValueSeparatorMapper.class);
//测试自定义缓存对象
SupportClasses.CustomCache cache = SupportClasses.Utils.unwrap(configuration.getCache(CustomDefaultValueSeparatorMapper.class.getName()));
//验证配置解析结果
Assertions.assertThat(configuration.getJdbcTypeForNull()).isEqualTo(JdbcType.NULL);
Assertions.assertThat(((UnpooledDataSource) configuration.getEnvironment().getDataSource()).getUrl())
.isEqualTo("jdbc:hsqldb:mem:global_variables_defaults");
Assertions.assertThat(configuration.getDatabaseId()).isEqualTo("hsql");
Assertions.assertThat(((SupportClasses.CustomObjectFactory) configuration.getObjectFactory()).getProperties().getProperty("name"))
.isEqualTo("default");
Assertions.assertThat(cache.getName()).isEqualTo("default");
//开启SqlSession
SqlSession sqlSession = factory.openSession();
try {
CustomDefaultValueSeparatorMapper mapper = sqlSession.getMapper(CustomDefaultValueSeparatorMapper.class);
//执行selectValue
Assertions.assertThat(mapper.selectValue(null)).isEqualTo("default");
} finally {
sqlSession.close();
}
}
}
5.3 XPathParser 功能介绍
XPath形式XML解析器 XPathParser 包揽了MyBatis所有的XML文件解析
2个重要的配置文件解析入口,代码实例:
/**
* #mark 配置解析入口 #20170821
* http://www.mybatis.org/mybatis-3/zh/configuration.html
* @return
*/
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 解析 mapper
* http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html
*/
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
5.3.1 XPathParser 源码解析
/**
* XPath形式XML解析器
* @author Clinton Begin
*/
public class XPathParser {
/** dom 对象 */
private final Document document;
/** 是否验证配置文件 */
private boolean validation;
/** 实体解析器,包含XML语法定义。具体查看 {@link org.apache.ibatis.builder.xml.XMLMapperEntityResolver} */
private EntityResolver entityResolver;
/** properties 变量 */
private Properties variables;
private XPath xpath;
p/**
* 构造器
* @param reader
* @param validation
* @param variables
* @param entityResolver
*/
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
/**
* 解析XML节点集合
* @param expression
* @return
*/
public List<XNode> evalNodes(String expression) {
return evalNodes(document, expression);
}
public List<XNode> evalNodes(Object root, String expression) {
List<XNode> xnodes = new ArrayList<XNode>();
NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
xnodes.add(new XNode(this, nodes.item(i), variables));
}
return xnodes;
}
/**
* 解析XML节点
* @param expression
* @return
*/
public XNode evalNode(String expression) {
return evalNode(document, expression);
}
/**
* 创建Document
* @param inputSource
* @return
*/
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
//设置解析器
builder.setEntityResolver(entityResolver);
//XML文件验证错误处理
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
//XML 配置错误
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
//省略其他代码
}
测试用例
配置文件:
<employee id="${id_var}">
<blah something="that"/>
<first_name>Jim</first_name>
<last_name>Smith</last_name>
<birth_date>
<year>1970</year>
<month>6</month>
<day>15</day>
</birth_date>
<height units="ft">5.8</height>
<weight units="lbs">200</weight>
<active>true</active>
</employee>
测试代码:
public class XPathParserTest {
@Test
public void shouldTestXPathParserMethods() throws Exception {
String resource = "resources/nodelet_test.xml";
//读取资源文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//实例XPathParser对象
XPathParser parser = new XPathParser(inputStream, false, null, null);
//获取 <employee><birth_date><year> 节点值
assertEquals((Long)1970l, parser.evalLong("/employee/birth_date/year"));
//获取 <employee id> id属性值
assertEquals("${id_var}", parser.evalString("/employee/@id"));
// 省略其他代码
inputStream.close();
}
}
参考资料
- MyBatis官方文档 - http://www.mybatis.org/mybatis-3/zh/index.html
- MyBatis-Spring官方文档 - http://www.mybatis.org/spring/zh/index.html
- MyBatis源码 - https://gitee.com/rainwen/mybatis
- MyBatis-Spring源码 - https://github.com/rainwen/spring
关于MyBatis源码解读之工具类就介绍到这里。如有疑问,欢迎留言,谢谢。