优秀的框架必然有着优秀的工具类,而我们在阅读优秀的框架之前先熟悉这些优秀的工具类会为我们读源码的时候减少不少的障碍,正所谓:磨刀不误砍柴工嘛;而且,当我们掌握了这些优秀的工具类,即使我们不去阅读源码,当我们在实际项目中也可以直接使用这些优秀的框架,拿来主义,该拿的时候我们还得拿。本文介绍下Mybatis中通过反射操作对象的getter/setter方法的四个重要工具类:Reflector、MetaClass、BeanWrapper、MetaObject,这四个工具类可所谓,层层封装,逐渐完美,底层也是大量使用,非常值得学习。
该类中的java doc文档的描述如下:
This class represents a cached set of class definition information thatallows for easy mapping between property names and getter/setter methods.
此类表示一组缓存的类定义信息,允许在属性名称和 getter/setter 方法之间轻松映射
从类的描述中就可以看出Reflector主要帮助我们封装处理好了类定义信息中属性的getter和setter方法,而且是递归获取实现的接口或者继承的父类(排除了Object类)属性的的getter和setter方法,并为我们封装成了MethodInvoker这样一个反射方法执行类,能让我们轻松的获取并执行指定类的相关属性的getter和setter方法,而getter/setter方法是最普通同时也是最常用的方法,值得掌握,一起来看看Reflector的代码吧:
public class Reflector {
private final Class<?> type;
// 所有具有get方法的属性名
private final String[] readablePropertyNames;
// 所有具有set方法的属性名
private final String[] writablePropertyNames;
// 存储属性名和set方法的Invoker的映射
private final Map<String, Invoker> setMethods = new HashMap<>();
// 存储属性名和get方法的Invoker的映射
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;
// 属性值去大写到小驼峰的映射
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
}
如上代码是Reflector类中的属性值,当我们通过new Reflector(Class> clazz) 构造函数进行构造时,内部就会帮我们处理好并赋值上面的属性值,(ps:大家可以自己去看看构造函数中的处理,不难理解的,都是反射相关的处理),需要注意的一点是,当我们的属性不是静态static和final,并且属性名不是以$开头,并且属性名不等于serialVersionUID或class的时候,即使我们没有为这个属性生成getter/setter方法,Reflector也会默认通过反射的方式给我们加上;当我们有了上面的属性值之后,我们就可以很灵活调用相关类中属性的getter/setter方法了,比如我们来看一下很简单demo:
EmployeeInfo的结构如下,并且BaseEmployee值有一个id属性
public class EmployeeInfo extends BaseEmployee implements Serializable
public class MainTest {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
Reflector reflector = new Reflector(EmployeeInfo.class);
Invoker getInvoker = reflector.getGetInvoker("id");
Invoker setInvoker = reflector.getSetInvoker("id");
EmployeeInfo info = new EmployeeInfo();
info.setId("1111");
String invoke = (String)getInvoker.invoke(info, null);
System.out.println(invoke); // 1111
setInvoker.invoke(info, new Object[]{"112121"});
System.out.println((String)getInvoker.invoke(info, null)); // 112121
}
}
MetaClass工具类是基于Reflector的,可以说是Reflector的增强版,所以基本上Reflector所拥有的操作,MetaClass都有,除此之外,MetaClass还扩展了嵌套操作属性的功能:比如获取组合对象中getter/setter的返回类型,以及判断组合对象中属性是否有getter/setter的方法,包括有Reflector生成的:允许我们通过使用 属性名.组合属性名的方式来进行嵌套操作。不知道,通过阅读上面Reflector的介绍和demo你有没有发现一个问题,那就是通过继承的方式我们确实很容易操作超类的属性,那如果是组合的模式的,比如我们操作的结构如下:
public class EmployeeInfo extends BaseEmployee implements Serializable {
private static final long serialVersionUID = -86069724889912552L;
private BaseEmployee baseEmployee;
// ...省略其他属性
}
如果说,此时你要获取EmployeeInfo的属性BaseEmployee的id得类型的话,使用MetaClass就很简单,
MetaClass metaClass = MetaClass.forClass(EmployeeInfo.class, new DefaultReflectorFactory());
Class<?> setterType = metaClass.getSetterType("baseEmployee.id"); // 属性名.组合属性名
boolean hasGetter = metaClass.hasGetter("baseEmployee.id");
System.out.println(setterType.getName()); // java.lang.String
实际上BeanWrapper是ObjectWrapper的一个实现类,我们来看下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);
}
根据规范我们可以发现,其实ObjectWrapper所提供的功能和MetaClass提供的功能差不多,扩展了两个方便我们使用的get/set方法;而对于这个接口的实现,有以下三个实现类:BeanWrapper,MapWrapper,CollectionWrapper,主要也是区分对象的几种形式,我们可以主要看下BeanWrapper的代码结构:
public class BeanWrapper extends BaseWrapper {
private final Object object;
private final MetaClass metaClass;
public BeanWrapper(MetaObject metaObject, Object object) {
super(metaObject);
this.object = object;
this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
}
}
public abstract class BaseWrapper implements ObjectWrapper {
protected static final Object[] NO_ARGUMENTS = new Object[0];
protected final MetaObject metaObject;
protected BaseWrapper(MetaObject metaObject) {
this.metaObject = metaObject;
}
}
我们可以看到,BeanWrapper组合了MetaClass,并且还继承了一个BaseWrapper,而在BaseWrapper中组合了MetaObject对象,这个对象我们会在后面介绍,所以BeanWrapper的功能实现,主要就是依赖于了MetaClass和MetaObject的方法了。虽然BeanWrapper为我们扩展了get/set两个方法,但是我们发现,他两底层都是调用的metaClass.getGetInvoker和metaClass.getSetInvoker的方法,而这也意味着我们只能操作一层属性,不能通过属性名.属性名的形式进行嵌套设置组合属性的值,感觉还是很不方便的,是吧?一个递归就可以解决的难道MyBatis的开发人员没有考虑到吗?我们继续往下看
如果说以上三个工具类是在一层一层的扩展,而且还是存在不足。那么MetaObject就是你最需要的工具类了,为什么这么说呢?因为它整合了上面三个工具类的功能,并还进行了完善,可以嵌套设置和获取属性值。MetaObject的构造结构如下:
public class MetaObject {
private final Object originalObject;
private final ObjectWrapper objectWrapper;
private final ObjectFactory objectFactory;
private final ObjectWrapperFactory objectWrapperFactory;
private final ReflectorFactory 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);
}
}
从构造上看,MetaObject组合了ObjectWrapper,ObjectFactory,ObjectWrapperFactory,ReflectorFactory,有了这几个组件,还有什么事前前面工具类能干的它不能干的呢。另外它提供的getValue/setValue方法,可以进行属性名.属性名的格式来进行嵌套属性获取和设置,简直完美啊,来炫一炫嵌套操作属性吧:
EmployeeInfo info = new EmployeeInfo();
info.setBaseEmployee(new BaseEmployee("12121"));
List<FriendInfo> friendInfos = Arrays.asList(
new FriendInfo("张三"),
new FriendInfo("李四"),
new FriendInfo("王五")
);
info.setFriendInfoList(friendInfos);
MetaObject metaObject = SystemMetaObject.forObject(info);
String name = beanWrapper.getSetterType("friendInfoList[1].friendName").getName();
System.out.println(name);
FriendInfo friendInfo = (FriendInfo) metaObject.getValue("friendInfoList[1]");
System.out.println(friendInfo.toString()); // FriendInfo{friendName='李四'}
String value = (String) metaObject.getValue("friendInfoList[1].friendName");
System.out.println(value); // 李四
metaObject.setValue("friendInfoList[1].friendName","王二麻子");
String value2 = (String) metaObject.getValue("friendInfoList[1].friendName");
System.out.println(value2); // 王二麻子
FriendInfo friendInfo2 = (FriendInfo) metaObject.getValue("friendInfoList[1]");
System.out.println(friendInfo2.toString()); // FriendInfo{friendName='王二麻子'}
当然了MetaObject的构造器参数比较多,而我们多数又都是使用默认的,所以Mybatis为我们提供了一个final的SystemMetaObject工具类,方便我们构造MetaObject,项目中也是推荐大家使用这个呢:
public final class SystemMetaObject {
public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
private SystemMetaObject() {
// Prevent Instantiation of Static Class
}
private static class NullObject {
}
public static MetaObject forObject(Object object) {
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
}
}