mybaits源码分析(六) 基础支持模块-反射-Io-类型处理

    mybaits基础支撑

        概述: mybaits的核心处理工作是由Executor、StatementHandler、ResultHandler、paramHandler四个模块处理整个sql执行过程,在这些过程中,主要对配置解析的一些MapperStatement、SqlSource、ResultMap、ParameterMapping这些参数型对象,进行处理工作,最终生成真实sql执行的过程,而本文主要讲解一些基础支撑的模块,方便后续的分析。

        本文主要分析三个基础模块
        1、资源加载模块
        2、反射模块
        3、类型处理模块

 一、资源加载模块

资源加载模块在io包下,主要是由Resources类、ClassLoaderWrapper类组成,ClassLoaderWrapper类提供了获得类加载器的方法,以及根据类加载器加载资源文件和classForName等方法,Resources是对ClassLoaderWrapper对象的包装,提供静态调用的一系列方法。


1、ClassLoaderWrapper的部分代码

 /**
             * 获取类加载器列表: 指定的,当前线程的,当前类的,system类加载器
             */
            ClassLoader[] getClassLoaders(ClassLoader classLoader) {
                return new ClassLoader[] { classLoader, Thread.currentThread().getContextClassLoader(),
                        getClass().getClassLoader(), systemClassLoader };
            }
            
            ClassLoader systemClassLoader;
            
            {
                systemClassLoader = ClassLoader.getSystemClassLoader();
            }
            
            
            /**
             * classForName :取可用的classLoader调用Class.forName
             */
            Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
                for (ClassLoader cl : classLoader) {
                    if (null != cl) {
                        try {
                            Class<?> c = Class.forName(name, true, cl);
                            if (null != c)
                                return c;
                        } catch (ClassNotFoundException e) {
                        }
                    }
                }
                throw new ClassNotFoundException("Cannot find class: " + name);
            }
            
            /**
             * getResourceAsStream :取可用的classLoader调用getResourceAsStream。
             */
            InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
                for (ClassLoader cl : classLoader) {
                    if (null != cl) {
                        InputStream returnValue = cl.getResourceAsStream(resource);
                        if (null == returnValue)
                            returnValue = cl.getResourceAsStream("/" + resource);
                        if (null != returnValue)
                            return returnValue;
                    }
                }
                return null;
            }

    二、    反射模块

mybaits很多地方用了反射,外部api多数通过调用的是MetaObject方法实现调用底层反射方法,这个类可以包装普通bean,也可以包装集合,并且实现了属性标示符的方式访问属性。另外Reflector类是用于反射模块调用原生反射方法,也是MetaObject的一个底层功能操作的类。

1、MetaObject测试案例

@Test
	public void test1(){
		Person p = new Person();
		MetaObject meta = MetaObject.forObject(p, new DefaultObjectFactory(), new DefaultObjectWrapperFactory());
		meta.setValue("name", "haha"); // setValue通过反射设置对象的属性
		System.out.println(p.getName());
	}
	
	@Test
	public void test2(){
		Person p = new Person();
		p.setName("haha");
		List<Person> ps = new ArrayList<Person>(); // 包装lists
		MetaObject meta = MetaObject.forObject(ps, new DefaultObjectFactory(), new DefaultObjectWrapperFactory());
		meta.add(p); // MetaObject传入参数如果是集合类型,可以通过add给集合添加属性。
		System.out.println(ps.get(0).getName());
	}
	
	@Test
	public void test3(){
		Person p = new Person();
		p.setName("haha");
		Map<String,Person> map = new HashMap<String,Person>(); // 包装map
		MetaObject meta = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory());
		meta.setValue("deer", p); // 通过setValue可以给map添加属性
		System.out.println(map.get("deer").getName());
	}
	
	/**
	 * 属性分割符,操作内部成员对象的属性
	 */
	@Test
	public void test4(){
		Person p = new Person();
		MetaObject meta = MetaObject.forObject(p, new DefaultObjectFactory(), new DefaultObjectWrapperFactory());
		meta.setValue("child.name", "haha"); // 可以通过.的方式调用属性的属性
		System.out.println(p.getChild().getName()); // 普通方式获取
		System.out.println(meta.getValue("child.name")); // 反射方式获取值,也可以用.的方式
	}

	/**
	 * 属性分割符,操作内部成员集合对象的属性
	 */
	@Test
	public void test5(){
		Person p = new Person();
		p.getChildren().add(new Person());
		MetaObject meta = MetaObject.forObject(p, new DefaultObjectFactory(), new DefaultObjectWrapperFactory());
		meta.setValue("children[0].name", "haha");
		System.out.println(p.getChildren().get(0).getName());
		System.out.println(meta.getValue("children[0].name"));
	}

