Mybatis是个优秀的ORM框架,所以它的反射层一定不会让我们失望

图比较大,可以开新页面查看

可以看到,Mybatis对这一块抽象的比较复杂,我们可以看到有几个比较主要的部分:Reflector、Invoker、PropertyTokenizer、MetaClass,MetaObject和ObjectWrapper,下面我们一个一个解析这几个部分,最后合并在一起看看他们是如何协作工作的。

Reflector

我对Reflector的理解是 Reflector是对类的描述 ,我们从一段UT开始(代码位于ReflectorTest):

  1. static interface Entity<T> {
  2. T getId();
  3. void setId(T id);
  4. }
  5.  
  6. static abstract class AbstractEntity implements Entity<Long> {
  7.  
  8. private Long id;
  9.  
  10. @Override
  11. public Long getId() {
  12. return id;
  13. }
  14.  
  15. @Override
  16. public void setId(Long id) {
  17. this.id = id;
  18. }
  19. }
  20.  
  21. static class Section extends AbstractEntity implements Entity<Long> {
  22. }
  23.  
  24. @Test
  25. public void testGetSetterType() throws Exception {
  26. ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  27. Reflector reflector = reflectorFactory.findForClass(Section.class);
  28. Assert.assertEquals(Long.class, reflector.getSetterType("id"));
  29. }

这个测试方法首先创建了个ReflectorFactory对象,然后用这个factory创建了一个Section类的Reflector,然后判断Section类中id的setter方法是Long类型的。

ReflectorFactory是对Reflector做的一个简单的工厂,提供类反射的缓存(所以反射这块的开销基本上可以不计了,既灵活又快捷)

DefaultReflectorFactory 是 Reflector的默认实现类,用一个ConcurrentMap缓存所有的Reflector示例,它的findForClass方法如下,它首先尝试从map中获取Reflector,获取失败调用Reflector的构造方法创建示例,缓存并返回:

  1. @Override
  2. public Reflector findForClass(Class<?> type) {
  3. if (classCacheEnabled) {
  4. // synchronized (type) removed see issue #461
  5. Reflector cached = reflectorMap.get(type);
  6. if (cached == null) {
  7. cached = new Reflector(type);
  8. reflectorMap.put(type, cached);
  9. }
  10. return cached;
  11. } else {
  12. return new Reflector(type);
  13. }
  14. }

,之后是Reflector的构造方法:

  1. public Reflector(Class<?> clazz) {
  2. type = clazz;
  3. addDefaultConstructor(clazz);
  4. addGetMethods(clazz);
  5. addSetMethods(clazz);
  6. addFields(clazz);
  7. readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
  8. writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
  9. for (String propName : readablePropertyNames) {
  10. caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
  11. }
  12. for (String propName : writeablePropertyNames) {
  13. caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
  14. }
  15. }

这一块不再细细展开,方法名见名知义,首先将type成员设置为原始的class对象,之后获取class的构造方法,getter/setter属性,成员字段,之后将属性名转大写存放到caseInsensitivePropertyMap中,为了后面的查找,大小写不敏感。

Reflector的其他方法就是对我们保存的这些类的描述做查找, 其中有两个特别的,也就是我们接下来要讨论的 getSetInvoker和 getGetInvoker

Invoker

Invoker,顾名思义,就是调用,可以调用的东西,他有一个invoke方法,意思就是调用,参数是target和args,就是调用的对象和调用的参数。

我们来看下它的几个实现类:

MethodInvoker: 方法调用

SetFieldInvoker:Setter方法调用

GetFieldInvoker:Getter方法调用

MethodInvoker中invoke方法的实现:

  1. @Override
  2. public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
  3. return method.invoke(target, args);
  4. }

就是简单的method.invoke,

PropertyTokenizer

这个就比较牛逼了,他可以处理属性表达式,PropertyTokenizer还实现了Iterator接口,这意味着他可以处理复杂的嵌套属性

  1. @Override
  2. public boolean hasNext() {
  3. return children != null;
  4. }
  5.  
  6. @Override
  7. public PropertyTokenizer next() {
  8. return new PropertyTokenizer(children);
  9. }

字段的含义,name表示当前对象的名字,indexedName是当前对象的名字加上后面的索引([])如果有的话,index是索引下标,children是延伸属性(子对象)

