Mybatis源码--生成JavaBean

Mybatis反射模块

Mybatis中的反射模块是在Statement执行之后对结果集进行处理,转换为我们的Dao实体的重要基础模块,该模块是基于Java的高级特性-反射功能实现,并且根据Java反射又进行了进一步的封装,便于我们更加高效快捷的调用Java的反射功能对Class类和类实例进行操作。当前的Mybatis版本基于3.3.0

模块结构

│ ExceptionUtil.java
│ MetaClass.java 核心
│ MetaObject.java 核心
│ ReflectionException.java
│ Reflector.java 全局核心
│ SystemMetaObject.java

├─factory 对象工厂
│ DefaultObjectFactory.java
│ ObjectFactory.java

├─invoker 方法(setter)调用
│ GetFieldInvoker.java
│ Invoker.java
│ MethodInvoker.java
│ SetFieldInvoker.java

├─property 属性解析
│ PropertyCopier.java
│ PropertyNamer.java
│ PropertyTokenizer.java

└─wrapper 各种包装器
BaseWrapper.java
BeanWrapper.java
CollectionWrapper.java
DefaultObjectWrapperFactory.java
MapWrapper.java
ObjectWrapper.java
ObjectWrapperFactory.java

核心类总结

Reflector

⚠️ 如果要更好的理解该类的作用,必须要理解,对于Java虚拟机,即一个Java应用,Class实例在整个Java应用是唯一的,相应的,Class实例下的Method实例,Field实例也是唯一

该类是整个Mybatis反射模块的核心类,该类的主要作用是封装Mybatis用到的相关类的类型信息,即__Class__ 对象信息。

该类在Mybatis中作为全局核心,其中维护了一个静态的__ConcurrentHashMap__实例,其key值为Class对象,而键则为Reflector对象,所以在这里我们把Reflector类就当作是Class类,只不过对于Mybatisy来说,使用Reflector完全是根据自身需求又对Class对象进行又一次的__轻量__的封装,接下来让我们看看该类的核心属性和核心方法

核心属性
// 是否开启Class缓存,默认为开启
// 如果开启了,Class与Reflector将会全局缓存到  REFLECTOR_MAP中
private static boolean classCacheEnabled = true;
  private static final String[] EMPTY_STRING_ARRAY = new String[0];
  //Class与Refletor的缓存map,这里用ConcurrentHashMap,多线程支持,作为一个缓存
  private static final Map<Class<?>, Reflector> REFLECTOR_MAP = new ConcurrentHashMap<Class<?>, Reflector>();
  //Class类型实例
  private Class<?> type;
  //getter的属性列表
  private String[] readablePropertyNames = EMPTY_STRING_ARRAY;
  //setter的属性列表
  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;
  //setter的方法列表
  private Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
  //getter的方法列表
  private Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
  //setter的类型列表
  private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
  //getter的类型列表
  private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
  //构造函数,这里是使用默认的无参构造
  private Constructor<?> defaultConstructor;

  private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();
核心方法
  • 构造方法
