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.pswp.cn/news/719532.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/719532.shtml
英文地址,請注明出處:http://en.pswp.cn/news/719532.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PowerShell禁止運行腳本解決方案

錯誤代碼 CategoryInfo : SecurityError: (:) []&#xff0c;ParentContainsErrorRecordException FullyQualifiedErrorId : UnauthorizedAccess在計算機上啟動 Windows PowerShell 時&#xff0c;執行策略很可能是 Restricted&#xff08;默認設置&#xff09;。 Restricted …

圖像分類應用

先留一段圖像分類代碼&#xff0c;空閑時間再做分析&#xff1a; 創建神經網絡&#xff1a; import torch from torch import nn import torch.nn.functional as F class MyAlexNet(nn.Module):def __init__(self):super(MyAlexNet, self).__init__()self.c1nn.Conv2d(in_cha…

二刷代碼隨想錄算法訓練營第十天 | 232.用棧實現隊列、 225. 用隊列實現棧

目錄 一、232. 用棧實現隊列 二、225. 用隊列實現棧 一、232. 用棧實現隊列 題目鏈接&#xff1a;力扣 文章講解&#xff1a;代碼隨想錄 視頻講解&#xff1a; 棧的基本操作&#xff01; | LeetCode&#xff1a;232.用棧實現隊列 題目&#xff1a; 請你僅使用兩個棧實現先…

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…

CentOS7安裝DockerCompose和Docker鏡像倉庫的配置

CentOS7安裝DockerCompose 1.下載 Linux下需要通過命令下載&#xff1a; # 安裝 curl -L https://github.com/docker/compose/releases/download/1.23.1/docker-compose-uname -s-uname -m > /usr/local/bin/docker-compose2.修改文件權限 修改文件權限&#xff1a; # …

YOLOv9獨家原創改進|加入幽靈卷積Ghost Convolution模塊,輕量化!

專欄介紹&#xff1a;YOLOv9改進系列 | 包含深度學習最新創新&#xff0c;主力高效漲點&#xff01;&#xff01;&#xff01; 一、論文摘要 由于內存和計算資源有限&#xff0c;在嵌入式設備上部署卷積神經網絡是困難的。特征圖中的冗余是那些成功的細胞神經網絡的一個重要特征…

【網站項目】158企業人事管理系統

&#x1f64a;作者簡介&#xff1a;擁有多年開發工作經驗&#xff0c;分享技術代碼幫助學生學習&#xff0c;獨立完成自己的項目或者畢業設計。 代碼可以私聊博主獲取。&#x1f339;贈送計算機畢業設計600個選題excel文件&#xff0c;幫助大學選題。贈送開題報告模板&#xff…

突破編程_C++_字符串算法(判斷字符串是否包含)

1 算法題 &#xff1a;判斷一個字符串是否包含另一個字符串的所有字符&#xff08;不一定連續&#xff09; 1.1 題目含義 判斷一個字符串&#xff08;稱為“主字符串”或“大字符串”&#xff09;是否包含另一個字符串&#xff08;稱為“子字符串”或“小字符串”&#xff09…

代碼隨想錄算法訓練營第31天—貪心算法05 | ● 435. 無重疊區間 ● *763.劃分字母區間 ● *56. 合并區間

435. 無重疊區間 https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4.html 考點 貪心算法重疊區間 我的思路 先按照區間左坐標進行排序&#xff0c;方便后續處理進行for循環&#xff0c;循環范圍是0到倒數第二個元素如果當前區間和下一區間重疊…

在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 …

初學者學習51還是STM32

初學者學習51還是STM32 在嵌入式系統領域&#xff0c;51和STM32是兩種常見的單片機架構。對于初學者來說&#xff0c;選擇學習哪種架構可能會成為一個難題。本文將對初學者學習51和STM32進行比較&#xff0c;以幫助讀者做出明智的選擇。 1. 51架構 51架構是指Intel 8051系列…

深度相機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;下面…

ajax請求servlet成功但接收不到返回數據問題

ajax請求servlet成功但接收不到返回數據問題 javaweb初學者&#xff0c;最近老師布置的課設&#xff0c;所有功能都完成了&#xff0c;唯獨ajax與servlet交互出現問題&#xff0c;無論怎么調試都收不到數據 查詢兩天無果&#xff0c;剛才無意間看到 Crabime前輩的文章才恍然大…