比如:用PropertyTokenizer去解析 "richType.richList[0].value",那么 name=richType, indexedName=richType,index=null,children=richList[0].value

之后执行tokenizer.next()得到新的tokenizer,此时 name=richList, indexdName=richList[0],index=0, children=value

之后我们会结合MetaClass和MetaObject看看他有多牛逼

MetaClass

MetaClass实际上是对Reflector和ProeprtyTokenizer的一种结合,是我们可以用复杂的属性表达式来获取类型的描述。

同样的,我们结合UT来看看它是怎样工作的,首先是一个示例的复杂类型 RichType

  1. public class RichType {
  2.  
  3. private RichType richType;
  4.  
  5. private String richField;
  6.  
  7. private String richProperty;
  8.  
  9. private Map richMap = new HashMap();
  10.  
  11. private List richList = new ArrayList() {
  12. {
  13. add("bar");
  14. }
  15. };
  16.  
  17. public RichType getRichType() {
  18. return richType;
  19. }
  20.  
  21. public void setRichType(RichType richType) {
  22. this.richType = richType;
  23. }
  24.  
  25. public String getRichProperty() {
  26. return richProperty;
  27. }
  28.  
  29. public void setRichProperty(String richProperty) {
  30. this.richProperty = richProperty;
  31. }
  32.  
  33. public List getRichList() {
  34. return richList;
  35. }
  36.  
  37. public void setRichList(List richList) {
  38. this.richList = richList;
  39. }
  40.  
  41. public Map getRichMap() {
  42. return richMap;
  43. }
  44.  
  45. public void setRichMap(Map richMap) {
  46. this.richMap = richMap;
  47. }
  48. }
  1. @Test
  2. public void shouldCheckGetterExistance() {
  3. ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  4. MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
  5. assertTrue(meta.hasGetter("richField"));
  6. assertTrue(meta.hasGetter("richProperty"));
  7. assertTrue(meta.hasGetter("richList"));
  8. assertTrue(meta.hasGetter("richMap"));
  9. assertTrue(meta.hasGetter("richList[0]"));
  10.  
  11. assertTrue(meta.hasGetter("richType"));
  12. assertTrue(meta.hasGetter("richType.richField"));
  13. assertTrue(meta.hasGetter("richType.richProperty"));
  14. assertTrue(meta.hasGetter("richType.richList"));
  15. assertTrue(meta.hasGetter("richType.richMap"));
  16. assertTrue(meta.hasGetter("richType.richList[0]"));
  17.  
  18. assertFalse(meta.hasGetter("[0]"));
  19. }

这段代码说明了metaClass.hasGetter方法可以接受一个复杂的属性表达式来找到对应的类型描述(利用PropertyTokenizer),这个神奇的功能是这么实现的:

  1. public boolean hasGetter(String name) {
  2. PropertyTokenizer prop = new PropertyTokenizer(name);
  3. if (prop.hasNext()) {
  4. if (reflector.hasGetter(prop.getName())) {
  5. MetaClass metaProp = metaClassForProperty(prop);
  6. return metaProp.hasGetter(prop.getChildren());
  7. } else {
  8. return false;
  9. }
  10. } else {
  11. return reflector.hasGetter(prop.getName());
  12. }
  13. }

首先检查tokenizer的name字段对应的属性是不是有getter方法,之后迭代子属性,直到最后,children为空。

MetaClass中的还有几个方法的实现和这个类似,hasSetter, getGetterType, getSetterType

以上都是类级别的反射抽象,下面看看对象级别的

ObjectWrapper

ObjectWrapper是对对象的描述的抽象,它抽象出一系列对对象描述的查询和更新的接口

  1. public interface ObjectWrapper {
  2.  
  3. Object get(PropertyTokenizer prop);
  4.  
  5. void set(PropertyTokenizer prop, Object value);
  6.  
  7. String findProperty(String name, boolean useCamelCaseMapping);
  8.  
  9. String[] getGetterNames();
  10.  
  11. String[] getSetterNames();
  12.  
  13. Class<?> getSetterType(String name);
  14.  
  15. Class<?> getGetterType(String name);
  16.  
  17. boolean hasSetter(String name);
  18.  
  19. boolean hasGetter(String name);
  20.  
  21. MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
  22.  
  23. boolean isCollection();
  24.  
  25. void add(Object element);
  26.  
  27. <E> void addAll(List<E> element);
  28.  
  29. }