// 构造方法私有,目的是限制要使用该类生成实例,则必须传入Class对象
// 传入Class实例后,会将该Class实例的默认构造方法、getter方法、setter方法、字段解析并放入到上面对应的Map容器中
private Reflector(Class<?> clazz) {
    type = clazz;
    //加入构造函数
    addDefaultConstructor(clazz);
    //加入getter
    addGetMethods(clazz);
    //加入setter
    addSetMethods(clazz);
    //加入字段
    addFields(clazz);
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    for (String propName : readablePropertyNames) {
        //这里为了能找到某一个属性,就把他变成大写作为map的key
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }
  • 实例化方法
  /*
   * Gets an instance of ClassInfo for the specified class.
   * 得到某个类的反射器,是静态方法,而且要缓存,又要多线程,所以REFLECTOR_MAP是一个ConcurrentHashMap
   *
   * @param clazz The class for which to lookup the method cache.
   * @return The method cache for the class
   */
  public static Reflector forClass(Class<?> clazz) {
      //先查看是否开启了类型缓存,开启了就通过Class对象去获取实例
    if (classCacheEnabled) {
      // synchronized (clazz) removed see issue #461
        //对于每个类来说,我们假设它是不会变的,这样可以考虑将这个类的信息(构造函数,getter,setter,字段)加入缓存,以提高速度
      Reflector cached = REFLECTOR_MAP.get(clazz);
        //如果没有就生成一个放到缓存中,然后返回
      if (cached == null) {
        cached = new Reflector(clazz);
        REFLECTOR_MAP.put(clazz, cached);
      }
      return cached;
    } else {
        //没有开启缓存则每次生成并返回
      return new Reflector(clazz);
    }
  }
  • 获取Getter方法和Setter方法

🗒 获取getter方法和setter方法的原理其实很简单,就是把属性名称当作key值,把getter、setter方法当作属性分别放到Reflection类的核心属性 getMethodssetMethods 这两个map中

// 通过属性名称获取对应的setter方法
public Invoker getSetInvoker(String propertyName) {
    Invoker method = setMethods.get(propertyName);
    if (method == null) {
      throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
    }
    return method;
  }
	
//通过属性名称获取对应的getter方法
  public Invoker getGetInvoker(String propertyName) {
    Invoker method = getMethods.get(propertyName);
    if (method == null) {
      throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
    }
    return method;
  }
  • 核心私有方法
//将Class实例中的getter方法放入 getMethods这个Map容器中 
// 同样有个addSetMethods方法  会将seter方法放入  setMethods这个Map容器中
private void addGetMethods(Class<?> cls) {
    Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
    //这里getter和setter都调用了getClassMethods,有点浪费效率了。不妨把addGetMethods,addSetMethods合并	成一个方法叫addMethods
    Method[] methods = getClassMethods(cls);
    for (Method method : methods) {
      String name = method.getName();
      if (name.startsWith("get") && name.length() > 3) {
        if (method.getParameterTypes().length == 0) {
          name = PropertyNamer.methodToProperty(name);
          addMethodConflict(conflictingGetters, name, method);
        }
      } else if (name.startsWith("is") && name.length() > 2) {
        if (method.getParameterTypes().length == 0) {
          name = PropertyNamer.methodToProperty(name);
          addMethodConflict(conflictingGetters, name, method);
        }
      }
    }
    resolveGetterConflicts(conflictingGetters);
  }

Invoker

Invoker类是位于mybatis反射模块下的invoker包中,是一个顶级接口,该接口的作用是对Method实例__invoke__方法的调用,即对Method方法的再次封装,目的是便于Mybatis调用setter方法

  • Invoker接口
public interface Invoker {
  //调用Method的invoke方法,具体由实现类决定
  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

  //获取类型,类型可能是方法的返回值类型,也可能是参数类型,由实现类决定
  Class<?> getType();

  //获取对应的Method实例,默认返回Null
  default Method getMethod(){ return null; }
}
  • MethodInvoker
public class MethodInvoker implements Invoker {

  private Class<?> type;
  private Method method;

  public MethodInvoker(Method method) {
    this.method = method;

    //如果只有一个参数,返回参数类型,否则返回return的类型
    if (method.getParameterTypes().length == 1) {
      type = method.getParameterTypes()[0];
    } else {
      type = method.getReturnType();
    }
  }

  //就是调用Method.invoke
  @Override
  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {

    return method.invoke(target, args);
  }

  @Override
  public Class<?> getType() {
    return type;
  }


  public Method getMethod() {
    return this.method;
  }
}

MetaClass

MetaClass在mybatis模块中的职责是对Reflector实例进行调用,其核心属性就只有一个私有的Reflector实例,该实例不对外使用,MetaClass的所有核心方法都是基于Reflector实例中的方法进行操作

PropertyTokenizer

PropertyTokenizer是一个将属性字符串分解为标记的属性分解器

如person[0].birthdate.year,将依次取得person[0], birthdate, year

核心属性
/**
 * @author Clinton Begin
 */
/**
 * 属性分解为标记,迭代子模式
 * 如person[0].birthdate.year,将依次取得person[0], birthdate, year
 * 
 */
public class PropertyTokenizer implements Iterable<PropertyTokenizer>, Iterator<PropertyTokenizer> {
  //例子: person[0].birthdate.year
  private String name; //person
  private String indexedName; //person[0]
  private String index; //0
  private String children; //birthdate.year
    //此处省略了很多代码
}
核心方法

// 在构造方法分解属性,并赋值给核心属性
public PropertyTokenizer(String fullname) {
      //person[0].birthdate.year
    //先找到 "." 的下标
    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) {
      //去除中括号中的值[0],即取 “0”
      index = name.substring(delim + 1, name.length() - 1);
      name = name.substring(0, delim);
    }
  }
//重写Iterator的 hasNext()方法,判断是否有子属性
//子属性的分解由构造方法实现
@Override
  public boolean hasNext() {
    return children != null;
  }


// 重写Iterator的  next()方法,再次生成构造出一个属性解析器,并且由构造方法实现解析
  @Override
  public PropertyTokenizer next() {
    return new PropertyTokenizer(children);
  }

ObjectWrapper

ObjectWrapper,根据名字我们知道这个类是对象包装者,即对对象进行装饰的类,mybatis中对数据库实体填充数据时需要调用setter方法,就是由该类提供支持

