Mybatis技術內幕(2.3.4):反射模組-ObjectFactory

失控的阿甘發表於2019-03-19

基於Mybatis-3.5.0版本

1.0 ObjectFactory物件工廠

org.apache.ibatis.reflection.factory.ObjectFactoryMybatis中很多模組都會使用到ObjectFactory介面,該介面提供了多個create()方法的過載,通過這些create()方法可以建立指定型別的物件。程式碼和類圖如下:

Mybatis技術內幕(2.3.4):反射模組-ObjectFactory

/**
 * 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);
}
複製程式碼

2.0 DefaultObjectFactory

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);
	}
}
複製程式碼

3.0 mybatis-config 配置objectFactory

以下內容摘錄自 Mybatis官網中文文件

MyBatis 每次建立結果物件的新例項時,它都會使用一個物件工廠(ObjectFactory)例項來完成。 預設的物件工廠需要做的僅僅是例項化目標類,要麼通過預設構造方法,要麼在引數對映存在的時候通過引數構造方法來例項化。 如果想覆蓋物件工廠的預設行為,則可以通過建立自己的物件工廠來實現。比如:

// 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 方法。

4.0 總結

本節內容比較簡單主要就是利用反射去建立物件。但是有個小的知識點大家可以學習下

4.1 List toArray(T[] a)

在Mybatis程式碼中有很多List轉陣列轉的地方,以DefaultObjectFactory程式碼為例:

type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
複製程式碼

像阿甘最開始的時候也沒有太注意這一塊,經常是直接給了一個長度為0的陣列,發現也可以正常工作。如:

list.toArray(new String[0]);
複製程式碼

在實際的轉換中除了指定陣列型別,最好能加上陣列長度。

為什麼呢? 可以先看下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操作。對效能的提升顯而易見啦

失控的阿甘,樂於分享,記錄點滴

相關文章