从上面测试可以看到通过MetaObject可以非常方便的使用需要通过反射进行属性操作、方法调用、创建实例等等反射的操作,并且它兼容了集合或普通bean的使用。

    2、反射模块的类

    MetaObject:主要外部使用类,上面已经演示功能。
    Reflector:基础反射类, 将一个class的所有methods、fileds、Constructor、等等反射的方法进行包装,并且初始化的时候全部解析了。
    MetaClass:包装反射功能,并且增强了属性解析的支持,即prop.prop.prop的形式。
    PropertyTokenizer:对prop.prop.prop形式属性结构的包装。
    ObjectWrapper:对象包装接口,它由MetaObject创建,并且持有MetaObject的引用(用于类递归调用),有4个实现,一个是BaseWrapper,一个是BeanWrapper,一个是CollectionWrapper,一个是MapWrapper。

    3、Reflector

a)先看一下主要属性和构造函数

 private Class<?> type;
	  private String[] readablePropertyNames = EMPTY_STRING_ARRAY; // 可读属性名数组
	  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY; // 可写属性名数组
	  private Map<String, Invoker> setMethods = new HashMap<String, Invoker>(); // set方法
	  private Map<String, Invoker> getMethods = new HashMap<String, Invoker>(); // get方法
	  private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>(); // set类型
	  private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>(); // get类型
	  private Constructor<?> defaultConstructor; // 默认构造函数
	  private Reflector(Class<?> clazz) {
	    type = clazz; // 指定类型
	    addDefaultConstructor(clazz); //解析默认构造函数
	    addGetMethods(clazz); // 解析所有get方法
	    addSetMethods(clazz); // 解析所有set方法
	    addFields(clazz); // 所有字段
	    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); // 可读属性名的集合
	    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); // 可写属性名的集合
	  }

      Reflector在创建构造函数的时候,就解析了class所有的方法、字段、构造函数等信息,后续可直接使用或供判断。下面看看解析一个添加方法的过程。
  b) addGetMethods 方法

 private void addGetMethods(Class<?> cls) {
	    Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>(); // 用于解决多个method冲突用
	    Method[] methods = getClassMethods(cls); // 得到本类父类的所有声明方法,踢出了桥接方法
	    for (Method method : methods) {
	      String name = method.getName();
	      if (name.startsWith("get") && name.length() > 3) { // 是get方法
		if (method.getParameterTypes().length == 0) { // 并且没有参数
		  name = PropertyNamer.methodToProperty(name); // 方法名称变成属性名
		  addMethodConflict(conflictingGetters, name, method); // 添加到处理冲突的map中,因为一个属性名可能对应多个方法
		}
	      } else if (name.startsWith("is") && name.length() > 2) { // 是is方法
		if (method.getParameterTypes().length == 0) {
		  name = PropertyNamer.methodToProperty(name);
		  addMethodConflict(conflictingGetters, name, method);
		}
	      }
	    }
	    resolveGetterConflicts(conflictingGetters); // 处理冲突
	  }
		 
    	   private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
	    for (String propName : conflictingGetters.keySet()) {
	      List<Method> getters = conflictingGetters.get(propName);
	      Iterator<Method> iterator = getters.iterator();
	      Method firstMethod = iterator.next();
	      if (getters.size() == 1) { // 冲突只有1个method直接添加
		addGetMethod(propName, firstMethod);
	      } else {
		Method getter = firstMethod; 
		Class<?> getterType = firstMethod.getReturnType(); // 上次遍历方法的返回类型
		while (iterator.hasNext()) {
		  Method method = iterator.next();
		  Class<?> methodType = method.getReturnType();
		  if (methodType.equals(getterType)) {
		    throw new ReflectionException(""); // 虽然是合法重载,但是属性类型不明确,不符合规范。
		  } else if (methodType.isAssignableFrom(getterType)) { // 返回类型是上次的父类型,更不明确,不使用。
		    // OK getter type is descendant
		  } else if (getterType.isAssignableFrom(methodType)) { // 如果当前遍历方法返回类型比上次返回类型更具体。
		    getter = method; // 指定此方法为当前遍历方法
		    getterType = methodType;  // 指定成此类型
		  } else {
		    throw new ReflectionException();
		  }
		}
		addGetMethod(propName, getter);
	      }
	    }
	  }