ObjectWrapper接口

ObjectWrapper接口定义了包装类所必须的方法,但在这里mybatis将查询返回的对象类型分为Bean类型、Collection类型,Map类型。所以该接口的实现类分别有BeanWrapper,用于对普通Java进行包装;MapWrapper,对Map类型进行包装,CollectionWrapper,对List类型的数据进行包装

/**
 * @author Clinton Begin
 */
/**
 * 对象包装器
 * 
 */
public interface ObjectWrapper {

    //get
  Object get(PropertyTokenizer prop);

  //set
  void set(PropertyTokenizer prop, Object value);

  //查找属性
  String findProperty(String name, boolean useCamelCaseMapping);

  //取得getter的名字列表
  String[] getGetterNames();

  //取得setter的名字列表
  String[] getSetterNames();

  //取得setter的类型
  Class<?> getSetterType(String name);

  //取得getter的类型
  Class<?> getGetterType(String name);

  //是否有指定的setter
  boolean hasSetter(String name);

  //是否有指定的getter
  boolean hasGetter(String name);

  //实例化属性
  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
  
  //是否是集合
  boolean isCollection();
  
  //添加属性
  public void add(Object element);
  
  //添加属性
  public <E> void addAll(List<E> element);

}
BeanWrapper

BeanWrapper,是对JavaBean进行包装的包装器类型,通过维护一个Object类型的JavaBean对象,和一个MetaClass实例,其原理是通过调用MetaClass实例为Object类型的JavaBean设置属性和进行一些其他操作

核心属性
public class BeanWrapper extends BaseWrapper {


  private static Logger logger = LoggerFactory.getLogger(BeanWrapper.class);
  //原来的对象  该对象其实是一个JavaBean,只不过这里为了更新抽象,使用了Object类型来接收JavaBean
  private Object object;
  //元类
  private MetaClass metaClass;
 
  //此处省略了很多代码....  
    
}
核心方法
// 执行setter方法  
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
    try {
        //得到setter方法,然后调用
      Invoker method = metaClass.getSetInvoker(prop.getName());
      logger.info("获取到setter方法:{} -- 设置值:{}",method.getMethod().getName(),value);
      Object[] params = {value};
      try {
        method.invoke(object, params);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (Throwable t) {
      throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
    }
  }

// 执行getter方法
  private Object getBeanProperty(PropertyTokenizer prop, Object object) {
    try {
        //得到getter方法,然后调用
      logger.info("");
      Invoker method = metaClass.getGetInvoker(prop.getName());
      try {
        return method.invoke(object, NO_ARGUMENTS);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (RuntimeException e) {
      throw e;
    } catch (Throwable t) {
      throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);
    }
  }

MetaObject

MetaObject是mybatis反射模块中给对象设置属性值,调用setter方法的实际操作者,我们可以将该类比作一位指挥家,他是一个乐团的操控者,他会根据乐谱(类)指挥演奏者(ObjectWrapper、ObjectFactory、ObjectWrapperFactory)完成一首曲子(Object)

核心属性
/**
 * @author Clinton Begin
 */
/**
 * 元对象,各种get,set方法有点ognl表达式的味道
 * 可以参考MetaObjectTest来跟踪调试,基本上用到了reflection包下所有的类
 * 
 */
public class MetaObject {

  private static Logger logger = LoggerFactory.getLogger(MetaObject.class);
    //需要设置属性的对象,在Mybatis中可以理解为数据库实体
  private Object originalObject;
    //对象包装器
  private ObjectWrapper objectWrapper;
    //对象工厂
  private ObjectFactory objectFactory;
    // 对象包装器工厂
  private ObjectWrapperFactory objectWrapperFactory;
  
    //此处省略了很多代码.....
  }
核心方法
//私有化构造方法
// 构造方法中判断需要设置的对象是属于普通类型、Map类型、Collection类型,将ObjectWrapper进行赋值
// 不同类型的ObjectWrapper赋值时所调用方法不同
// 普通类型   调用setter方法
// Map类型    调用put方法
// Collection类型  调用add方法
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    this.originalObject = object;
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;

    if (object instanceof ObjectWrapper) {
        //如果对象本身已经是ObjectWrapper型,则直接赋给objectWrapper
      this.objectWrapper = (ObjectWrapper) object;
    } else if (objectWrapperFactory.hasWrapperFor(object)) {
        //如果有包装器,调用ObjectWrapperFactory.getWrapperFor
      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    } else if (object instanceof Map) {
        //如果是Map型,返回MapWrapper
      this.objectWrapper = new MapWrapper(this, (Map) object);
    } else if (object instanceof Collection) {
        //如果是Collection型,返回CollectionWrapper
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    } else {
        //除此以外,返回BeanWrapper
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }
  
  // 静态实例化方法
  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    if (object == null) {
        //处理一下null,将null包装起来
        //把空值当作一个实例,当判断一个属性是否为空时,则判断生成的MetaObject是否为						//SystemMetaObject.NULL_META_OBJECT即可,并且该实例全局唯一并且使用缓存在内存中
        // 使用方法类似于  Collections.emptyList()   Collections.emptySet().....
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory);
    }
  }
  //对象的值获取,其实内部调用getter方法
  //调用顺序  ObjectWrapper.get() --  MetaClass.getGetInvoker() -- Invoker.invoke()
  // 这个方法的神奇之处在于,可以通过object.field.field...的方法调用,具体逻辑有下面的两种形式

  public Object getValue(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
      // 如果name是object.field.field...形式的,则会递归调用,直至找到最后一个.后面的属性名称
      // 具体请查看PropertyTokenizer
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
          //如果上层就是null了,那就结束,返回null
        return null;
      } else {
          //否则继续看下一层,递归调用getValue
       return metaValue.getValue(prop.getChildren());
      }
    } else {
        // name直接是属性名称,则直接调用该属性对应的getter方法
      return objectWrapper.get(prop);
    }
  }

 // 执行逻辑与getValue方法类似
 // 调用顺序    ObjectWrapper.set() --  MetaClass.getSetInvoker() -- Invoker.invoke()
  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 && prop.getChildren() != null) {
          // don't instantiate child path if value is null
          //如果上层就是null了,还得看有没有儿子,没有那就结束
          return;
        } else {
            //否则还得new一个,委派给ObjectWrapper.instantiatePropertyValue
          metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
        }
      }
      //递归调用setValue
      metaValue.setValue(prop.getChildren(), value);
    } else {
        //到了最后一层了,所以委派给ObjectWrapper.set
      objectWrapper.set(prop, value);
    }
  }