ObjectWrapper有个几个实现类

BeanWrapper,包装Javabean的描述,

MapWrapper,包装Map(键值对)的描述

CollectionWrapper,包装Collection(集合)的描述

ObjectWrapperFactory 了操作实例化ObjectWrapper的工厂方法的抽象,可自定义实现

  1. private Object getBeanProperty(PropertyTokenizer prop, Object object) {
  2. try {
  3. Invoker method = metaClass.getGetInvoker(prop.getName());
  4. try {
  5. return method.invoke(object, NO_ARGUMENTS);
  6. } catch (Throwable t) {
  7. throw ExceptionUtil.unwrapThrowable(t);
  8. }
  9. } catch (RuntimeException e) {
  10. throw e;
  11. } catch (Throwable t) {
  12. throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t);
  13. }
  14. }
  15.  
  16. private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
  17. try {
  18. Invoker method = metaClass.getSetInvoker(prop.getName());
  19. Object[] params = {value};
  20. try {
  21. method.invoke(object, params);
  22. } catch (Throwable t) {
  23. throw ExceptionUtil.unwrapThrowable(t);
  24. }
  25. } catch (Throwable t) {
  26. throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
  27. }
  28. }

MetaObject

MetaObject也是对对象的描述,它代理了objectWrapper的大部分方法,和MetaClass一样,它利用PropertyTokenizer做了对复杂属性表达式的处理

  1. @Test
  2. public void shouldGetAndSetNestedField() {
  3. RichType rich = new RichType();
  4. MetaObject meta = SystemMetaObject.forObject(rich);
  5. meta.setValue("richType.richField", "foo");
  6. assertEquals("foo", meta.getValue("richType.richField"));
  7. }

MetaObject有5个成员字段,

originalObject:原始对象,

objectWrapper,被包装的objectWrapper,

objectFactory,对象创建工厂,用于在setValue方法中创建不存在的对象属性实例,

objectWrapperFactory,创建特定的objectWrapper,

reflectorFactory,暂时不知道是干什么的

协作

反射层的类互相协作,最终根据入参制作出来一个完美的MetaObject和MetaClass给其他组件使用,这其中,比较重要的方法有:

Configuration.newMetaObject,根据传入的object和配置的factory创建对象描述

实际上,ObjectFactory,ObjectWrapperFactory,ReflectorFactory是可以在XML中配置成自定义的,工厂对象全局单例(Configuration对象中),

  1. private void parseConfiguration(XNode root) {
  2. try {
  3. //issue #117 read properties first
  4. propertiesElement(root.evalNode("properties"));
  5. Properties settings = settingsAsProperties(root.evalNode("settings"));
  6. loadCustomVfs(settings);
  7. typeAliasesElement(root.evalNode("typeAliases"));
  8. pluginElement(root.evalNode("plugins"));
  9. objectFactoryElement(root.evalNode("objectFactory"));
  10. objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  11. reflectorFactoryElement(root.evalNode("reflectorFactory"));
  12. settingsElement(settings);
  13. // read it after objectFactory and objectWrapperFactory issue #631
  14. environmentsElement(root.evalNode("environments"));
  15. databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  16. typeHandlerElement(root.evalNode("typeHandlers"));
  17. mapperElement(root.evalNode("mappers"));
  18. } catch (Exception e) {
  19. throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  20. }
  21. }

XMlConfigBuilder.settingsAsProperties方法使用MetaClass检查Properties参数有没有非法的key,

MetaObject和MetaClass在Session的执行周期(executor, mapping, builder...)中还具有广泛的应用

