ibatis2.3源码之Accessplan&Exchange&Mapping
Accessplan包
UML:
接口和实现类的组织结构如下(很典型的3层,1层Interface,2层abstract,3层实现class:
Accessplan是用来保存JavaBean对象的Method[]、成员变量类型Class[]的工具类。
protected Class clazz;
protected String[] propertyNames;
protected ClassInfo info;
Accessplan对外只提供个Factory,这种“封闭”设计可以借鉴:
对外接口调用如下:
parameterPlan = AccessPlanFactory.getAccessPlan(parameterMap.getParameterClass(), parameterPropNames);
其中parameterMap.getParameterClass(),是需要映射的CLASS,就是XML里parameterXXX里的类,后面那个是类的成员变量名,其中ParameterMapping是映射元素类(在Mapping模块详细介绍),如下:
// 从某个映射对象中取出所有元素
ParameterMapping[] parameterMappings = parameterMap.getParameterMappings();
String[] parameterPropNames = new String[parameterMappings.length];
for (int i = 0; i < parameterPropNames.length; i++) {
// 从元素中取出被映射对象的成员名
parameterPropNames[i] = parameterMappings[i].getPropertyName();
}
AccessPlanFactory取出的是AccessPlan接口,抽象类为:BaseAccessPlan,总计有4种实现类:ComplexAccessPlan、EnhancedPropertyAccessPlan、MapAccessPlan、PropertyAccessPlan。
其中用到ClassInfo这个工具类(如果没有缓存的话,就为每个Class配属都new一个ClassInfo工具类),另外接口为:ClassInfo.getInstance(clazz),其中clazz就是parameterMap.getParameterClass()传递过来的。
如下:
public static ClassInfo getInstance(Class clazz) {
if (cacheEnabled) {//这里的ClassInfo是可缓存的(缓存到一个static synchronizedMap里),并不是每次都申请新的ClassInfo对象
synchronized (clazz) {
ClassInfo cache = (ClassInfo) CLASS_INFO_MAP.get(clazz);
if (cache == null) {
cache = new ClassInfo(clazz);
CLASS_INFO_MAP.put(clazz, cache);
}
return cache;
}
} else {
return new ClassInfo(clazz);
}
}
ClassInfo的作用是加载clazz类(POJO)的set*或get*或is方法及方法的参数(Class[])对象存储到Map里。为POJO的后续处理做准备。(看到其中取set*方法对象判断语句为:if (name.startsWith("set") && name.length() > 3&&methods[i].getParameterTypes().length == 1),记得一定要name.length()>3,并这里参数为1)
另外 name = null;// help GC
PropertyAccessPlan(for working with beans)和MapAccessPlan(for working with Maps):
上面BaseAccessPlan实现了获取set,get方法以及她们的参数类型。那么剩下工作就需要赋值了。AccessPlan的实现类各自实现setProperties和getProperties。
其中setProperties可借鉴下:
public void setProperties(Object object, Object[] values) {
int i = 0;
try {
Object[] arg = new Object[1];
for (i = 0; i < propertyNames.length; i++) {
arg[0] = values[i];
try {
setters[i].invoke(object, arg);
} catch (Throwable t) {
throw ClassInfo.unwrapThrowable(t);
}
}
} catch (Throwable t) {
throw new RuntimeException("Error setting property '" + setters[i].getName() + "' of '" +
object + "'. Cause: " + t, t);
}
}
public Object[] getProperties(Object object) {
int i = 0;
Object[] values = new Object[propertyNames.length];
try {
for (i = 0; i < propertyNames.length; i++) {
try {
values[i] = getters[i].invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ClassInfo.unwrapThrowable(t);
}
}
} catch (Throwable t) {
throw new RuntimeException("Error getting property '" + getters[i].getName() + "' of '" + object + "'. Cause: " + t, t);
}
return values;
}
注意到所有的AccessPlan都没有包含POJO的实例,包含的是POJO的某些Method[]和Class[],为什么呢?
整个AccessPlan类似一个很大的工具类,用于给POJO进行赋值和取值操作。
Exchange Package包
UML
dataExchange用于数据交换的工具类,主要工作是对象的获取和赋值,结构很简约,而涉及的对象有Primitive,POJO,List,MAP,DOM,复杂对象。
public void initialize(Map properties);
public Object[] getData(RequestScope request, ParameterMap parameterMap(对象描述类), Object parameterObject(对象实例));
public Object setData(RequestScope request, ResultMap resultMap(描述类), Object resultObject(对象实例), Object[] values(需要赋的值));
这里参数有 parameterMap和parameterObject是为了“定制对象”,简单说就是parameterMap描述了需要获取和赋值的成员变量名,并不是parameterObject所有的成员我都需要获取,从而实现定制,比如ibatis的sqlXML写成如下形式:
<insert id="test" parameterClass="com.company.cjcj.User"> insert into t_user values(#id#,#name#) </insert>
这里只需要获取User这个POJO的id和name就够了。
基本实现思路:
getData()
public Object[] getData(RequestScope request, ParameterMap parameterMap, Object parameterObject) {
// 获取参数实例需要获取的成员变量名
Object result[]=new Object[变量个数];
for(int i=0;i<个数;i++{
// 从parameterObject中取出相应的值
}
}
setData()
public Object setData(RequestScope request, ParameterMap parameterMap, Object parameterObject, Object[] values) {
// 获取需要赋值的变量名
for(int i=0;i<个数;i++){
// 对需要复制的每个变量进行赋值values[i]
}
}
实现类:
PrimitiveDataExchange(如果是单一对象)
public Object[] getData(RequestScope request, ParameterMap parameterMap, Object parameterObject),很简单把parameterObject直接赋值给Object[]
JavaBeanDataExchange(如果是POJO对象)
借助AccessPlan工具类,进行赋值和取值操作,利用反射知识匹配方法名获取、赋值对象成员变量,详情见AccessPlan实现。
ListDataExchange(如果是List)
直接调用List的get和set方法。
MapDataExchange(如果是Map)
直接调用Map的get和put方法。
DomDataExchange(如果是Document)
ibatis为处理dom对象实现了DomProbe类(commons.beans包)来处理,利用的是w3c.dom的包来处理dom对象,从而实现取值和赋值操作。
ComplexDataExchange(复杂对象)