4、MetaClass

 MetaClass是对Reflector的一个包装,并且增强了对prop.prop属性访问符的反射支持。

5、 PropertyTokenizer

     PropertyTokenizer主要功能就是把一个prop.prop属性符号形式用对象表示,
     比如 : list[0].item[2].id 最后解析成TempObj [name=list, IndexedName=list[0], index=0, children=item[2].id]的对象
     代码实现如下:

 public PropertyTokenizer(String fullname) {
		    int delim = fullname.indexOf('.'); // 对象符
		    if (delim > -1) { // 有
		      name = fullname.substring(0, delim); // name第一节点
		      children = fullname.substring(delim + 1); // children为剩余的
		    } else {
		      name = fullname; 
		      children = null;
		    }
		    indexedName = name; // 属性名称有可能带[]的形式
		    delim = name.indexOf('[');
		    if (delim > -1) { // 如果有[
		      index = name.substring(delim + 1, name.length() - 1); // index为数组属性中角标
		      name = name.substring(0, delim); // name要去除[]
		    }
		  }

6、MetaObject

 a) 先看MetaObject的构造函数

private Object originalObject;  // 原对象
	  private ObjectWrapper objectWrapper; // 对象包装
	  private ObjectFactory objectFactory; // 对象创建工厂
	  private ObjectWrapperFactory objectWrapperFactory; // 忽略
	  private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
	    this.originalObject = object;
	    this.objectFactory = objectFactory;
	    this.objectWrapperFactory = objectWrapperFactory;
	    if (object instanceof ObjectWrapper) {
	      this.objectWrapper = (ObjectWrapper) object;
	    } else if (objectWrapperFactory.hasWrapperFor(object)) { // 空实现
	      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
	    } else if (object instanceof Map) { // MapWrapper的包装
	      this.objectWrapper = new MapWrapper(this, (Map) object);
	    } else if (object instanceof Collection) { // Collection的包装
	      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
	    } else {  // bean的包装
	      this.objectWrapper = new BeanWrapper(this, object); // 如果是javabean,就包装成这个,并且传递this,是为了伪递归调用本对象的getValue等方法。
	    }
	  }

从MetaObject的构造函数可以看到,MetaObject会根据待包装对象类型的不同,创建对应的ObjectWrapper,其中MapWrapper和CollectionWrapper是一些容器方法的实现,BeanWrapper是反射方法的实现。每个BeanWrapper持有创建它的MetaObject的引用,并且内部调用MetaClass实现反射操作。

     我们对下面的setValue方法进行分析

  public void setValue(String name, Object value) {
		    PropertyTokenizer prop = new PropertyTokenizer(name); // 包装属性访问符成为对象
		    if (prop.hasNext()) { // 判断是否有子属性
			// 这里反射获取此子属性的值,这里内部调用getValue,但是只有单个name,因此不会递归获取子属性
			// 获取子属性后,会包装成新的MetaObject
		        MetaObject metaValue = metaObjectForProperty(prop.getIndexedName()); 
			if (metaValue == SystemMetaObject.NULL_META_OBJECT) { // value空值包装成的默认空的MetaObject
		            if (value == null && prop.getChildren() != null) {
		              return; 
		            } else {
			      // 实例化此子属性,并且反射设置到此对象中,另外包装成MetaObject
		             metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
		            } 
			}
			// 当前name属性因为有子属性需要设置,本name的属性已经实例化,并且本name属性也已经包装成了MetaValue,就可以伪递归SetValue
		        metaValue.setValue(prop.getChildren(), value);
		    } else { // 只设置一级属性的值。
		      objectWrapper.set(prop, value);
		    }
		  }
		  public MetaObject metaObjectForProperty(String name) {
		    Object value = getValue(name); // 因为name是个单节点的属性,getValue不会递归了。
		    return MetaObject.forObject(value, objectFactory, objectWrapperFactory); // value就算空值也会进行包装
		  }

