1.概述
Java 中的反射虽然功能强大,但对大多数开发人员来说,写出高质量的反射代码还是 有一定难度的。MyBatis 中专门提供了反射模块,该模块对 Java 原生的反射进行了良好的封装,提了更加简洁易用的 API,方便上层使调用,并且对反射操作进行了一系列优化,例如缓存了类的元数据,提高了反射操作的性能。
2. Reflector
org.apache.ibatis.reflection.Reflector ,反射器,每个 Reflector 对应一个类。Reflector 会缓存反射操作需要的类的信息,例如:构造方法、属性名、setting / getting 方法等等。代码如下:
public class Reflector {
/**
* 对应的类
*/
private final Class<?> type;
/**
* 可读属性数组
*/
private final String[] readablePropertyNames;
/**
* 可写属性集合
*/
private final String[] writeablePropertyNames;
/**
* 属性对应的 setting 方法的映射。
*
* key 为属性名称
* value 为 Invoker 对象
*/
private final Map<String, Invoker> setMethods = new HashMap<>();
/**
* 属性对应的 getting 方法的映射。
*
* key 为属性名称
* value 为 Invoker 对象
*/
private final Map<String, Invoker> getMethods = new HashMap<>();
/**
* 属性对应的 setting 方法的方法参数类型的映射。{@link #setMethods}
*
* key 为属性名称
* value 为方法参数类型
*/
private final Map<String, Class<?>> setTypes = new HashMap<>();
/**
* 属性对应的 getting 方法的返回值类型的映射。{@link #getMethods}
*
* key 为属性名称
* value 为返回值的类型
*/
private final Map<String, Class<?>> getTypes = new HashMap<>();
/**
* 默认构造方法
*/
private Constructor<?> defaultConstructor;
/**
* 不区分大小写的属性集合
*/
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
public Reflector(Class<?> clazz) {
// 设置对应的类
type = clazz;
// <1> 初始化 defaultConstructor
addDefaultConstructor(clazz);
// <2> // 初始化 getMethods 和 getTypes ,通过遍历 getting 方法
addGetMethods(clazz);
// <3> // 初始化 setMethods 和 setTypes ,通过遍历 setting 方法。
addSetMethods(clazz);
// <4> // 初始化 getMethods + getTypes 和 setMethods + setTypes ,通过遍历 fields 属性。
addFields(clazz);
// <5> 初始化 readablePropertyNames、writeablePropertyNames、caseInsensitivePropertyMap 属性
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
// ... 省略一些方法
}
2.1 addDefaultConstructor
addDefaultConstructor(Class<?> clazz) 方法,查找默认无参构造方法。
private void addDefaultConstructor(Class<?> clazz) {
// 获得所有构造方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0)
.findAny().ifPresent(constructor -> this.defaultConstructor = constructor);
}
2.2 addGetMethods
addGetMethods(Class<?> cls) 方法,初始化 getMethods 和 getTypes ,通过遍历 getting 方法
private void addGetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingGetters = new HashMap<>();
//获得所有方法
Method[] methods = getClassMethods(clazz);
//过滤获得get 方法
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveGetterConflicts(conflictingGetters);
}
2.2.1 getClassMethods
#getClassMethods(Class<?> cls) 方法,获得所有方法。代码如下:
Map<String, Method> uniqueMethods = new HashMap<>();
Class<?> currentClass = clazz;
while (currentClass != null && currentClass != Object.class) {
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
// we also need to look for interface methods -
// because the class may be abstract
Class<?>[] interfaces = currentClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
addUniqueMethods(uniqueMethods, anInterface.getMethods());
}
currentClass = currentClass.getSuperclass();
}
Collection<Method> methods = uniqueMethods.values();
return methods.toArray(new Method[0]);
2.2.2 resolveGetterConflicts
resolveGetterConflicts(Map<String, List>) 方法,解决 getting 冲突方法。最终,一个属性,只保留一个对应的方法。代码如下:
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;
}
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
if (candidateType.equals(winnerType)) {
if (!boolean.class.equals(candidateType)) {
isAmbiguous = true;
break;
} else if (candidate.getName().startsWith("is")) {
winner = candidate;
}
//isAssignableFrom()方法是判断是否为某个类的父类
} else if (candidateType.isAssignableFrom(winnerType)) {
// OK getter type is descendant
} else if (winnerType.isAssignableFrom(candidateType)) {
winner = candidate;
} else {
isAmbiguous = true;
break;
}
}
addGetMethod(propName, winner, isAmbiguous);
}
}
2.3 addSetMethods
addSetMethods(Class<?> cls) 方法,初始化 setMethods 和 setTypes ,通过遍历 setting 方法。代码如下:
Map<String, List<Method>> conflictingSetters = new HashMap<>();
//获得所有方法
Method[] methods = getClassMethods(clazz);
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveSetterConflicts(conflictingSetters);
2.4 addFields
addFields(Class<?> clazz) 方法,初始化 getMethods + getTypes 和 setMethods + setTypes ,通过遍历 fields 属性。实际上,它是 #addGetMethods(…) 和 #addSetMethods(…) 方法的补充,因为有些 field ,不存在对应的 setting 或 getting 方法,所以直接使用对应的 field ,
private void addFields(Class<?> clazz) {
//获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!setMethods.containsKey(field.getName())) {
// issue #379 - removed the check for final because JDK 1.5 allows
// modification of final fields through reflection (JSR-133). (JGB)
// pr #16 - final static can only be set by the classloader
int modifiers = field.getModifiers();
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
addSetField(field);
}
}
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
// 递归,处理父类
if (clazz.getSuperclass() != null) {
addFields(clazz.getSuperclass());
}
}
3. ReflectorFactory
org.apache.ibatis.reflection.ReflectorFactory ,Reflector 工厂接口,用于创建和缓存 Reflector 对象。代码如下:
/**
* @return 是否缓存 Reflector 对象
*/
boolean isClassCacheEnabled();
/**
* 设置是否缓存 Reflector 对象
*
* @param classCacheEnabled 是否缓存
*/
void setClassCacheEnabled(boolean classCacheEnabled);
/**
* 获取 Reflector 对象
*
* @param type 指定类
* @return Reflector 对象
*/
Reflector findForClass(Class<?> type);
3.1 DefaultReflectorFactory
org.apache.ibatis.reflection.DefaultReflectorFactory ,实现 ReflectorFactory 接口,默认的 ReflectorFactory 实现类。代码如下:
public class DefaultReflectorFactory implements ReflectorFactory {
/**
* 是否缓存
*/
private boolean classCacheEnabled = true;
/**
* Reflector 的缓存映射
*
* KEY:类
* VALUE:Reflector 对象
*/
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
public DefaultReflectorFactory() {
}
@Override
public boolean isClassCacheEnabled() {
return classCacheEnabled;
}
@Override
public void setClassCacheEnabled(boolean classCacheEnabled) {
this.classCacheEnabled = classCacheEnabled;
}
@Override
public Reflector findForClass(Class<?> type) {
// 开启缓存,则从 reflectorMap 中获取
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
return reflectorMap.computeIfAbsent(type, Reflector::new); // 不存在,则进行创建
// 关闭缓存,则创建 Reflector 对象
} else {
return new Reflector(type);
}
}
}
4. Invoker
org.apache.ibatis.reflection.invoker.Invoker ,调用者接口。代码如下:
// Invoker.java
public interface Invoker {
/**
* 执行调用
*
* @param target 目标
* @param args 参数
* @return 结果
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
/**
* @return 类
*/
Class<?> getType();
}
4.1 GetFieldInvoker
org.apache.ibatis.reflection.invoker.GetFieldInvoker ,实现 Invoker 接口,获得 Field 调用者。代码如下:
// GetFieldInvoker.java
public class GetFieldInvoker implements Invoker {
/**
* Field 对象
*/
private final Field field;
public GetFieldInvoker(Field field) {
this.field = field;
}
// 获得属性
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
return field.get(target);
}
// 返回属性类型
@Override
public Class<?> getType() {
return field.getType();
}
}
4.2 SetFieldInvoker
org.apache.ibatis.reflection.invoker.SetFieldInvoker ,实现 Invoker 接口,设置 Field 调用者。代码如下:
public class SetFieldInvoker implements Invoker {
private final Field field;
public SetFieldInvoker(Field field) {
this.field = field;
}
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException {
try {
field.set(target, args[0]);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
field.set(target, args[0]);
} else {
throw e;
}
}
return null;
}
@Override
public Class<?> getType() {
return field.getType();
}
}
4.3 MethodInvoker
org.apache.ibatis.reflection.invoker.MethodInvoker ,实现 Invoker 接口,指定方法的调用器。代码如下:
// MethodInvoker.java
public class MethodInvoker implements Invoker {
/**
* 类型
*/
private final Class<?> type;
/**
* 指定方法
*/
private final Method method;
public MethodInvoker(Method method) {
this.method = method;
// 参数大小为 1 时,一般是 setting 方法,设置 type 为方法参数[0]
if (method.getParameterTypes().length == 1) {
type = method.getParameterTypes()[0];
// 否则,一般是 getting 方法,设置 type 为返回类型
} else {
type = method.getReturnType();
}
}
// 执行指定方法
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
return method.invoke(target, args);
}
@Override
public Class<?> getType() {
return type;
}
}
5. ObjectFactory
org.apache.ibatis.reflection.factory.ObjectFactory ,Object 工厂接口,用于创建指定类的对象。代码如下:
// ObjectFactory.java
public interface ObjectFactory {
/**
* 设置 Properties
*
* Sets configuration properties.
* @param properties configuration properties
*/
void setProperties(Properties properties);
/**
* 创建指定类的对象,使用默认构造方法
*
* Creates a new object with default constructor.
* @param type Object type
* @return 对象
*/
<T> T create(Class<T> type);
/**
* Creates a new object with the specified constructor and params.
*
* 创建指定类的对象,使用特定的构造方法
*
* @param type Object type
* @param constructorArgTypes Constructor argument types 指定构造方法的参数列表
* @param constructorArgs Constructor argument values 参数数组
* @return 对象
*/
<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.
*
* 判断指定类是否为集合类
*
* @param type Object type
* @return whether it is a collection or not
* @since 3.1.0
*/
<T> boolean isCollection(Class<T> type);
}
5.1 DefaultObjectFactory
org.apache.ibatis.reflection.factory.DefaultObjectFactory ,实现 ObjectFactory、Serializable 接口,默认 ObjectFactory 实现类。
5.1.1 create
// DefaultObjectFactory.java
@Override
public <T> T create(Class<T> type) {
return create(type, null, null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
// <1> 获得需要创建的类
Class<?> classToCreate = resolveInterface(type);
// we know types are assignable
// <2> 创建指定类的对象
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
调用instantiateClass(Class type, List<Class<?>> constructorArgTypes, List constructorArgs) 方法,创建指定类的对象。代码如下:
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
// <x1> 通过无参构造方法,创建指定类的对象
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance();
}
// <x2> 使用特定构造方法,创建指定类的对象
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (Exception e) {
// 拼接 argTypes
StringBuilder argTypes = new StringBuilder();
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
for (Class<?> argType : constructorArgTypes) {
argTypes.append(argType.getSimpleName());
argTypes.append(",");
}
argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
}
// 拼接 argValues
StringBuilder argValues = new StringBuilder();
if (constructorArgs != null && !constructorArgs.isEmpty()) {
for (Object argValue : constructorArgs) {
argValues.append(String.valueOf(argValue));
argValues.append(",");
}
argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
}
// 抛出 ReflectionException 异常
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}
6. Property 工具类
6.1 PropertyCopier
org.apache.ibatis.reflection.property.PropertyCopier ,属性复制器。代码如下:
// PropertyNamer.java
public final class PropertyCopier {
private PropertyCopier() {
// Prevent Instantiation of Static Class
}
/**
* 将 sourceBean 的属性,复制到 destinationBean 中
*
* @param type 指定类
* @param sourceBean 来源 Bean 对象
* @param destinationBean 目标 Bean 对象
*/
public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
// 循环,从当前类开始,不断复制到父类,直到父类不存在
Class<?> parent = type;
while (parent != null) {
// 获得当前 parent 类定义的属性
final Field[] fields = parent.getDeclaredFields();
for (Field field : fields) {
try {
// 设置属性可访问
field.setAccessible(true);
// 从 sourceBean 中,复制到 destinationBean 去
//Field.set()向对象的这个Field属性设置新值value
field.set(destinationBean, field.get(sourceBean));
} catch (Exception e) {
// Nothing useful to do, will only fail on final fields, which will be ignored.
}
}
// 获得父类
parent = parent.getSuperclass();
}
}
}
6.2 PropertyNamer
org.apache.ibatis.reflection.property.PropertyNamer ,属性名相关的工具类方法。代码如下:
public final class PropertyNamer {
private PropertyNamer() {
// Prevent Instantiation of Static Class
}
/**
* 根据方法名,获得对应的属性名
*
* @param name 方法名
* @return 属性名
*/
public static String methodToProperty(String name) {
// is 方法
if (name.startsWith("is")) {
name = name.substring(2);
// get 或者 set 方法
} else if (name.startsWith("get") || name.startsWith("set")) {
name = name.substring(3);
// 抛出 ReflectionException 异常,因为只能处理 is、set、get 方法
} 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)))) {
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
return name;
}
/**
* 判断是否为 is、get、set 方法
*
* @param name 方法名
* @return 是否
*/
public static boolean isProperty(String name) {
return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");
}
/**
* 判断是否为 get、is 方法
*
* @param name 方法名
* @return 是否
*/
public static boolean isGetter(String name) {
return name.startsWith("get") || name.startsWith("is");
}
/**
* 判断是否为 set 方法
*
* @param name 方法名
* @return 是否
*/
public static boolean isSetter(String name) {
return name.startsWith("set");
}
}
7. MetaClass
org.apache.ibatis.reflection.MetaClass ,类的元数据,基于 Reflector 和 PropertyTokenizer ,提供对指定类的各种骚操作。
7.1 构造方法
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
8.ParamNameResolver
org.apache.ibatis.reflection.ParamNameResolver ,参数名解析器。
8.1 构造方法
public ParamNameResolver(Configuration config, Method method) {
final Class<?>[] paramTypes = method.getParameterTypes();
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// 忽略,如果是特殊参数
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
// 首先,从 @Param 注解中获取参数
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
// 其次,获取真实的参数名
if (config.isUseActualParamName()) { // 默认开启
name = getActualParamName(method, paramIndex);
}
// 最差,使用 map 的顺序,作为编号
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
// 添加到 map 中
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);
}
8.2 getNamedParams
#getNamedParams(Object[] args) 方法,获得参数名与值的映射。代码如下:
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
// 无参数,则返回 null
if (args == null || paramCount == 0) {
return null;
// 只有一个非注解的参数,直接返回首元素
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
} else {
// 集合。
// 组合 1 :KEY:参数名,VALUE:参数值
// 组合 2 :KEY:GENERIC_NAME_PREFIX + 参数顺序,VALUE :参数值
final Map<String, Object> param = new ParamMap<>();
int i = 0;
// 遍历 names 集合
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// 组合 1 :添加到 param 中
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
// 组合 2 :添加到 param 中
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。