mybatis 与 反射的更多相关文章

  1. mybatis源码- 反射模块一(跟着MyBatis学反射):类级别信息的封装

    目录 1 JavaBean 规范 2 Reflector和ReflectorFactory 2.1 Reflector 属性 2.1.1 属性 2.1.2 Invoker 接口 2.2 Reflect ...

  2. 【mybatis源码学习】mybatis的反射模块

    一.重要的类和接口 org.apache.ibatis.reflection.MetaClass//对于javaBean的calss类进行反射操作的代理类(获取属性的类型,获取属性的get,set方法 ...

  3. MyBatis之反射技术+JDK动态代理+cglib代理

    一.反射 引用百度百科说明: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功 ...

  4. MyBatis源码分析-基础支持层反射模块Reflector/ReflectorFactory

    本文主要介绍MyBatis的反射模块是如何实现的. MyBatis 反射的核心类Reflector,下面我先说明它的构造函数和成员变量.具体方法下面详解. org.apache.ibatis.refl ...

  5. 【mybatis源码学习】mybtias基础组件-反射工具

    一.JavaBean的规范 类中定义的成员变量也称为字段,属性则是通过getter/setter方法得到的,属性只与类中的方法有关,与是否存在对应成员变量没有关系. 属性的getter/setter方 ...

  6. Mybatis源码学习之反射工具(三)

    简述 MyBatis在进行参数处理.结果映射等操作时,会涉及大量的反射操作.Java中的反射虽然功能强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码,MyBatis提供了专门的反射 ...

  7. Mybatis面试整理

    #{}和${}的区别 #{}是预编译处理,${}是字符串替换. Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值: Mybatis ...

  8. Mybatis面试集合(转)

    Mybatis技术内幕系列博客,从原理和源码角度,介绍了其内部实现细节,无论是写的好与不好,我确实是用心写了,由于并不是介绍如何使用Mybatis的文章,所以,一些参数使用细节略掉了,我们的目标是介绍 ...

  9. Mybatis面试题

    面试题示例 1.JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的? 1)数据库链接创建.释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题. 解决:在SqlMap ...

随机推荐

  1. VS2013打包与部署

    近期做一个配置工具,完事了想打包一下:由于用的是VS2013:与之前的略有不同,简单的做了一下,在这里分享一下,直接看吧: 首先  是自己新建一个项目 ,我的WPF应用程序 第二步:右键解决方案添加新 ...

  2. java学习——多线程

    本文内容来源于  历经5年锤练--史上最适合初学者入门的Java基础视频 线程:就是进程中一个负责程序执行的控制单元(执行路径) 每一个线程都有自己运行的内容.这个内容可以称为线程要执行的任务. 多线 ...

  3. 你好,C++(23) 4.4.2 工资程序成长记:用数组处理批量数据,用循环结构执行重复动作

    4.4  从语句到程序 了解了各种表达式和语句之后,就相当于掌握了写作文要用到的词语和句子,但是,仅有词语和句子是无法构成一篇有意义的文章的.要完成一篇文章,先需要确定这篇文章的结构,是先分述再总述, ...

  4. C++ Primer 5th 第7章 类

    类的基本思想是数据抽象和封装,定义类就是定义一个抽象数据类型. 类中的所有成员必须在类中声明,也即默认定义在类中的成员全部为声明,除非显式的定义成员函数的函数体.成员函数是在类中声明的,定义可以在类内 ...

  5. htmlspecialchars()函数

    htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体. 预定义的字符是: & (和号) 成为 & " (双引号) 成为 " ' (单引 ...

  6. Razor视图引擎

    在MVC3.0版本的时候,微软终于引入了第二种模板引擎:Razor.在这之前,我们一直在使用WebForm时代沿留下来的ASPX引擎或者第三方的NVelocity模板引擎. (1)Razor文件类型: ...

  7. twisted(3)--再谈twisted

    上一章,我们直接写了一个小例子来从整体讲述twisted运行的大致过程,今天我们首先深入一些概念,在逐渐明白这些概念以后,我们会修改昨天写的例子. 先看下面一张图: 这个系列的第一篇文章,我们已经为大 ...

  8. DOCKER脚本一例---快速建立大批测试机

    这个会由一系列的脚本构成,比如: 系统重启后,如何快速恢复服务,如何建立网桥(也可一次写入),如何在新系统上快速部署. ADDBRIDGE #!/bin/sh br_name=br100 brctl ...

  9. Local System/Network Service/Local Service

    // The name of the account under which the service should run// 1 NT AUTHORITY\\SYSTEM 2 NT AUTHORIT ...

  10. Python web.py 实例和源码分析:

    示例: http://jyd.me/python/cookie-and-session/ simple to do:http://simple-is-better.com/news/309 http: ...