MyBatis源码分析之基础支持层反射

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨
👀👀👀 个人博客:小奥的博客
👍👍👍:个人CSDN
⭐️⭐️⭐️:传送门
🍹 本人24应届生一枚,技术和水平有限,如果文章中有不正确的内容,欢迎多多指正!
📜 欢迎点赞收藏关注哟! ❤️

文章目录

    • 2.2 反射模块
      • ① 对象工厂子包
        • ObjectFactory接口
        • DefaultObjectFactory
      • ② 执行器子包
        • Invoker接口
        • MethodInvoker
        • SetFieldInvoker & GetFieldInvoker
      • ③ 属性子包
        • PropertyCopier
        • PropertyNamer
        • PropertyTokenizer
      • ④ 对象包装器子包
        • ObjectWrapperFactory
        • DefaultObjectWrapperFactory
        • ObjectWrapper
          • BaseWrapper
          • BeanWrapper
          • MapWrapper
          • CollectionWrapper
      • ⑤ 反射核心类
        • Reflector
        • ReflectorFactory
        • DefaultReflectorFactory
      • ⑥ 反射包装类
        • MetaObject
        • MetaClass
        • SystemMetaObject
      • ⑦ 异常拆包工具
        • ExceptionUtil
        • InvocationTargetException
        • UndeclaredThrowableException
      • ⑧ 参数名解析器
      • ⑨ 泛型解析器

2.2 反射模块

MyBatis在进行参数处理、结果集映射等操作时会使用到大量的反射操作,Java中的反射功能虽然强大,但是编码比较复杂且容易出错,为了简化反射相关的代码,MyBatis提供了专门的反射模块,对Java原生反射进行了封装,提供更加简单易用的api。

反射模块目录如下:

在这里插入图片描述

包路径:org.apache.ibatis.reflection

主要分析一下几个包中的类:

  • 对象工厂包。reflection包下的factory子包时一个对象工厂子包。该包中的类用来基于反射生产出各种对象。
  • 执行器包
  • 属性子包
  • 对象包装器包反射核心类
  • 反射包装类
  • 异常拆包工具
  • 参数名解析器
  • 泛型解析器

① 对象工厂子包

ObjectFactory接口

ObjectFactory接口是MyBatis对象创建工厂。其默认实现是DefaultObjectFactory通过构造器反射创建对象,支持无参构造和有参构造器。

/*** MyBatis uses an ObjectFactory to create all needed new Objects.* MyBatis使用ObjectFactory来创建所有需要的新对象。* @author Clinton Begin*/
public interface ObjectFactory {/*** Sets configuration properties.* 设置configuration属性。* @param properties*          configuration properties*/default void setProperties(Properties properties) {// NOP}/*** Creates a new object with default constructor.* 使用默认构造函数创建一个新对象* @param <T> the generic type* @param type Object type* @return the t*/<T> T create(Class<T> type);/*** Creates a new object with the specified constructor and params.* 用指定的构造函数和参数创建一个新对象。* @param <T> the generic type* @param type Object type* @param constructorArgTypes Constructor argument types* @param constructorArgs Constructor argument values* @return the t*/<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);/*** Returns true if this object can have a set of other objects. It's main purpose is to support* non-java.util.Collection objects like Scala collections.* 如果此对象可以有一组其它对象,则返回true。它的主要目的是支持非java.util.Collection对象比如Scala集合。* @param <T> the generic type* @param type Object type* @return whether it is a collection or not** @since 3.1.0*/<T> boolean isCollection(Class<T> type);}
DefaultObjectFactory

DefaultObjectFactory,实现了ObjectFactory工厂接口,用于创建Class类对象。

public class DefaultObjectFactory implements ObjectFactory, Serializable {private static final long serialVersionUID = -8855120656740914948L;@Overridepublic <T> T create(Class<T> type) {return create(type, null, null);}@SuppressWarnings("unchecked")@Overridepublic <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {Class<?> classToCreate = resolveInterface(type); // 获得需要创建的类// we know types are assignable 类型是可赋值的// 创建指定类的的对象return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);}/*** 创建类的实例* @param type 要创建实例的类* @param constructorArgTypes 构造方法传入参数类型* @param constructorArgs 构造方法输入参数* @return* @param <T> 实例类型*/private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {try {Constructor<T> constructor; // 构造方法// 参数类型列表为null或者参数列表为null,即通过无参构造方法,创建指定类的对象if (constructorArgTypes == null || constructorArgs == null) {constructor = type.getDeclaredConstructor(); // 获取无参构造函数try {return constructor.newInstance(); // 使用无参构造函数创建对象} catch (IllegalAccessException e) {if (Reflector.canControlMemberAccessible()) {// 如果发生异常,则修改构造函数的访问属性后再次尝试constructor.setAccessible(true);return constructor.newInstance();}throw e;}}// 根据输入参数类型查找对应的构造器,即通过特定构造方法,创建指定类的对象constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));try {// 采用有参构造函数创建实例return constructor.newInstance(constructorArgs.toArray(new Object[0]));} catch (IllegalAccessException e) {if (Reflector.canControlMemberAccessible()) {// 如果发生异常,则修改构造函数的访问属性后再次尝试constructor.setAccessible(true);return constructor.newInstance(constructorArgs.toArray(new Object[0]));}throw e;}} catch (Exception e) {// 收集所有的参数类型String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList).stream().map(Class::getSimpleName).collect(Collectors.joining(","));// 收集所有的参数String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList).stream().map(String::valueOf).collect(Collectors.joining(","));throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values ("+ argValues + "). Cause: " + e, e);}}protected Class<?> resolveInterface(Class<?> type) {Class<?> classToCreate;if (type == List.class || type == Collection.class || type == Iterable.class) {// 判断是否是集合,是则返回List对象classToCreate = ArrayList.class;} else if (type == Map.class) {classToCreate = HashMap.class; // Map类型返回HashMap对象} else if (type == SortedSet.class) { // issue #510 Collections SupportclassToCreate = TreeSet.class; // SortedSet类型返回TreeSet对象} else if (type == Set.class) {classToCreate = HashSet.class; // Set类型返回HashSet对象} else {// 如果不满足以上类型,直接返回原对象classToCreate = type;}return classToCreate;}@Overridepublic <T> boolean isCollection(Class<T> type) {return Collection.class.isAssignableFrom(type);}}

② 执行器子包

reflection包下的invoker子包是执行器子包,该子包中的类能够基于反射实现对象方法的调用和对象属性的读写。

Invoker接口

Invoker接口用于抽象设置和读取字段值的操作。对于有getter/setter方法的字段,通过MethodInvoker反射执行;对应其它字段,通过GetFieldInvokerSetFieldInvoker操作Feild对象的getter/setter方法反射执行。

public interface Invoker {// 执行Field或者MethodObject invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;// 返回属性相应的属性Class<?> getType();
}
  • invoker方法:即执行方法。该方法负责完成对象方法的调用和对象属性的读写。在三个实现类中,分别是属性读取操作、属性赋值操作、方法触发操作。
  • getType方法:用来获取类型。它对于 GetFieldInvoker和 SetFieldInvoker的含义也是明确的,即获得目标属性的类型。对于MethodInvoker则是直接返回type属性。

该接口有三个实现:

在这里插入图片描述

Invoker接口的三个实现类分别用来处理三种不同的情况:

  • GetFieldInvoker:负责对象属性的读操作
  • SetFieldInvoker:负责对象属性的写操作
  • MethodInvoker:负责对象其他方法的操作
MethodInvoker

Class类中属性对应set方法或者get方法的封装。

public class MethodInvoker implements Invoker {private final Class<?> type; // 类型private final Method method; // 指定方法public MethodInvoker(Method method) {this.method = method;// 获取属性类型if (method.getParameterTypes().length == 1) {// 如果方法有且只有一个输入参数,type为输入参数类型,即setter方法。type = method.getParameterTypes()[0];} else {// 否则,type为方法返回值的类型,即getter方法。type = method.getReturnType();}}@Overridepublic Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {try {return method.invoke(target, args); // 通过调用method.invoke()执行目标对象方法} catch (IllegalAccessException e) {if (Reflector.canControlMemberAccessible()) {method.setAccessible(true);return method.invoke(target, args);}throw e;}}@Overridepublic Class<?> getType() {return type;}
}

MethodInvoker在其构造函数中,设置set方法或者get方法,并获取其参数类型或者返回值类型进行保存。

SetFieldInvoker & GetFieldInvoker

没有getter/setter方法的属性,使用GetFieldInvokerSetFieldInvoker 封装了属性对应的Field对象,通过调用Field.get()/set()实现获取设置属性值。

public class GetFieldInvoker implements Invoker {private final Field field; // 属性对应的Field对象public GetFieldInvoker(Field field) {this.field = field;}/*** 代理方法,获取目标对象的属性值。* @param target 被代理的目标对象* @param args 方法的参数* @return 方法执行结果* @throws IllegalAccessException*/@Overridepublic Object invoke(Object target, Object[] args) throws IllegalAccessException {try {return field.get(target); // 直接通过反射获取目标属性值} catch (IllegalAccessException e) {if (Reflector.canControlMemberAccessible()) {// 如果无法访问,修改属性的访问属性field.setAccessible(true); // 将属性的可访问性修改为可访问return field.get(target); // 再次通过反射获取目标属性的值}throw e;}}// 获取属性类型@Overridepublic Class<?> getType() {return field.getType();}
}public class SetFieldInvoker implements Invoker {private final Field field; // 属性对应的Field对象public SetFieldInvoker(Field field) {this.field = field;}/*** 代理方法,设置目标对象的属性值* @param target 被代理的目标对象* @param args 方法的参数* @return* @throws IllegalAccessException*/@Overridepublic Object invoke(Object target, Object[] args) throws IllegalAccessException {try {field.set(target, args[0]); // 设置属性值} catch (IllegalAccessException e) {// 如果无法访问if (!Reflector.canControlMemberAccessible()) {// 如果不能访问控制成员,则抛出异常throw e;}// 如果可以,则修改属性的访问属性field.setAccessible(true);// 再次设置目标属性的值field.set(target, args[0]);}return null;}// 获取属性类型@Overridepublic Class<?> getType() {return field.getType();}
}

③ 属性子包

reflection包下的property子包是属性子包,该子包中的类用来完成与对象属性相关的操作。

  • PropertyCopier:主要用于两个对象之间的复制。
  • PropertyNamer:主要用于完成属性名和方法名之间的转化。
  • PropertyTokenizer:主要用于解析表达式,比如mapper.xml中的动态sql。
PropertyCopier

PropertyCopier作为属性复制器,就是用来解决上述问题的,借助于属性复制器PropertyCopier,我们可以方便地将一个对象属性复制到另一个对象中。

User user = new User(1L, "张三");
User copy = new User();
PropertyCopier.copyBeanProperties(user.getClass(), user, copy);输出:
User{id=1, name='张三'}
User{id=1, name='张三'}
public final class PropertyCopier {private PropertyCopier() {// Prevent Instantiation of Static Class 阻止静态类的实例化}/*** 完成对象的输出复制* @param type 对象的类型* @param sourceBean 提供属性值的对象* @param destinationBean 要被写入新属性值的对象*/public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {Class<?> parent = type; // 对象的类型while (parent != null) {final Field[] fields = parent.getDeclaredFields(); // 获取该类的所有属性,不包含继承属性// 循环遍历属性进行复制for (Field field : fields) {try {try {field.set(destinationBean, field.get(sourceBean));} catch (IllegalAccessException e) {// 如果无法访问if (!Reflector.canControlMemberAccessible()) {throw e; // 如果无法访问控制成员,则抛出异常}field.setAccessible(true); // 修改属性的可访问性field.set(destinationBean, field.get(sourceBean)); // 再次复制属性值}} catch (Exception e) {// Nothing useful to do, will only fail on final fields, which will be ignored.// 没有做任何有用的操作,只会在final字段失败,该字段将会被忽略。}}parent = parent.getSuperclass();}}
}

copyBeanProperties方法的工作原理十分简单:通过反射获取类的所有属性,然后依次将这些属性值从源对象复制出来并赋给目标对象。但是要注意,该属性复制器无法完成继承得来的属性的赋值,因为getDeclalaredFields方法返回的属性中不包含继承属性。

PropertyNamer

PropertyNamer提供属性名称相关的操作功能,例如,通过get、set方法的方法名找出对应的属性等。

要想让PropertyNamer正常的发挥作用,需要被整对象书对象、方法的命名遵循Java Bean的命名规范。

即:

  • 如果类成员变量名字是abc,那么该属性对应的读写方法分别命名为getAbc()setAbc()
  • 如果类的属性是boolean类型,则允许使用is代替上面的get,读方法命名为isAbc()
public final class PropertyNamer {private PropertyNamer() {// Prevent Instantiation of Static Class 阻止静态类实例化}/*** 通过方法名找出对应的属性* @param name 方法名* @return 属性名*/public static String methodToProperty(String name) {if (name.startsWith("is")) {name = name.substring(2); // 如果方法名以“is”开头,即boolean类型的属性,则去除“is”} else if (name.startsWith("get") || name.startsWith("set")) {name = name.substring(3); // 如果方法名以“get”或者“set”开头,则去除} else {// 否则,抛出异常throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");}if (name.length() == 1 || name.length() > 1 && !Character.isUpperCase(name.charAt(1))) {// 处理get、set方法的驼峰式命名,将首字母改为小写name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);}return name;}public static boolean isProperty(String name) {return isGetter(name) || isSetter(name);}public static boolean isGetter(String name) {// 判断是否是getter方法命名。条件:①以get开头并且字符串长度大于3 ②以is开头并且字符串长度大于2return name.startsWith("get") && name.length() > 3 || name.startsWith("is") && name.length() > 2;}public static boolean isSetter(String name) {// 判断是否是setter方法命名。条件:以set开头并且字符串长度大于3return name.startsWith("set") && name.length() > 3;}
}
PropertyTokenizer

PropertyTokenizer是一个属性标记器,用于解析属性表达式,传入一个形如student[sId].name的字符串后,该标记器会将其拆分开,放入各个属性中。拆分结束后各个属性的值如下代码注释所示:

