介紹MyBatis工具類,包括資源文件讀取,反射器功能,解析工具類,瞭解了這些基礎工具類,對後面閱讀其餘源碼很是有幫助。html
資源文件讀取工具類 位於項目 org.apache.ibatis.io 參考MyBatis源碼結構java
類經過類加載器簡化對資源的訪問。主要實現讀取classpath包下的資源文件node
經過搜索Resources被引用的地方,有2個用途:git
如圖: github
/** * 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 實現spring
/*** * 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}; } }
在應用服務中提供很是簡單API訪問資源文件。不一樣的應用服務器有不一樣的文件結構,有些特殊的須要進行適配,例如:JBoss6VFS。 主要是實現package這種目錄式文件加載 <package name="org.apache.ibatis.builder.mapper"/>sql
配置文件事例:數據庫
<mappers> <package name="org.apache.ibatis.builder.mapper"/> </mappers>
VFS 爲抽象類,定義了模板方法 List<String> list(URL url, String forPath) 由子類實現, VFS是一個靜態單例模式express
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; } }
ResoulverUtil 主要用於解析給定package目錄下知足特定條件的class,從源碼中能夠看到,實際是調用VFS.getInstance().list(path) 解析apache
/** * 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; }
反射器功能位於項目 org.apache.ibatis.reflection 參考MyBatis源碼結構
MyBatis使用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>
這個類緩存了類定義信息集合,容許在屬性名和getter/setter方法之間進行簡單的映射
經過調用Reflector解刨對象,將屬性和方法所有分離,經過屬性名能夠找到對應的getter/setter方法
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具體實現
/** * 反射器工廠 */ 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
元對象,操做對象的對象
爲何會叫作操做對象的對象呢?
MetaObject從字面翻譯爲元對象。將對象轉換爲MetaObject對象後,就能夠經過MetaObject操做源對象的全部操做。能夠理解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的工具類,這裏就不列出來了。具體功能請下載源碼查看
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); } }
元類,操做類的類。
MetaClass和MetaObject相似,但側重點是對類的操做,MetaClass封裝了Reflector的功能
/** * 元類,操做類的類 * @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]")); } //省略其餘代碼 }
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); }
解析工具類 位於項目 org.apache.ibatis.parsing
通用的佔位符解析器,基於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()); }
/** * #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.")); } //省略其餘代碼 }
解析配置文件 ${} 變量
咱們常使用這種佔位符方式配置數據庫鏈接池
/** * 解析配置文件 ${} 變量 * * @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(); } } }
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(); }
/** * 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源碼解讀之工具類就介紹到這裏。若有疑問,歡迎留言,謝謝。