MetaObject小结

通过阅读MetaObject的核心属性和核心方法我们可以得出一个结论,MetaObject是对JavaBean属性操作的最终执行者,他可以通过ObjectWrapper获取Getter、Setter方法,然后结合 originalObject(即我们需要操作的JavaBean),进行操作,所以MetaObject是一个非常强力的类,并且在setValue、getValue中结合PropertyTokizer类可以实现类似于__ognr__表达式的形式对属性进行更加方便、灵活的操作,MetaObject就是一个集大成者

实例

给一个JavaBean赋值

    //对象工厂和对象包装器工厂
    public static ObjectFactory objectFactory = new DefaultObjectFactory();
    public static ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
	
	//创建JavaBean
	Role role = objectFactory.create(Role.class);

	//创建元对象
	MetaObject metaObject = MetaObject.forObject(role, objectFactory, objectWrapperFactory);

	//赋值
	metaObject.setValue("roleName","admin");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/***********************基本描述**********************************/ 0、根据表可以单独生成javaBean后缀可以自定义 1、工具本身是非常简单的,每个人都能做就是使用模板替换生成相应文件 2、工具主要针对SpringMvc+Mybatis注解+Mysql生成对象,dao、sqlDao、interface、实现接口 3、根据表生成Excel 4、生成成功后倒入到自己对应的项目中,然后Ctrl+Shipt+O(Eclipse快速倒入包)实现 5、里面因为运用的是注解,所以很多包我就没有提供了因为这些都是很基础的东西,不会的同学可以去网上查看搭建Mybatis的注解 6、生成了些什么,具体主要是对单表的增、删、改、查(分页) /********************************/ /********************************/ /*************完全免费***********/ /********************************/ /********************************/ 如果大家喜欢可以再给我提其他功能,有时间我加上 /*********************************************************************************/ 模板介绍: MySql.Data.dll :连接Mysql基本dl我们的的驱动。 foxjava.exe :直接运行程序 xml : Excel文件夹 ##### TemplateXml.xml 根据数据库对应表生成字段描述,生成后最好用WPS打开,然后重新另存为office认识的Excel template : 文件生成模板(非常重要的不能修改) ##### BasePojo.template 所有基础表对象都要继承,方便序列化(系统自动生成) ##### Pager.template 分页对象 (系统自动生成) ##### dao.template 数据库接口Dao(mybatis接口方式,在方法上写sql,复杂的使用sqlProvider) ##### daoSqlProvider.template 复杂sql提供者 ##### service.template 对外开放的接口 ##### serviceImpl.template 实现开放接口,基本数据操作逻辑 /*********************************************************************************/

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值