  private String name; // studentprivate final String indexedName; // student[sId]private String index;  // sIdprivate final String children; // name
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {/*** 例如表达式 "student[sId].name",解析器的每个字段的值如下:*  name:student*  indexedName: student[sId]*  index:sId*  children:name*/private String name; // 当前属性名private final String indexedName; // 表示带索引的属性名,如果当前无索引,则该值和name相同private String index; // 表示索引下标private final String children; // 去除name外的子表达式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);}}// getter方法.../*** hasNext()、next()、remove() 实现自Iterator接口中的方法*/@Overridepublic boolean hasNext() {return children != null;}@Overridepublic PropertyTokenizer next() {return new PropertyTokenizer(children); // 创建下一个PropertyTokenizer对象}@Overridepublic void remove() {throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");}
}

④ 对象包装器子包

reflection包下的wrapper子包是对象包装器子包,该子包中的类使用装饰器模式对各种类型的对象(包括基本的Bean对象、集合对象、Map对象)进行进一步的封装,为其增加一些功能,使它们更易于使用。

在这里插入图片描述

ObjectWrapperFactory

ObjectWrapperFactory是对象包装器的工厂的接口,DefaultObjectWrapperFactory是它的默认实现。不过该默认实现中并没有实现任何功能。

MyBatis允许用户通过配置文件中的objectWrapperFactory节点来注入新的ObjectWrapperFactory。

/*** 一个对象包装器创建工厂的接口定义,负责将普通的Java对象包装成ObjectWrapper实例。* @author Clinton Begin*/
public interface ObjectWrapperFactory {boolean hasWrapperFor(Object object); // 是否拥有指定对象的装饰对象ObjectWrapper getWrapperFor(MetaObject metaObject, Object object); // 通过对象元数据获取指定对象的包装对象
}
DefaultObjectWrapperFactory

ObjectWrapperFactory对象包装器工厂接口的默认实现。

/*** 默认的对象包装器工厂* @author Clinton Begin*/
public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {@Overridepublic boolean hasWrapperFor(Object object) {return false;  // 是否用于指定对象的装饰对象,默认为false}@Overridepublic ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {// 通过对象元数据获取指定对象的包装对象,默认不允许获取,直接抛出异常。throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");}}
ObjectWrapper

ObjectWrapper接口是所有对象包装器的总接口,提供统一的包装对象的属性操作规范。

/*** 对象包装器接口定义,包装对象后提供统一的属性操作方法* @author Clinton Begin*/
public interface ObjectWrapper {Object get(PropertyTokenizer prop); // 获取被包装对象某个属性的值void set(PropertyTokenizer prop, Object value); // 设置被包装对象某个属性的值String findProperty(String name, boolean useCamelCaseMapping); // 查找属性名称String[] getGetterNames(); // 获取所有的属性get方法名称String[] getSetterNames(); // 获取所有的属性set方法名称Class<?> getSetterType(String name); // 获取指定属性的set方法的参数类型Class<?> getGetterType(String name); // 获取指定属性的get方法的返回类型boolean hasSetter(String name); // 是否有指定属性的set方法boolean hasGetter(String name); // 是否有指定属性的get方法/*** 实例化某个属性的值,并获取对应的MetaObject对象* @param name 完整的属性名* @param prop 属性名描述符* @param objectFactory 对象创建工厂* @return 指定的属性的值对应的MetaObject对象*/MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);boolean isCollection(); // 判断被包装的对象是否是集合void add(Object element); // 往被包装的集合对象中添加新的元素 <E> void addAll(List<E> element); // 往被包装的集合对象中添加一组元素
}

因为不同的Java对象,其属性操作方法也有所不同,所以MyBatis默认为ObjectWrapper提供了四个实现类:

  • BaseWrapper:抽象类,为子类BeanWrapper和MapWrapper提供公共的方法和属性。
  • BeanWrapper:普通对象的包装器实现类,处理User这样的实体类。
  • MapWrapper:是对Map接口子类的包装。
  • CollectionWrapper:是对集合对象的包装。

在这里插入图片描述

BaseWrapper

BaseWrapper是ObjectWrapper接口的抽象实现,用于定义公共逻辑的处理方法。使用了模板设计模式。

/*** 基础包装器,实现ObjectWrapper接口。* 为子类BeanWrapper和MapWrapper提供公共的方法和属性。* @author Clinton Begin*/
public abstract class BaseWrapper implements ObjectWrapper {protected static final Object[] NO_ARGUMENTS = {}; // 无参,主要用于执行get方法所需protected final MetaObject metaObject; // 被包装对象的元数据对象protected BaseWrapper(MetaObject metaObject) {this.metaObject = metaObject;}/*** 解析对象中的集合名* 根据属性表达式获取对应属性的集合(Array、List、Map)对象,调用MetaObject的getValue()方法获取。* @param prop PropertyTokenizer对象* @param object 指定Object对象* @return*/protected Object resolveCollection(PropertyTokenizer prop, Object object) {if ("".equals(prop.getName())) {// 如果表达式不合法解析不到属性名,则直接返回默认值return object;}// 解析到属性名,调用metaObject.getValue()方法获取属性值并返回return metaObject.getValue(prop.getName());}/*** 根据属性表达式获取集合(Array、List、Map)的值* @param prop  PropertyTokenizer对象* @param collection 集合(Array、List、Map)* @return 对应下标或key的值*/protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {if (collection instanceof Map) {return ((Map) collection).get(prop.getIndex()); // 如果是Map类型,则index为key}int i = Integer.parseInt(prop.getIndex());// 如果是其他类型,则index为下标if (collection instanceof List) {return ((List) collection).get(i);} else if (collection instanceof Object[]) {return ((Object[]) collection)[i];} else if (collection instanceof char[]) {return ((char[]) collection)[i];} else if (collection instanceof boolean[]) {return ((boolean[]) collection)[i];} else if (collection instanceof byte[]) {return ((byte[]) collection)[i];} else if (collection instanceof double[]) {return ((double[]) collection)[i];} else if (collection instanceof float[]) {return ((float[]) collection)[i];} else if (collection instanceof int[]) {return ((int[]) collection)[i];} else if (collection instanceof long[]) {return ((long[]) collection)[i];} else if (collection instanceof short[]) {return ((short[]) collection)[i];} else {// 不是集合类型,则抛出异常throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");}}/*** 根据(参数prop)设置集合(Array、List、Map)的值* @param prop* @param collection* @param value*/protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {if (collection instanceof Map) {((Map) collection).put(prop.getIndex(), value); // 如果是Map类型,则index为key} else {int i = Integer.parseInt(prop.getIndex());// 如果是其他类型,则index为下标if (collection instanceof List) {((List) collection).set(i, value);} else if (collection instanceof Object[]) {((Object[]) collection)[i] = value;} else if (collection instanceof char[]) {((char[]) collection)[i] = (Character) value;} else if (collection instanceof boolean[]) {((boolean[]) collection)[i] = (Boolean) value;} else if (collection instanceof byte[]) {((byte[]) collection)[i] = (Byte) value;} else if (collection instanceof double[]) {((double[]) collection)[i] = (Double) value;} else if (collection instanceof float[]) {((float[]) collection)[i] = (Float) value;} else if (collection instanceof int[]) {((int[]) collection)[i] = (Integer) value;} else if (collection instanceof long[]) {((long[]) collection)[i] = (Long) value;} else if (collection instanceof short[]) {((short[]) collection)[i] = (Short) value;} else {// 不是集合类型,则抛出异常throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");}}}
}
BeanWrapper
/*** 普通对象包装器* 继承BaseWrapper类,基于MetaClass实现Object的属性操作。* @author Clinton Begin*/
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());}/*** 获取被包装对象中对应表达式的属性值* @param prop 属性表达式,注意,该表达式不包含子表达式* @return** 如果表达式形如:`arr[0]/list[0]/map[key]`,则先获取对应属性`arr/list/map`对象的值,再获取索引对应元素的值。* 如果表达式不带索引,则传入的就是个属性名,调用`getBeanProperty`方法,获取属性对应的值。*/@Overridepublic Object get(PropertyTokenizer prop) {if (prop.getIndex() != null) {// 若存在索引信息,则表示该属性表达式中的name部分为集合属性// 通过BaseWrapper中的公共方法resolveCollection获取集合对象和集合属性Object collection = resolveCollection(prop, object);return getCollectionValue(prop, collection);}// 不存在索引信息,则name部分为普通对象,查找并调用Invoker相关方法获取属性return getBeanProperty(prop, object);}@Overridepublic void set(PropertyTokenizer prop, Object value) {if (prop.getIndex() != null) {// 若存在索引信息,则表示该属性表达式中的name部分为集合属性// 通过BaseWrapper中的公共方法resolveCollection获取集合对象和集合属性Object collection = resolveCollection(prop, object);setCollectionValue(prop, collection, value);} else {// 不存在索引信息,则name部分为普通对象,查找并调用Invoker相关方法设置属性setBeanProperty(prop, object, value);}}@Overridepublic String findProperty(String name, boolean useCamelCaseMapping) {return metaClass.findProperty(name, useCamelCaseMapping);}@Overridepublic String[] getGetterNames() {return metaClass.getGetterNames();}@Overridepublic String[] getSetterNames() {return metaClass.getSetterNames();}@Overridepublic Class<?> getSetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name); // 解析表达式if (!prop.hasNext()) {// 不存在子表达式,直接调用metaClass.getSetterType()方法获取属性类型// 这里之所以是使用metaClass.getSetterType(name)而不是metaValue.getSetterType(name)// 是因为metaValue.getSetterType也是依赖objectWrapper.getSetterType,如果还是调用// metaValue.getSetterType会陷入无限递归,metaClass才是递归的出口return metaClass.getSetterType(name);}// 创建MetaObject对象// 为什么优先使用MetaObject?而当且仅当封装的对象为空时,才使用MetaClass对象呢?// MetaClass封装的是类的元信息,MetaObject封装的是对象的元信息,可以将类的元信息看成是对象元信息的一个子集。// 根据类元信息得到的一些类型信息可能更加具体。MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {// 如果metaValue为SystemMetaObject.NULL_META_OBJECT,表示封装的Java对象值为null// 通过类元信息,获取set方法对应属性类型return metaClass.getSetterType(name);} else {// 当对象不为空时,通过对象元信息,获取set方法对应属性类型,可以获得更具体的类型信息// 递归判断子表达式的children。然后返回。return metaValue.getSetterType(prop.getChildren());}}/*** 获取属性表达式对应的属性的get方法的返回类型* @param name* @return*/@Overridepublic Class<?> getGetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (!prop.hasNext()) {return metaClass.getGetterType(name);}MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return metaClass.getGetterType(name);} else {return metaValue.getGetterType(prop.getChildren());}}/*** 是否存在属性表达式对应的属性的set方法* @param name* @return*/@Overridepublic boolean hasSetter(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (!prop.hasNext()) {return metaClass.hasSetter(name);}if (metaClass.hasSetter(prop.getIndexedName())) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return metaClass.hasSetter(name);} else {return metaValue.hasSetter(prop.getChildren());}} else {return false;}}/*** 是否存在表达式对应的属性的get方法 * @param name* @return*/@Overridepublic boolean hasGetter(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (!prop.hasNext()) {return metaClass.hasGetter(name);}if (metaClass.hasGetter(prop.getIndexedName())) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return metaClass.hasGetter(name);} else {return metaValue.hasGetter(prop.getChildren());}} else {return false;}}/*** 为表达式指定的属性创建对应的MetaObject对象* @param name 完整的属性名* @param prop 属性名描述符* @param objectFactory 对象创建工厂* @return*/@Overridepublic MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {MetaObject metaValue;Class<?> type = getSetterType(prop.getName()); // 获取属性表达式指定属性的类型 try {Object newObject = objectFactory.create(type); // 创建对应的属性对象 // 创建属性对应的MetaObject对象metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(),metaObject.getReflectorFactory());set(prop, newObject); // 为属性所属对象设置对应的属性值} catch (Exception e) {throw new ReflectionException("Cannot set value of property '" + name + "' because '" + name+ "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + e.toString(), e);}return metaValue;}private Object getBeanProperty(PropertyTokenizer prop, Object object) {try {// 得到获取属性对应的Invoker对象Invoker method = metaClass.getGetInvoker(prop.getName());try {return method.invoke(object, NO_ARGUMENTS); // 通过Invoker封装的反射操作获取属性值} 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);}}private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {try {Invoker method = metaClass.getSetInvoker(prop.getName());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);}}@Overridepublic boolean isCollection() {return false;}@Overridepublic void add(Object element) {throw new UnsupportedOperationException();}@Overridepublic <E> void addAll(List<E> list) {throw new UnsupportedOperationException();}
}
MapWrapper
/*** Map对象包装器* 继承BaseWrapper类,基于Map接口方法实现对属性的操作。* @author Clinton Begin*/
public class MapWrapper extends BaseWrapper {private final Map<String, Object> map; // 封装的Map对象public MapWrapper(MetaObject metaObject, Map<String, Object> map) {super(metaObject);this.map = map;}@Overridepublic Object get(PropertyTokenizer prop) {if (prop.getIndex() != null) {// 存在索引信息,则表示该属性表达式中的name部分为集合属性Object collection = resolveCollection(prop, map);return getCollectionValue(prop, collection);}// 不存在索引信息,则name部分为普通对象,直接从map中获取值return map.get(prop.getName());}@Overridepublic void set(PropertyTokenizer prop, Object value) {if (prop.getIndex() != null) {// 存在索引信息,则表示该属性表达式中的name部分为集合属性Object collection = resolveCollection(prop, map);setCollectionValue(prop, collection, value);} else {// 不存在索引信息,则name部分为普通对象,直接从map中设置值map.put(prop.getName(), value);}}@Overridepublic String findProperty(String name, boolean useCamelCaseMapping) {return name;}@Overridepublic String[] getGetterNames() {return map.keySet().toArray(new String[0]);}@Overridepublic String[] getSetterNames() {return map.keySet().toArray(new String[0]);}@Overridepublic Class<?> getSetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name); // 根据属性表达式创建PropertyTokenizer对象if (prop.hasNext()) {// 如果存在子表达式,根据indexedName创建MetaObject对象MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return Object.class; // 如果对应的属性为null,直接返回Object类型} else {// 否则子表达式由MetaObject处理return metaValue.getSetterType(prop.getChildren());}}// 没有子表达式,直接map操作if (map.get(name) != null) {return map.get(name).getClass();} else {return Object.class;}}@Overridepublic Class<?> getGetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return Object.class;} else {return metaValue.getGetterType(prop.getChildren());}}if (map.get(name) != null) {return map.get(name).getClass();} else {return Object.class;}}@Overridepublic boolean hasSetter(String name) {return true;}@Overridepublic boolean hasGetter(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (!prop.hasNext()) {return map.containsKey(prop.getName());}if (map.containsKey(prop.getIndexedName())) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return true;} else {return metaValue.hasGetter(prop.getChildren());}} else {return false;}}/*** 针对嵌套属性的场景* @param name 完整的属性名* @param prop 属性名描述符* @param objectFactory 对象创建工厂* @return** 如:person.name 首次设置person会创建一个key为person,value为new HashMap<>()*/@Overridepublic MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {HashMap<String, Object> map = new HashMap<>();set(prop, map);return MetaObject.forObject(map, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(),metaObject.getReflectorFactory());}@Overridepublic boolean isCollection() {return false;}@Overridepublic void add(Object element) {throw new UnsupportedOperationException();}@Overridepublic <E> void addAll(List<E> element) {throw new UnsupportedOperationException();}}
CollectionWrapper

集合包装器源码比较简单,这里不再介绍。

⑤ 反射核心类

reflection包中最为核心的类就是Reflector类。

在这里插入图片描述

Reflector

MyBatis提供Reflector类来缓存类的字段名和getter/setter方法的元信息,使得涉及反射的操作不再去获取这些元信息,使操作更加便捷,使用方式是将原始类对象Class传入其构造方法,生成Reflector对象。

Reflector将一个类反射解析后,会将该类的属性、方法等一一归类放到各个属性集合中。

public class Reflector {private static final MethodHandle isRecordMethodHandle = getIsRecordMethodHandle();private final Class<?> type; // 对应的class类型private final String[] readablePropertyNames; // 可读属性的名称集合,可读属性就是存在get方法的属性,初始值为nullprivate final String[] writablePropertyNames; // 可写属性的名称集合,可写属性就是存在set方法的属性,初始值为nullprivate final Map<String, Invoker> setMethods = new HashMap<>(); // 属性对应的set方法,key是属性名称,value是对应的set方法private final Map<String, Invoker> getMethods = new HashMap<>(); // 属性对应的get方法,key是属性名称,value是对应的get方法// set方法输入类型。key是属性名,值为对应的该属性的set方法的类型(实际为set方法的第一个参数的类型)private final Map<String, Class<?>> setTypes = new HashMap<>();// get方法输出类型。key是属性名,值为对应的该属性的get方法的类型(实际为get方法的返回值类型)private final Map<String, Class<?>> getTypes = new HashMap<>();private Constructor<?> defaultConstructor; // 默认的构造方法private final Map<String, String> caseInsensitivePropertyMap = new HashMap<>(); // 所有属性名称的集合,key为大写的属性名称,value为属性名称/*** 解析在指定的Class类型,并填充上述的集合的属性,即初始化相关字段* @param clazz*/public Reflector(Class<?> clazz) {type = clazz; // 初始化type字段addDefaultConstructor(clazz); // 设置默认的构造方法,默认无参,具体实现是通过反射遍历所有的构造方法Method[] classMethods = getClassMethods(clazz); // 获取类的所有方法// 判断Class是不是record类型if (isRecord(type)) {addRecordGetMethods(classMethods);} else {addGetMethods(classMethods); // 处理所有方法中的getter方法,填充getMethods集合和getTypes接口addSetMethods(classMethods); // 处理所有方法中的setter方法,填充setMethods集合和setTypes接口addFields(clazz); // 处理没有getter和setter方法的字段}// 初始化为空数组readablePropertyNames = getMethods.keySet().toArray(new String[0]);writablePropertyNames = setMethods.keySet().toArray(new String[0]);// 初始化caseInsensitivePropertyMap集合,其中记录了所有大写格式的属性名称for (String propName : readablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}for (String propName : writablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}}...
}

从上述代码可以看到Reflector在初始化的时候会通过反射机制进行解析该类,整个解析的过程并不复杂。

  • Reflector的构造函数中,会解析指定的Class对象,并填充上述集合。
  • Reflector.addGetMethods()方法主要解析类中定义的getter方法
  • Reflector.addSetMethods()方法主要解析类中定义的Setter方法。

下面以addGetMethods()方法为例进行介绍。

private void addGetMethods(Method[] methods) {Map<String, List<Method>> conflictingGetters = new HashMap<>();// 使用Java8的stream从所有方法中找出符合以下条件的方法:(1)方法参数长度为0,即无参方法 (2)符合getter方法命名规范(以get开头且方法名长度大于3 或者 以is开头且方法名长度大于2)Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName())).forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m)); // 记录方法参数类型、返回值等信息resolveGetterConflicts(conflictingGetters); // 处理重复方法名。注意:一个key会有多个method的原因是:当子类覆盖了父类的getter方法并且返回值发生变化时,会产生两个签名不同的方法。}

addGetMethods方法主要用于处理clazz的所有方法中的getter方法,并填充getMethods集合和getTypes集合。

  • addMethodConflict()方法,主要用来获取字段名和字段名对应的getter方法的映射集合,并把结果保存到conflictingGetters变量中;
  • resolveGetterConflicts()方法,主要用来处理方法名重复的方法。

下面简单看一下 resolveGetterConflicts()方法是如何去重的:

private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {Method winner = null;String propName = entry.getKey(); // 属性名boolean isAmbiguous = false;for (Method candidate : entry.getValue()) {if (winner == null) {winner = candidate;continue;}// 字段对应多个get方法 Class<?> winnerType = winner.getReturnType();Class<?> candidateType = candidate.getReturnType();if (candidateType.equals(winnerType)) { // 返回值类型相同if (!boolean.class.equals(candidateType)) {isAmbiguous = true;break;}if (candidate.getName().startsWith("is")) {winner = candidate; // 返回值为boolean的get方法可能有多个,如getIsSave()和isSave(),优先使用is开头的、更规范的方法}} else if (candidateType.isAssignableFrom(winnerType)) {// OK getter type is descendant getter类型是后代,比如接口中方法的返回值是List类型,子类实现方法的返回值是ArrayList,这时会使用子类返回的方法。} else if (winnerType.isAssignableFrom(candidateType)) {winner = candidate;} else {isAmbiguous = true;break;}}addGetMethod(propName, winner, isAmbiguous); // 记录字段名对应的get方法对象和返回值类型}
}

去重的方式是使用更加规范的方法以及使用子类的返回值类型的方法。在确认字段名对应的唯一 getter/setter 方法后,记录方法名对应的方法、参数、返回值等信息。

另外,Reflector中还提供了一些公共的API方法,如下表格:

方法名称作用
getType获取Reflector表示的Class
getDefaultConstructor获取默认的构造器
hasDefaultConstructor判断是否有默认的构造器
getSetInvoker根据属性名称获取对应的Invoker 对象
getGetInvoker根据属性名称获取对应的Invoker对象
getSetterType获取属性对应的类型 比如:
String name; // getSetterType(“name”) --> java.lang.String
getGetterType获取属性对应的类型
getGetablePropertyNames获取所有的可读属性名称的集合
getSetablePropertyNames获取所有的可写属性名称的集合
hasSetter判断是否具有某个可写的属性
hasGetter判断是否具有某个可读的属性
findPropertyName根据名称查找属性
ReflectorFactory

ReflectorFactory是Reflector的工厂接口,而DefaultReflectorFactory是该工厂接口的默认实现。

ReflectorFactory接口主要实现了对Reflector对象的创建和缓存

public interface ReflectorFactory {boolean isClassCacheEnabled(); // 检测该ReflectorFactory是否缓存了Reflector对象void setClassCacheEnabled(boolean classCacheEnabled); // 设置是否缓存Reflector对象Reflector findForClass(Class<?> type); // 创建指定了Class的Reflector对象
}

具体是实现类图如下:

在这里插入图片描述

MyBatis只为该接口提供了DefaultReflectorFactory这一个实现类。

DefaultReflectorFactory

代码比较简单,主要是根据Class对象创建Reflector对象。

public class DefaultReflectorFactory implements ReflectorFactory {private boolean classCacheEnabled = true; // 该字段决定是否开启对Reflector对象的缓存private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>(); // 目标类和反射器映射缓存public DefaultReflectorFactory() {}@Overridepublic boolean isClassCacheEnabled() {return classCacheEnabled;}@Overridepublic void setClassCacheEnabled(boolean classCacheEnabled) {this.classCacheEnabled = classCacheEnabled;}@Overridepublic Reflector findForClass(Class<?> type) {if (classCacheEnabled) { // classCacheEnabled默认为true// synchronized (type) removed see issue #461// 从缓存中获取Reflector对象,如果缓存为空,则创建一个新的实例,放入缓存中return MapUtil.computeIfAbsent(reflectorMap, type, Reflector::new);}return new Reflector(type); // 创建一个新的Reflector实例}}

⑥ 反射包装类

reflection包中存在许多的包装类,它们使用装饰器模式将许多反射相关的类包装的更加简单易用。

下面介绍两个更为基础的包装类:MetaClass类和MetaObject类。

MetaObject

MetaObject被称为元对象,是一个针对普通Object对象的反射包装类。

public class MetaObject {private final Object originalObject; // 被包装的对象的原始对象private final ObjectWrapper objectWrapper; // 原始对象的包装器private final ObjectFactory objectFactory; // 实例化对象的工厂private final ObjectWrapperFactory objectWrapperFactory; // 创建对象包装器的工厂private final ReflectorFactory reflectorFactory; // 创建Reflector对象的反射工厂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);}}...
}
MetaClass

Class类的元数据,包装Reflector,基于PropertyTokenizer(分词器)提供对Class类的元数据各种操作,可以理解成对Reflector操作的进一步增强

其中包含了ReflectorFactory和Reflector两个字段,通过PropertyTokenizer分词器提供了获取属性的名称和返回值类型等等方法,也就是在Reflector上面新增了一些方法。

在Reflector中可以针对对普通属性的操作,但是如果出现了比较复杂的属性,比如private Person person;这种,我们要查找的表达式person.userName。针对这种表达式的处理,我们就可以使用MetaClass来处理了。

public class MetaClass {private final ReflectorFactory reflectorFactory; // Reflector的工厂类,具有缓存Reflector对象的功能private final Reflector reflector; // 反射器,用于解析和存储目标类中的元信息。创建MetaClass时,会指定一个Class reflector会记录该类的相关信息private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {this.reflectorFactory = reflectorFactory;this.reflector = reflectorFactory.findForClass(type); // <settings>标签解析 根据类型创建Reflector}public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {return new MetaClass(type, reflectorFactory); // <settings>标签解析 调用构造方法}...
}  
SystemMetaObject

SystemMetaObject系统级的MetaObject,限定了一些默认值。

public final class SystemMetaObject {// DefaultObjectFactory的单例public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();// DefaultObjectWrapperFactory的单例public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();// 空对象的MetaObject对象单例public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(new NullObject(), DEFAULT_OBJECT_FACTORY,DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());private SystemMetaObject() {// Prevent Instantiation of Static Class}private static class NullObject {}/*** 创建MetaObject对象* @param object 指定对象* @return MetaObject对象*/public static MetaObject forObject(Object object) {return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());}
}

⑦ 异常拆包工具

ExceptionUtil

ExceptionUtil是一个异常工具类,它提供一个拆包异常的工具方法unwrapThrowable。

该方法将InvocationTargetException和UndeclaredThrowableException这两类异常进行拆包,得到其中包含的真正的异常。

public class ExceptionUtil {private ExceptionUtil() {// Prevent Instantiation}/*** 拆解InvocationTargetException和UndeclaredThrowableException异常的包装,从而得到被包装的真正异常。* @param wrapped 包装后的异常 * @return 拆解出的被包装异常 */public static Throwable unwrapThrowable(Throwable wrapped) {Throwable unwrapped = wrapped; // 存放拆包得到的异常 while (true) {if (unwrapped instanceof InvocationTargetException) {// 拆包获得内部异常 unwrapped = ((InvocationTargetException) unwrapped).getTargetException();} else if (unwrapped instanceof UndeclaredThrowableException) {// 拆包获得内部异常unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();} else {return unwrapped; // 其他异常无需拆包}}}
}

unwrapThrowable方法的结构非常简单。但是我们需要思考一下,为什么单独给这两个类拆包呢?

下面我们看一看InvocationTargetException和UndeclaredThrowableException的源码。

InvocationTargetException为必检异常,UndeclaredThrowableException为免检的运行时异常。它们都不需要MyBatis,而是来自java.lang.reflect包。

InvocationTargetException

反射操作中,代理类通过反射调用目标类的方法时,目标类的方法可能抛出异常。发射可以调用各种目标方法,因此目标方法抛出的异常是多种多样无法确定的。这意味着反射操作可能抛出一个任意类型的异常。可以使用Throwable去接收这个异常,但这无疑太过宽泛。

InvocationTargetException就是为解决这个问题而设计的,当反射操作的目标方法中出现异常时,都统一包装成一个必检异常InvocationTargetException。

InvocationTargetException内部的target则保存了原始的异常。这样一来,使得反射操作中的异常更加容易管理。

public class InvocationTargetException extends ReflectiveOperationException {private static final long serialVersionUID = 4085088731926701167L;private Throwable target; // 用来保存被包装的异常protected InvocationTargetException() {super((Throwable)null);  // Disallow initCause}/*** 构造方法* @param target 被包装的异常 * @return 异常的详细信息 */public InvocationTargetException(Throwable target) {super((Throwable)null);  // Disallow initCausethis.target = target;}...
}
UndeclaredThrowableException

根据Java的继承原则,我们知道:如果子类中要重写父类中的方法,那么子类方法中抛出的必检异常必须是父类方法中声明过的类型。

在建立目标类的代理类时,通常时建立了目标类接口的子类或者目标类的子类。因此,将Java的继承原则放在代理类和被代理类上可以演化为:

  • 如果代理类和被代理类实现了共同的接口,则代理方法中抛出的必检异常必须是在共同接口中声明过的;
  • 如果代理类是被代理类的子类,则代理类方法中抛出的必检异常必须是在被代理类的方法中声明过的。

可是在代理类中难免会在执行某些方法时抛出一些共同接口或者父类方法中没有声明的必检异常。那该怎么解决呢?

  • 如果不抛出,但是它是必检异常,必须抛出;
  • 如果抛出,则父接口或者父类中没有声明该必检异常,不能抛出。

答案就是这些必检异常会被包装为免检异常 UndeclaredThrowabl eException 后抛出。

public class UndeclaredThrowableException extends RuntimeException {static final long serialVersionUID = 330127114055056639L;private Throwable undeclaredThrowable; // 被包装的必检异常public UndeclaredThrowableException(Throwable undeclaredThrowable) {super((Throwable) null);  // Disallow initCausethis.undeclaredThrowable = undeclaredThrowable;}/*** 构造方法 * @param   undeclaredThrowable  被包装的必检异常* @param   s 异常的详细信息*/public UndeclaredThrowableException(Throwable undeclaredThrowable,String s){super(s, null);  // Disallow initCausethis.undeclaredThrowable = undeclaredThrowable;}...
}

总之,InvocationTargetException 和 UndeclaredThrowableExc eption 这两个类都是异常包装类,需要拆包后才能得到真正的异常类。而 ExceptionUtil的 unwrapThrowable方法就可以完成该拆包工 作。

⑧ 参数名解析器

ParamNameResolver 是一个参数名解析器,用来按顺序列出方法 中的虚参,并对实参进行名称标注。

/*** 参数名解析器*/
public class ParamNameResolver {public static final String GENERIC_NAME_PREFIX = "param"; // 自动生成的参数名前缀private final boolean useActualParamName; // 是否使用实际的参数名(通过反射获取)/*** key是索引,value是参数名称。如果指定@Param,则从@Param中获取。当不指定时,将使用参数索引。* 请注意:当方法具有特殊参数RowBounds或者ResultHandler时,该索引可能与实际索引不同。*/// 方法输入参数的参数次序表。key为参数次序,value为参数名称或者参数@Param注解的值private final SortedMap<Integer, String> names;// 该方法输入参数中是否还有@Param注解private boolean hasParamAnnotation;/*** 构造方法* 将目标方法的参数名依次列举出来。在列举的过程中,如果某个参数存在@Param注解,则会用注解的value替换参数名。* (1)优先获取@Param注解的value* (2)通过反射获取参数名* (3)使用参数下标* @param config* @param method*/public ParamNameResolver(Configuration config, Method method) {// 从配置对象中获取:是否使用实际参数名称 <setting name="useActualParamName" value="true" /> 配置this.useActualParamName = config.isUseActualParamName();// 反射获取方法参数类型final Class<?>[] paramTypes = method.getParameterTypes();// 反射获取参数注解final Annotation[][] paramAnnotations = method.getParameterAnnotations();// key为参数次序,value为参数名称,存放参数的map容器final SortedMap<Integer, String> map = new TreeMap<>();int paramCount = paramAnnotations.length;// get names from @Param annotationsfor (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {if (isSpecialParameter(paramTypes[paramIndex])) {// skip special parameters 跳过特殊参数/*** 跳过RowBounds和ResultHandler参数,这两个参数不做解析* RowBounds:处理分页 ResultHandler:处理结果*/continue;}String name = null;for (Annotation annotation : paramAnnotations[paramIndex]) {if (annotation instanceof Param) {hasParamAnnotation = true;// 如果加了@Param注解,则使用value中指定的值name = ((Param) annotation).value();break;}}if (name == null) {// @Param was not specified. 没有使用@Param驻俄籍if (useActualParamName) {// 如果使用实际的参数名,则通过反射获取参数名// JDK8编译类加 -parameters参数可以保留参数名称,否则得到的是arg0,arg1这种无意义的参数名name = getActualParamName(method, paramIndex);}if (name == null) {// use the parameter index as the name ("0", "1", ...)// gcode issue #71// 如果名称还是为null,则可以使用下标来获取参数:#{param1}, #{param2}..name = String.valueOf(map.size());}}map.put(paramIndex, name);}names = Collections.unmodifiableSortedMap(map); // 使其不可变}private String getActualParamName(Method method, int paramIndex) {return ParamNameUtil.getParamNames(method).get(paramIndex);}private static boolean isSpecialParameter(Class<?> clazz) {return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);}public String[] getNames() {return names.values().toArray(new String[0]);}/**** 获取参数名对应的参数值* 一般是Map结构,当参数只有1个时,直接返回,xml中写任意值都可以匹配到。*/public Object getNamedParams(Object[] args) {final int paramCount = names.size(); // 参数个数if (args == null || paramCount == 0) {return null; // 无参情况直接返回null}if (!hasParamAnnotation && paramCount == 1) {// 没有@Param注解且参数只有一个Object value = args[names.firstKey()];// 如果参数是集合类型,参数名会封装成 collection/list/arrayreturn wrapToMapIfCollection(value, useActualParamName ? names.get(names.firstKey()) : null);} else {final Map<String, Object> param = new ParamMap<>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {// 参数名对应实参值param.put(entry.getValue(), args[entry.getKey()]);// add generic param names (param1, param2, ...) 额外自动生成一个参数名映射:param1, param2...final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);// ensure not to overwrite parameter named with @Param 确保不要覆盖以@Param命名的参数if (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;}}/*** Wrap to a {@link ParamMap} if object is {@link Collection} or array.* 如果对象是Collection或者数组,则包装为ParamMap。* 如果是集合,通过Collection访问;如果是List,通过List访问,如果是数组,通过Array访问。否则,直接返回。*/public static Object wrapToMapIfCollection(Object object, String actualParamName) {if (object instanceof Collection) {ParamMap<Object> map = new ParamMap<>();map.put("collection", object);if (object instanceof List) {map.put("list", object);}Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));return map;}if (object != null && object.getClass().isArray()) {ParamMap<Object> map = new ParamMap<>();map.put("array", object);Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));return map;}return object;}}
  • 构造方法 ParamNameResolver能够将目标方法的参数名称依次列 举出来。在列举的过程中,如果某个参数存在@Param注解,则会用注 解的 value值替换参数名。
  • 而 getNamedParams方法是在构造方法确定的 names属性和 hasPa ramAnnotation属性值的基础上,给出实参的参数名。

⑨ 泛型解析器

TypeParameterResolver类的功能是帮助MyBatis推断出属性、返回值、输入参数中泛型的具体类型。

它对外提供了三个方法:

  • resolveFieldType:解析属性的泛型
  • resolveReturnType:解析方法返回值的泛型
  • resolveParamTypes:解析方法输入参数的泛型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/719532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vision Pro开发者学习路线

官方给到的Vision Pro开发者学习路线&#xff1a; 1. 学习基础知识&#xff1a; - 学习 Xcode、Swift 和 SwiftUI 的基础知识&#xff0c;包括语法、UI 设计等。 - 掌握 ARKit 和 SwiftUI 的使用&#xff0c;了解如何创建沉浸式增强现实体验。 2. 学习 3D 建模&#xf…

『Linux从入门到精通』第 ㉕ 期 - System V 共享内存

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f427;共享内存原理&#x1f427;共享内存相关函数&#x1f426;key 与 shmid 区别 &#x1f427;代码实例 &#x1f490;专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0…

YOLOv9独家原创改进|加入幽灵卷积Ghost Convolution模块,轻量化!

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、论文摘要 由于内存和计算资源有限&#xff0c;在嵌入式设备上部署卷积神经网络是困难的。特征图中的冗余是那些成功的细胞神经网络的一个重要特征…

【网站项目】158企业人事管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

在Linux以命令行方式(静默方式/非图形化方式)安装MATLAB(正版)

1.根据教程&#xff0c;下载windows版本matlab&#xff0c;打开图形化界面&#xff0c;选择linux版本的只下载不安装 2.获取安装文件夹 3.获取许可证 4.安装 &#xff08;1&#xff09;跳过引用文章的2.2章节 &#xff08;2&#xff09;本文的安装文件夹代替引用文章的解压IS…

Java进阶(锁)——锁的升级,synchronized与lock锁区别

目录 引出Java中锁升级synchronized与lock锁区别 缓存三兄弟&#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出 Java进阶&#xff08;锁&#xff09;——锁的升级&#xff0c;synchronized与lock锁区别 Java中锁升级 看一段代码&#xff1a; public class…

Fastwhisper + Pyannote 实现 ASR + 说话者识别

文章目录 前言一、faster-whisper简单介绍二、pyannote.audio介绍三、faster-whisper pyannote.audio 实现语者识别四、多说几句 前言 最近在研究ASR相关的业务&#xff0c;也是调研了不少模型&#xff0c;踩了不少坑&#xff0c;ASR这块&#xff0c;目前中文普通话效果最好的…

Scrapy与分布式开发(1.1):课程导学

Scrapy与分布式开发&#xff1a;从入门到精通&#xff0c;打造高效爬虫系统 课程大纲 在这个专栏中&#xff0c;我们将一起探索Scrapy框架的魅力&#xff0c;以及如何通过Scrapy-Redis实现分布式爬虫的开发。在本课程导学中&#xff0c;我们将为您简要介绍课程的学习目标、内容…

Verilog Coding Styles For Improved Simulation Efficiency论文学习记录

原文基于Verilog-XL仿真器&#xff0c;测试了以下几种方式对仿真效率的影响。 1. 使用 Case 语句而不是 if / else if 语句 八选一多路选择器 case 实现效率比 if / else if 提升 6% 。 2. 如果可以尽量不使用 begin end 语句 使用 begin end 的 ff 触发器比不使用 begin end …

深度相机xyz点云文件三维坐标和jpg图像文件二维坐标的相互变换函数

深度相机同时拍摄xyz点云文件和jpg图像文件。xyz文件里面包含三维坐标[x,y,z]和jpg图像文件包含二维坐标[x&#xff0c;y],但是不能直接进行变换&#xff0c;需要一定的步骤来推演。 下面函数是通过box二维框[xmin, ymin, xmax, ymax, _, _ ]去截取xyz文件中对应box里面的点云…

MyCAT学习——在openEuler22.03中安装MyCAT2(网盘下载版)

准备工作 因为MyCAT 2基于JDK 1.8开发。也需要在虚拟机中安装JDK&#xff08;JDK官网就能下载&#xff0c;我这提供一个捷径&#xff09; jdk-8u401-linux-x64.rpmhttps://pan.baidu.com/s/1ywcDsxYOmfZONpmH9oDjfw?pwdrhel下载对应的tar安装包,以及对应的jar包 安装程序包…

九州金榜|孩子厌学要怎么办?

孩子从小学到初中再到高中&#xff0c;孩子出现厌学情绪很正常&#xff0c;但是孩子出现厌学情绪后&#xff0c;就必然会影响到孩子学习成绩&#xff0c;孩子产生厌学情绪的原因有哪些呢&#xff1f;只有找准孩子厌学原因才能去帮助孩子怎样去克服孩子厌学情绪&#xff0c;下面…

day34贪心算法 part03

1005. K 次取反后最大化的数组和 简单 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数…

OSError: [WinError 1455] 页面文件太小,无法完成操作。

[问题描述]&#xff1a;OSError: [WinError 1455] 页面文件太小&#xff0c;无法完成操作。 原因1&#xff1a;线程数太大 方法&#xff1a;改小线程&#xff08;workers&#xff09;数。 原因2&#xff1a;虚拟内存太小或为0&#xff0c;调大虚拟内存。 方法&#xff1a;右键…

Vue.js 实用技巧:深入理解 Vue.mixin

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【Linux进程间通信】共享内存

【Linux进程间通信】共享内存 目录 【Linux进程间通信】共享内存system V共享内存共享内存示意图共享内存的数据结构共享内存函数将共享内存挂接到对应的进程将共享内存取消挂接释放共享内存 共享内存的特性共享内存扩展共享内存配合管道进行使用 作者&#xff1a;爱写代码的刚…

用docker部署后端项目

一、搭建局域网 1.1、介绍前后端项目搭建 需要4台服务器&#xff0c;在同一个局域网中 1.2、操作 # 搭建net-ry局域网&#xff0c;用于部署若依项目 net-ry&#xff1a;名字 docker network create net-ry --subnet172.68.0.0/16 --gateway172.68.0.1#查看 docker network ls…

3d模型合并后一片漆黑是什么原因,怎么解决---模大狮模型网

当合并多个3D模型后&#xff0c;发现整个合并后的模型显示为一片漆黑通常是由以下几个可能的原因导致的&#xff1a; 材质设置问题&#xff1a;合并后的模型可能存在材质设置错误&#xff0c;导致模型无法正确显示。检查每个模型的材质属性&#xff0c;确保其正确设置&#xff…

老隋蓝海项目有哪些?能赚钱吗?

在创业的海洋中&#xff0c;每个人都渴望找到那片属于自己的“蓝海”&#xff0c;而“老隋蓝海项目”便是许多人心中的那片未知海域。那么&#xff0c;老隋蓝海项目究竟是指什么?它们又能否成为创业者的新财富之源? 蓝海项目的定义 我们要明白&#xff0c;蓝海项目通常指的是…

【漏洞复现】某厂商明御WEB应用防火墙任意用户登录漏洞

Nx01 产品简介 安恒明御WEB应用防火墙&#xff08;简称WAF&#xff09;是杭州安恒信息技术股份有限公司自主研发的一款专业应用安全防护产品&#xff0c;专注于为网站、APP等Web业务系统提供安全防护。 Nx02 漏洞描述 安恒明御WEB应用防火墙report.php文件存在硬编码设置的Con…