反射包解析
概述
mybatis反射包,提供了反射相關的工具,爲啥要提供這些工具呢,java不是已經提供反射的功能了嗎?
java反射雖然已經很強大,但是api偏底層,要想使用好可能會需要大量重複代碼,可能使用不當也會產生性能問題,因此mybatis提供反射包提煉、封裝、增強反射api,讓上層應用用的更舒心。
包內容:
接下來會挑重點的類講解下原理。
Invoker
invoker單獨有一個子包,概念比較獨立,也比較簡單,代表執行器。包裝了Method、Field的執行
Invoker定義:
public interface Invoker {
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
Class<?> getType();
}
主要定義了一個invoke方法,和Method.invoke幾乎一樣,這裏做了抽象主要是把Field給包了進來。
GetFieldInvoker的invoke實現爲 field.get(target,arg)
SetFieldInvoker的invoke實現爲 field.set(target,arg)
factory
這裏的工廠子包只包含了ObjectFactory和其實現類DefaultObjectFactory。
public interface ObjectFactory {
default void setProperties(Properties properties) {
// NOP
}
<T> T create(Class<T> type);
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
<T> boolean isCollection(Class<T> type);
}
就是通用的對象創建工廠,DefaultObjectFactory就是用構造函數去實例化對象,比較簡單。
property
這個子包主要提供對類屬性的反射解析工具。重點是PropertyTokenizer,意思是屬性分隔解析器。
解析格式例如:order[0].item[name].firstName這樣的表達式,有點類似json表達式,mybatis通過此類提供了表達式解析的能力,在處理嵌套屬性時更加方便和通用。
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
/**
* 當前屬性
*/
private String name;
/**
* 當前完整分詞
*/
private final String indexedName;
/**
* 索引值
* 如果是數組[0],則是0
* 如果是map[key],則是key、
*/
private String index;
/**
* 剩餘字符串
*/
private final String children;
/**
* 構造函數內部就完成解析
*/
public PropertyTokenizer(String fullname) {
int delim = fullname.indexOf('.');
if (delim > -1) {
name = fullname.substring(0, delim);
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
indexedName = name;
delim = name.indexOf('[');
if (delim > -1) {
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
@Override
public boolean hasNext() {
return children != null;
}
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
}
PropertyTokenizer使用了迭代器模式實現了遞歸解析子串,挺巧妙,之後有類似遞歸的需求可以借鑑一下,代碼簡單清晰。
數據:order[0].item[name].firstName
解析結果:
name=orde
indexedName=order[0]
index=0
children=item[name].firstName
結果簡單清晰明瞭,有相關場景可以直接使用。
Reflector
Reflector反射器,保存類反射元數據
public class Reflector {
/**
* 對應的類
*/
private final Class<?> type;
/**
* 可讀的屬性名集合
*/
private final String[] readablePropertyNames;
/**
* 可寫的屬性名集合
*/
private final String[] writablePropertyNames;
/**
* 所有set方法,
*/
private final Map<String, Invoker> setMethods = new HashMap<>();
/**
* 所有get方法
*/
private final Map<String, Invoker> getMethods = new HashMap<>();
/**
* set方法的入參類型
*/
private final Map<String, Class<?>> setTypes = new HashMap<>();
/**
* get方法的返回類型
*/
private final Map<String, Class<?>> getTypes = new HashMap<>();
/**
* 默認構造函數
*/
private Constructor<?> defaultConstructor;
/**
* 不區分大小寫的屬性集合
* key 屬性名全大寫, value 屬性名
*/
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
相關屬性都是在構造函數中通過Class對象加工獲取的,基本包含了操作類的元數據。額外提供了不區分大小寫的屬性集合,爲了屬性查找、下劃線轉駝峯提供支持。
總結就是Class類增強,提供對property的一些簡便操作方法。
ReflectorFactory
Reflector工廠,就是控制Reflector的創建,目的是爲了緩存Reflector,Reflector雖好用,但是其創建過程比較重,使用了java的反射api,如果對同一class頻繁創建性能會被影響,且反射元數據也不會更改,緩存其是必須的。
DefaultReflectorFactory直接使用HashMap緩存Reflector,key是Class,value是Reflector。
MetaClass
MetaClass又是Reflector的增強類,包含了Reflector和ReflectorFactory,又額外提供了對屬性表達式的解析。
public class MetaClass {
private final ReflectorFactory reflectorFactory;
private final Reflector reflector;
}
上層應用使用MetaClass操作類,MetaClass內部使用ReflectorFactory緩存。
提供一些property相關的方法,實現委託給Reflector完成。
涉及屬性表達式解析則交給PropertyTokenizer。
Wapper
wapper包,放着ObjectWapper及其實現類。對象包裝器,在一個具體對象實例上添加反射功能,上面的Reflector、MetaClass都是在對靜態Class各種操作,ObjectWrapper是真正有個具體的對象實例,可以執行反射操作了。
public interface ObjectWrapper {
Object get(PropertyTokenizer prop);
void set(PropertyTokenizer prop, Object value);
String findProperty(String name, boolean useCamelCaseMapping);
String[] getGetterNames();
String[] getSetterNames();
Class<?> getSetterType(String name);
Class<?> getGetterType(String name);
boolean hasSetter(String name);
boolean hasGetter(String name);
MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
boolean isCollection();
void add(Object element);
<E> void addAll(List<E> element);
單看提供的方法比較簡單,屬性動態設置/獲取,屬性查找、getter/setter方法操作。
BaseWrapper
提供了集合的通用方法,屬性轉換集合、獲取/設置集合中的值。
BeanWrapper
普通Bean包裝器
public class BeanWrapper extends BaseWrapper {
private final Object object;
private final MetaClass metaClass;
}
具體對象object、MetaClass包裝,MetaClass的具象化。
藉助MetaClass實現ObjectWapper定義的方法,比較簡單。
@Override
public Object get(PropertyTokenizer prop) {
// index不爲空代表是集合類型
if (prop.getIndex() != null) {
// 解析集合的值
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
return getBeanProperty(prop, object);
}
}
get和set方法都有對集合類型的處理。
MetaObject
對象元數據,對ObjectWapper的進一步增強,表達式解析反射完全支持。
最主要兩個方法getValue、setValue
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
// 如果有子串遞歸解析
if (prop.hasNext()) {
// 獲取當前層值的MetaObject
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
// 遞歸解析子串的值
return metaValue.getValue(prop.getChildren());
}
} else {
return objectWrapper.get(prop);
}
}
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) {
// 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);
}
}
在複雜對象時可以方便的通過表達式,反射設置、獲取值。
總結
反射包重點在於Reflector、MetaClass、ObjectWapper、MetaObject的設計,職責清晰一層一層加強,遵守單一原則,在我們日常業務實現設計時值得借鑑。