上面就是通过MetaObject反射设置对象符形式属性字符串的方式,这个类的功能整合了上面所有类的功能。
       MetaObject实现逻辑的文字描述:
        一个对象可能是集合也可能是javaBean,我们要通过属性符方式访问,就先对属性符对象化,然后根据对象是集合还是javabean,实例化不同的ObjectWrapper, ObjectWrapper负责反射或集合方式访问属性,多级属性,通过每级包装成新的MetaObject的方式,进行伪递归操作,递归传入的prop符是第二级后的。

 

三、类型处理模块

 1、基本介绍。

  类型处理的顶级接口是TypeHandler,它的接口形式如下:

  类型处理的顶级接口是TypeHandler,它的接口形式如下:
	  public interface TypeHandler<T> {
		
		/**
		 * 填充PreparedStatement的参数
		 */
		void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
	
		/**
		 * 得到结果,根据别名
		 */
		T getResult(ResultSet rs, String columnName) throws SQLException;
	
		/**
		 * 得到结果,根据列角标
		 */
		T getResult(ResultSet rs, int columnIndex) throws SQLException;
	
		/**
		 * 从CallableStatement得到结果。
		 */
		T getResult(CallableStatement cs, int columnIndex) throws SQLException;
	
	}

 其主要作用就是供ParameterHandler和ResultetHandler在处理预处理语句的参数隐射,以及结果集处理的类型转换方法。而它的具体实现,就是每种类型对应不同处理的实现,我们挑选几个看看。

public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
	  @Override
	  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
	      throws SQLException {
	    ps.setInt(i, parameter);
	  }
	  @Override
	  public Integer getNullableResult(ResultSet rs, String columnName)
	      throws SQLException {
	    return rs.getInt(columnName);
	  }
	  @Override
	  public Integer getNullableResult(ResultSet rs, int columnIndex)
	      throws SQLException {
	    return rs.getInt(columnIndex);
	  }
	  @Override
	  public Integer getNullableResult(CallableStatement cs, int columnIndex)
	      throws SQLException {
	    return cs.getInt(columnIndex);
	  }
	}

从上面看到,每个类型的类型处理的实现都是比较简单的,关键是,为什么实用这个类型处理器,比如参数映射中,我们需要哪个 类型处理器呢?这个就是根据参数的实际类型找类型处理器,同理,返回值处理,可以是根据返回值类型或者resultMap的类型配置,找到具体的类型处理器,具体过程不去查看了,我们只看看类型处理器是怎么注册的,注册到哪里?

2、TypeHandler的注册
TypeHandler是注册到Configurn中的TypeHandlerRegistry实例中。

TypeHandlerRegistry的属性

      
        // JDBCType对应的处理器映射
        private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
        // java类型对应的处理器映射 (一个java类型可以注册多个jdbc类型,默认null的key注册的是默认的处理器)
        private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
        // 类型处理器clas和实例的映射
        private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();

实际注册方法

 private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
	    if (javaType != null) { 
	      Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
	      if (map == null) { // 创建子map
	        map = new HashMap<JdbcType, TypeHandler<?>>();
	        TYPE_HANDLER_MAP.put(javaType, map);
	      }
	      map.put(jdbcType, handler); // put进子map
	      if (reversePrimitiveMap.containsKey(javaType)) { // 同时注册元素类型
	        register(reversePrimitiveMap.get(javaType), jdbcType, handler);
	      }
	    } 
	    ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
	  }

3、mybaits类型处理器的实际使用。

 

END!

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值