基於Mybatis-3.5.0版本java
org.apache.ibatis.reflection.factory.ObjectFactory
Mybatis中不少模塊都會使用到ObjectFactory接口,該接口提供了多個create()方法的重載,經過這些create()方法能夠建立指定類型的對象。代碼和類圖以下:apache
/** * MyBatis uses an ObjectFactory to create all needed new Objects. * MyBatis使用ObjectFactory建立全部須要的新對象 * @author Clinton Begin */
public interface ObjectFactory {
/** * Sets configuration properties. * 設置配置信息 * @param properties configuration properties */
void setProperties(Properties properties);
/** * Creates a new object with default constructor. * 經過無參構造器建立指定類的對象 * @param type Object type * @return */
<T> T create(Class<T> type);
/** * Creates a new object with the specified constructor and params. * 根據參數列表,從指定類型中選擇合適的構造器建立對象 * @param type Object type * @param constructorArgTypes Constructor argument types * @param constructorArgs Constructor argument values * @return */
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
/** * Returns true if this object can have a set of other objects. It's main * purpose is to support non-java.util.Collection objects like Scala * collections. * 檢測指定類型是否爲集合類型,主要處理java.util.Collection及其子類 * @param type Object type * @return whether it is a collection or not * @since 3.1.0 */
<T> boolean isCollection(Class<T> type);
}
複製代碼
org.apache.ibatis.reflection.factory.DefaultObjectFactory
是ObjectFactory的默認實現,代碼比較簡單看下注釋就懂了,以下:數組
/** * @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) {
Class<?> classToCreate = resolveInterface(type);
// we know types are assignable
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
@Override
public void setProperties(Properties properties) {
// no props for default
}
/** * 根據參數列表,從指定類型中選擇合適的構造器建立對象 * @param type 建立類的類型 * @param constructorArgTypes 指定構造器的參數類型列表 * @param constructorArgs 構造器參數列表 * @return */
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
// 沒有constructorArgTypes和constructorArgs 則用默認無參構造器建立對象
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
try {
return constructor.newInstance();
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance();
} else {
throw e;
}
}
}
// 根據指定構造器參數類型列表 找到指定構造器 建立對應對象
constructor = type
.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
try {
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} else {
throw e;
}
}
} 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);
}
}
/** * 對於建立經常使用的集合接口,返回指定的默認實現 * List、Collection、Iterable 返回ArrayList * Map 返回HashMap * SortedSet 返回TreeSet * Set 返回HashSet * @param type * @return */
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;
}
/** * 判斷類是否爲集合類 * isAssignableFrom Reflector類裏面有介紹啦,主要針對Class爲主體的判斷 */
@Override
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
複製代碼
如下內容摘錄自 Mybatis官網中文文檔bash
MyBatis 每次建立結果對象的新實例時,它都會使用一個對象工廠(ObjectFactory)實例來完成。 默認的對象工廠須要作的僅僅是實例化目標類,要麼經過默認構造方法,要麼在參數映射存在的時候經過參數構造方法來實例化。 若是想覆蓋對象工廠的默認行爲,則能夠經過建立本身的對象工廠來實現。好比:mybatis
// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
public Object create(Class type) {
return super.create(type);
}
public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}
public void setProperties(Properties properties) {
super.setProperties(properties);
}
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
複製代碼
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
複製代碼
ObjectFactory 接口很簡單,它包含兩個建立用的方法,一個是處理默認構造方法的,另一個是處理帶參數的構造方法的。 最後,setProperties方法能夠被用來配置 ObjectFactory,在初始化你的ObjectFactory實例後,objectFactory元素體中定義的屬性會被傳遞給 setProperties 方法。app
本節內容比較簡單主要就是利用反射去建立對象。可是有個小的知識點你們能夠學習下ide
在Mybatis代碼中有不少List轉數組轉的地方,以DefaultObjectFactory代碼爲例:性能
type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
複製代碼
像阿甘最開始的時候也沒有太注意這一塊,常常是直接給了一個長度爲0的數組,發現也能夠正常工做。如:學習
list.toArray(new String[0]);
複製代碼
在實際的轉換中除了指定數組類型,最好能加上數組長度。ui
爲何呢? 能夠先看下ArrayList的實現
// ArrayList.java
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
//Arrays.java
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
複製代碼
能夠看到當初始化數組指定的長度小於要轉化的List的長度,則會利用反射建立一個新數組,再進行System.arraycopy操做,而指定了長度的初始化數組則會直接進行System.arraycopy操做。對性能的提高顯而易見啦
失控的阿甘,樂於分享,記錄點滴