MyBatis
作为一款 ORM
框架,主要通过 XML
定义 Object
,这就难免用到反射,虽然 JDK
自带的反射已经方便使用,但是 MyBatis
依然结合业务功能,将反射功能封装成一个更加易用的包,这个包就在 reflection
中。
在解析 MyBatis
的反色包之前,我们需要先通过需要看看 MyBatis
需要通过反射实现哪些功能。
在 MyBatis
中,可以方便的获取数组属性和对象属性。比如: student.names[0].firstName
因此, MyBatis
将反射获取值分为了两类:
POJO
容器类( Collection
)通过下标进行取值,而 POJO
则是通过 getter()/setter()
进行取值
一个 POJO
对象拥有两类属性,对象属性以及类信息,在 MyBatis
中分别通过通过 MetaObject
和 MetaClass
对应上述信息。
明白了上述信息,就能理解下面 MyBatis
反射包中几个关键类的作用:
MetaClass
: 保存了 POJO
的类相关信息,比如拥有的方法 hasGetter()/hasSetter()
MetaObject
: 保存了 POJO
对象的相关的信息,比如通过 getter()
获取值,通过 setter()
设置值 ObjectWrapper
: 用来区别不同的 POJO
获取属性的不同的方式,比如数组通过索引获取: nums[index]
, Map
通过 key
获取, POJO
通过 getter()
获取 Relector
: 这个类便是 MyBatis
的反射底层类,它简单的封装了 JDK
底层的反射,其他类都是调用此类进行反射操作。
接下来开始分析 MyBatis
的源码。
在 relection
包中,包含有 factory,invoker,property,wrapper
4个包,
factory
: 工厂类,主要用来通过反射创建类 invoker
: 对 POJO
的各种类的封装以及缓存,方便直接调用 property
: 设置/获取 POJO
属性的帮助类 wrapper
: POJO
的包装类,只要需要操作 POJO
的属性,即可通过此类包装后调用
在 MyBatis
中,主要通过 reflection.SystemMetaObject
作为简单工厂使用:
public final class SystemMetaObject { // 默认创建POJO的工厂类 public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory(); // 创建BeanWrapper的工厂类 public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory(); // null 对应的Object包装类 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 {} //简单工厂方法 用来创建MetaObject public static MetaObject forObject(Object object) { return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); } }
可以看到,这里是直接返回的 MetaObject
。
MetaObject
主要有如下方法:
getSetterNames()
: 获取该 class
所有的 setter name
getGetterNames()
: 获取该 class
所有的 getter name
hasGetter()
: 此 class
是否有该属性的 getter()
hasSetter()
: 此 class
是否有该属性的 setter()
getValue()
: 获取对应属性对应的值 setValue()
: 设置此属性为对应的值
从这里可以看出来, MetaObject
基本包含了 MyBatis
需要使用的所有功能,可以说通过此类,基本连接了 XML
与 POJO
。
在 MetaObject
代码中,基本上都是调用的 objectWrapper
的方法,而 MetaObject
主要作用在于根据类型创建对应的 ObjectWrapper
,类似简单工厂,其主要逻辑在构造方法:
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) { this.originalObject = object; this.objectFactory = objectFactory; this.objectWrapperFactory = objectWrapperFactory; this.reflectorFactory = reflectorFactory; //如果原本的对象本身就是ObjectWrapper ,那么直接赋值 if (object instanceof ObjectWrapper) { this.objectWrapper = (ObjectWrapper) object; } //如果wrapper工厂能够包装此`object`,则直接交给工厂处理 else if (objectWrapperFactory.hasWrapperFor(object)) { this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object); } //如果对象是Map 则直接使用`MapWrapper`处理 else if (object instanceof Map) { this.objectWrapper = new MapWrapper(this, (Map) object); } //如果对象属于collection,则使用`CollectionWrapper`处理 else if (object instanceof Collection) { this.objectWrapper = new CollectionWrapper(this, (Collection) object); } //否则,使用`BeanWrapper`包装此对象 else { this.objectWrapper = new BeanWrapper(this, object); } }
这里有个不合理的地方,查看 CollectionWrapper
的源码可以看到, CollectionWrapper
中全是抛出异常。也就是如果传入的是 List
之类的对象,调用任何方法都会抛出异常。
结合 MyBatis
整个源码可以发现, MyBatis
会处理 List
, Array
等,然后将其包装到 StrictMap
中:
private Object wrapCollection(final Object object) { if (object instanceof Collection) { StrictMap<Object> map = new StrictMap<>(); map.put("collection", object); if (object instanceof List) { map.put("list", object); } return map; } else if (object != null && object.getClass().isArray()) { StrictMap<Object> map = new StrictMap<>(); map.put("array", object); return map; } return object; }
而 Map
中会有逻辑处理 Collection
在 MetaObject
中还有一个重要逻辑,前面说过,可能会解析类似 student.names[0].firstName
的属性,这样就涉及到一个循环处理,并且第一个是 POJO
,第二个是集合。
在 MyBatis
中,通过递归调用来循环解决:
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 { //递归调用`MetaObject#getValue() return metaValue.getValue(prop.getChildren()); } } else { //通过`ObjectWrapper`获取属性 return objectWrapper.get(prop); } }
其实这样写倒比较难看懂,也不知道为什么不直接使用 foreach
,而且 PropertyTokenizer
也实现了 Iterator
接口
getValue
之所以要放在这一层,便是因为能处理 student.names[0].firstName
,每一层递归都会新建一个 MetaObject
对象,然后根据子对象的类型再次通过对应类型的 ObjectWrapper
去获取值,直到获取到最终类型的值。
看完了 MetaObject
,接下来看 MetaObject
中所包装的 ObjectWrapper
ObjectWrapper
在 MyBatis
的实现主要分两类: BeanWrapper
和 MapWrapper
其中这两个 Wrapper
都继承自 BaseWrapper
, BaseWrapper
中包含了一些通用的方法:
resolveCollection()
: 通过 key
或者 getter()
获取 collection
getCollectionValue()
: 通过下标,获取数组或容器的指定下标的值 setCollectionValue()
: 通过下标,设置数组或者容器的值
ObjectWrapper
接口所拥有的方法和 MetaObject
的方法差不多,只不过其实现类分几种。
@Override public Object get(PropertyTokenizer prop) { //判断表达式中是否包含类似list[1]符号 if (prop.getIndex() != null) { //如果有,则先从Map中获取此集合 Object collection = resolveCollection(prop, map); //从集合中获取指定下标的元素 return getCollectionValue(prop, collection); } else { //否则,通过使用map的key获取参数 return map.get(prop.getName()); } }
@Override public Object get(PropertyTokenizer prop) { //判断表达式中是否包含类似list[1]符号 if (prop.getIndex() != null) { //如果有,则先从现在的POJO中获取此集合 Object collection = resolveCollection(prop, object); return getCollectionValue(prop, collection); } else { //否则,通过`getter`获取属性的值 return getBeanProperty(prop, object); } }
接下来看通过 getBeanProperty()
获取属性的具体方法:
private Object getBeanProperty(PropertyTokenizer prop, Object object) { //获取对应方法的Invoker Invoker method = metaClass.getGetInvoker(prop.getName()); //通过Invoker调用对应的方法 return method.invoke(object, NO_ARGUMENTS); }
可以看到, BeanWrapper
底层有一部分是通过 MetaClass
获取的信息
接下来看 MetaCalss
的方法: MetaClass
可以类比 Java
中的 Class
,主要保存了 Class
的各种信息,按道理来说应该做一个缓存将 MetaClass
缓存起来,但是在 MyBatis
中缓存的是 Refector
,差别不大、
MetaClass
主要包含以下方法:
findProperty()
: 查找属性,主要用于查找映射数据库的字段和 POJO
的属性 getSetterType()
: 获取对应 setter
需要的字段类型 getGetterType()
: 获取对应 getter
返回的字段类型 hasSetter()
: 是否存在对应属性的 setter
hasGetter()
: 是否存在对应属性的 getter
get*Invoker()
: 获取对应方法的 Invoker
,获取后可以直接调用
可以看到,都是涉及到 class
的一些属性。
而 MetaClass
主要功能为分析 PropertyTokenizer
然后底层调用 Reflector
。
比如:
public boolean hasSetter(String name) { PropertyTokenizer prop = new PropertyTokenizer(name); //判断是否有子节点需要解析 if (prop.hasNext()) { //首先判断本身是否有getter,只有本身存在getter,才能获取到子节点 if (reflector.hasSetter(prop.getName())) { //创建子节点`MetaClass` MetaClass metaProp = metaClassForProperty(prop.getName()); //递归处理子节点 return metaProp.hasSetter(prop.getChildren()); } else { return false; } } else { //通过reflector获取setter return reflector.hasSetter(prop.getName()); } }
Reflector
主要通过简单工厂创建:
@Override public Reflector findForClass(Class<?> type) { if (classCacheEnabled) { // synchronized (type) removed see issue #461 return reflectorMap.computeIfAbsent(type, Reflector::new); } else { return new Reflector(type); } }
主要是为了能够缓存已经解析的信息
Reflector
主要成员变量如下:
//类型信息 private final Class<?> type; //可读的属性(有getter) private final String[] readablePropertyNames; //可写的属性(有setter) private final String[] writablePropertyNames; //set对应的Invoker private final Map<String, Invoker> setMethods = new HashMap<>(); //get对应的Invoker private final Map<String, Invoker> getMethods = new HashMap<>(); //属性setter需要的类型 private final Map<String, Class<?>> setTypes = new HashMap<>(); //属性getter返回的类型 private final Map<String, Class<?>> getTypes = new HashMap<>(); //默认构造方法 private Constructor<?> defaultConstructor;
对于 Reflector
,主要便是通过反射处理对应的 Class
,然后将获取的属性填充到上面的成员变量中。方法不一一分析,这里举例分析一个:
private Method[] getClassMethods(Class<?> clazz) { Map<String, Method> uniqueMethods = new HashMap<>(); Class<?> currentClass = clazz; //循环处理当前类,然后处理其父类,不处理`Object` while (currentClass != null && currentClass != Object.class) { //首先将当前类的所有方法加入到容器中 //方法中主要为Method生成一个唯一的签名 addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); //将当前类对应实现的接口添加到容器中 Class<?>[] interfaces = currentClass.getInterfaces(); for (Class<?> anInterface : interfaces) { addUniqueMethods(uniqueMethods, anInterface.getMethods()); } //处理父类 currentClass = currentClass.getSuperclass(); } Collection<Method> methods = uniqueMethods.values(); return methods.toArray(new Method[0]); }
这个方法是比较简单的方法,但是 Reflector
的逻辑远远不是这么简单,这其中会涉及到很多细节的语法处理,比如在子类重写父类方法中,返回类型可能不同,这个时候应该以子类的为准等等。。
到这里, MyBatis
的反射类基本分析完毕,其实看起来这么复杂,但是只要理解了 MyBatis
对反射的需求,看起来就不难明白了。
在整个解析过程中,起着最大的作用的类是: PropertyTokenizer
public class PropertyTokenizer implements Iterator<PropertyTokenizer> { //以下属性以`student.names[0].firstName` 为例 //student private String name; //names private final String indexedName; //0 private String index; //names 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); } } }
这个类就是分析 student.names[0].firstName
这个取值的关键类,并且这个类实现了 Iterator
接口,因此可以直接使用 foreach
便利,但是 MyBatis
的代码并没有这样做,而是使用了递归。
这里再次复习一下, MyBatis
的反射使用入口是 SystemMetaObject
,它包含了整个反射包的基本配置。
在 MyBatis
的代码中,很少能看到静态类,静态工具方法等,基本都是通过对象“注入”到对应的其他对象中。这样当需要修改实现的时候,就能很好的维护。
在 MyBatis
中,首先使用 MetaObject
, MetaObject
内部充当了一个简单工厂方法,用来分辨是处理 Map
还是处理 List
还是 Bean
,
ObjectWrapper
根据不同的实现,使用不同的方式获取底层的值,对于 Bean
来说,使用的是 MetaClass
,
MetaClass
包含了对应 Bean
的 Class
的各种元属性。其中 MetaClass
底层使用的 Reflector
对象
Reflector
对象便是真正的反射实现者,其内部根据传入的 Class
,生成了各种信息,并且由于 Class
是通用的,因此 MyBatis
使用 ReflectorFactory
将其缓存起来。