ParameterNameDiscoverer
这个 Spring 用于寻找现方法和构造函数的参数名的发现器。
/**
* Interface to discover parameter names for methods and constructors.
* <p>用于发现方法和构造函数的参数名的接口</p>
* <p>Parameter name discovery is not always possible, but various strategies are
* available to try, such as looking for debug information that may have been
* emitted at compile time, and looking for argname annotation values optionally
* accompanying AspectJ annotated methods.
* <p>
* 参数名并非总是可以发现参数名称,但可以尝试各种策略,比如寻找在编译时可能
* 发生调试信号,和寻找可选的附带AspectJ注解方法的argname注解值
* </p>
* @author Rod Johnson
* @author Adrian Colyer
* @since 2.0
*/
public interface ParameterNameDiscoverer {
/**
* Return parameter names for a method, or {@code null} if they cannot be determined.
* <p>
* 返回方法的参数名,如果不能确定就返回{@code null}
* </p>
* <p>Individual entries in the array may be {@code null} if parameter names are only
* available for some parameters of the given method but not for others. However,
* it is recommended to use stub parameter names instead wherever feasible.
* <p>
* 如果参数名称仅可用于给定方法的某些参数,而不适用于其他参数,则数组的各个条目
* 可能为{@code null}.但是,建议在可行的地方使用存根参数名名代替。
* </p>
* @param method the method to find parameter names for
* -- 查找参数名称的方法
* @return an array of parameter names if the names can be resolved,
* or {@code null} if they cannot
* -- 如果名称能被解析就返回一组参数名,否则返回{@code null}
*/
@Nullable
String[] getParameterNames(Method method);
/**
* Return parameter names for a constructor, or {@code null} if they cannot be determined.
* <p>
* 返回构造函数的参数名,如果不能确定就返回{@code null}
* </p>
* <p>Individual entries in the array may be {@code null} if parameter names are only
* available for some parameters of the given constructor but not for others. However,
* it is recommended to use stub parameter names instead wherever feasible.
* <p>
* 如果参数名称仅可用于给定方法的某些参数,而不适用于其他参数,则数组的各个条目
* 可能为{@code null}.但是,建议在可行的地方使用存根参数名名代替。
* </p>
* @param ctor the constructor to find parameter names for
* -- 查找参数名称的方法
* @return an array of parameter names if the names can be resolved,
* or {@code null} if they cannot
* -- 如果名称能被解析就返回一组参数名,否则返回{@code null}
*/
@Nullable
String[] getParameterNames(Constructor<?> ctor);
}
PrioritizedParameterNameDiscoverer
/**
* {@link ParameterNameDiscoverer} implementation that tries several discoverer
* delegates in succession. Those added first in the {@code addDiscoverer} method
* have highest priority. If one returns {@code null}, the next will be tried.
* <p>陆续尝试一些发现器委托类的{@link ParameterNameDiscoverer}实现。在
* {@code addDiscoverer}方法中最先添加的那些有效级最高。如果一个返回{@code null},
* 尝试下一个</p>
* <p>The default behavior is to return {@code null} if no discoverer matches.
* <p>如果没有发现器匹配,则默认行为是返回{@code null}</p>
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
public class PrioritizedParameterNameDiscoverer implements ParameterNameDiscoverer {
/**
* 参数名发现器集合
*/
private final List<ParameterNameDiscoverer> parameterNameDiscoverers = new LinkedList<>();
/**
* Add a further {@link ParameterNameDiscoverer} delegate to the list of
* discoverers that this {@code PrioritizedParameterNameDiscoverer} checks.
* <p>向此{@code PrioritizedParameterNameDiscoverer}检查的发现器列表添加一个
* {@link ParameterNameDiscoverer}委托对象</p>
*/
public void addDiscoverer(ParameterNameDiscoverer pnd) {
this.parameterNameDiscoverers.add(pnd);
}
@Override
@Nullable
public String[] getParameterNames(Method method) {
//遍历参数名发现器集合
for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) {
//通过参数名发现器获取method中的参数名数组
String[] result = pnd.getParameterNames(method);
//如果result不为null,表示该参数名发现器能拿到method的参数名
if (result != null) {
//返回结果
return result;
}
//如果result为null,表示该参数名发现器没法拿到method的参数名,就交给下一个参数名发现
}
//如果所有的参数名发现器都没法拿到参数名,就返回null
return null;
}
@Override
@Nullable
public String[] getParameterNames(Constructor<?> ctor) {
//遍历参数名发现器集合
for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) {
//通过参数名发现器获取ctor中的参数名数组
String[] result = pnd.getParameterNames(ctor);
//如果result不为null,表示该参数名发现器能拿到ctor的参数名
if (result != null) {
//返回结果
return result;
}
//如果ctor为null,表示该参数名发现器没法拿到ctor的参数名,就交给下一个参数名发现器处理
}
//如果所有的参数名发现器都没法拿到参数名,就返回null
return null;
}
}
DefaultParameterNameDiscoverer
/**
* Default implementation of the {@link ParameterNameDiscoverer} strategy interface,
* using the Java 8 standard reflection mechanism (if available), and falling back
* to the ASM-based {@link LocalVariableTableParameterNameDiscoverer} for checking
* debug information in the class file.
* <p>{@link ParameterNameDiscoverer}策略接口的默认实现,使用Java 8标准反射机制(如果有),
* 然后回退基于ASM的{@link LocalVariableTableParameterNameDiscoverer},以检查在类文件中
* 调试信息。
* </p>
* <p>If a Kotlin reflection implementation is present,
* {@link KotlinReflectionParameterNameDiscoverer} is added first in the list and used
* for Kotlin classes and interfaces. When compiling or running as a Graal native image,
* no {@link ParameterNameDiscoverer} is used.
* <p>
* 如果存在Kotlin反射实现,则将{@link KotlinReflectionParameterNameDiscoverer}首先添加
* 到列表中,并将用于Kotlin类和接口。当编译或作为Graal本机映像时,不使用{@link ParameterNameDiscoverer}
* </p>
* <p>Further discoverers may be added through {@link #addDiscoverer(ParameterNameDiscoverer)}.
* <p>可以通过{@link #addDiscoverer(ParameterNameDiscoverer)}添加更多发现器</p>
* <p>GraalVM 是一个跨语言的通用虚拟机,不仅支持了 Java、Scala、Groovy、Kotlin 等基于 JVM 的语言,
* 以及 C、C++ 等基于 LLVM 的语言,还支持其他像 JavaScript、Ruby、Python 和 R 语言等。称是一个全
* 新的通用全栈虚拟机,并具有高性能、跨语言交互等逆天特性</p>
* @author Juergen Hoeller
* @author Sebastien Deleuze
* @since 4.0
* @see StandardReflectionParameterNameDiscoverer
* @see LocalVariableTableParameterNameDiscoverer
* @see KotlinReflectionParameterNameDiscoverer
*/
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {
public DefaultParameterNameDiscoverer() {
//如果项目不是运行在GraalVM环境里
if (!GraalDetector.inImageCode()) {
//如果存在Kntlin反射
if (KotlinDetector.isKotlinReflectPresent()) {
//添加Kotlin的反射工具内省参数名发现器
addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
}
//添加 使用JDK8的反射工具内省参数名 (基于'-parameters'编译器标记)的参数名发现器
addDiscoverer(new StandardReflectionParameterNameDiscoverer());
//添加 基于ASM库对Class文件的解析获取LocalVariableTable信息来发现参数名 的参数名发现器
addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
}
}
}
StandardReflectionParameterNameDiscoverer
/**
* {@link ParameterNameDiscoverer} implementation which uses JDK 8's reflection facilities
* for introspecting parameter names (based on the "-parameters" compiler flag).
* <p>{@link ParameterNameDiscoverer}实现类,使用JDK8的反射工具内省参数名
* (基于'-parameters'编译器标记)</p>
* @author Juergen Hoeller
* @since 4.0
* @see java.lang.reflect.Method#getParameters()
* @see java.lang.reflect.Parameter#getName()
*/
public class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
@Override
@Nullable
public String[] getParameterNames(Method method) {
return getParameterNames(method.getParameters());
}
@Override
@Nullable
public String[] getParameterNames(Constructor<?> ctor) {
return getParameterNames(ctor.getParameters());
}
@Nullable
private String[] getParameterNames(Parameter[] parameters) {
//初始化一个用于存放参数名的数组,长度为parameters的长度
String[] parameterNames = new String[parameters.length];
//遍历Parameter对象数组
for (int i = 0; i < parameters.length; i++) {
//获取Parameters中的第i个Parameter对象
Parameter param = parameters[i];
//Parameter.isNamePresent:如果参数具有根据类文件的名称,则返回true。否则
// 返回false。参数是否具有名称由声明该参数的方法MethodParameters确定。
// 简单来说就是验证参数名是不是可用
if (!param.isNamePresent()) {
//返回null,表示获取参数名失败
return null;
}
//获取param的名称赋值给第i个Parameter对象
parameterNames[i] = param.getName();
}
//返回参数名数组
return parameterNames;
}
}
LocalVariableTableParameterNameDiscoverer
/**
* Implementation of {@link ParameterNameDiscoverer} that uses the LocalVariableTable
* information in the method attributes to discover parameter names. Returns
* {@code null} if the class file was compiled without debug information.
* <p>{@link ParameterNameDiscoverer}的实现,该方法使用方法属性中的LocalVariableTable信息
* 来发现参数名.如果类文件是在没有调试信息的情况下编译的,则返回{@code null}</p>
* <p>Uses ObjectWeb's ASM library for analyzing class files. Each discoverer instance
* caches the ASM discovered information for each introspected Class, in a thread-safe
* manner. It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
* <p>使用ObjectWeb的ASM库分析类文件。每个发现者实例以线程安全的方式为每个自省类缓存ASM
* 发现的信息。建议尽可能重用ParameterNameDiscover实例</p>
* @author Adrian Colyer
* @author Costin Leau
* @author Juergen Hoeller
* @author Chris Beams
* @author Sam Brannen
* @since 2.0
*/
public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {
/**
* 日志类
*/
private static final Log logger = LogFactory.getLog(LocalVariableTableParameterNameDiscoverer.class);
// marker object for classes that do not have any debug info
/**
* 没有任何调试信息的类的标记对象
*/
private static final Map<Executable, String[]> NO_DEBUG_INFO_MAP = Collections.emptyMap();
// the cache uses a nested index (value is a map) to keep the top level cache relatively small in size
/**
* <p> 方法声明类 - (Constructor/Method对象-参数名数组缓存) 线程安全缓存Map</p>
* <p>缓存使用嵌套索引(值是一个映射)来使得顶级缓存相对较小</p>
*/
private final Map<Class<?>, Map<Executable, String[]>> parameterNamesCache = new ConcurrentHashMap<>(32);
@Override
@Nullable
public String[] getParameterNames(Method method) {
//获取提供的桥接方法的原始方法
Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);
//获取给定Constructor/Method的参数名数组
return doGetParameterNames(originalMethod);
}
@Override
@Nullable
public String[] getParameterNames(Constructor<?> ctor) {
return doGetParameterNames(ctor);
}
/**
* 获取给定Constructor/Method的参数名数组
* @param executable Constructor/Method对象
* @return 给定Constructor/Method的参数名数组
*/
@Nullable
private String[] doGetParameterNames(Executable executable) {
//获取executable的声明类
Class<?> declaringClass = executable.getDeclaringClass();
//获取declaringClass对应的Map<Excecutable,String[]>对象,如果没有,通过inspectClass方法构建一个
Map<Executable, String[]> map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass);
//如果map不是没有任何调试信息的类的标记对象,就返回在map中对应executable的参数名数组;否则返回null
return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null);
}
/**
* Inspects the target class.
* <p>检查目标类</p>
* <p>Exceptions will be logged, and a marker map returned to indicate the
* lack of debug information.
* <p>将记录异常,并返回标记映射以指示调试信息</p>
*/
private Map<Executable, String[]> inspectClass(Class<?> clazz) {
//获取clazz的class文件流
InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));
//如果文件流为null
if (is == null) {
// We couldn't load the class file, which is not fatal as it
// simply means this method of discovering parameter names won't work.
// 我们无法加载类文件,它不是致命的,因为它仅仅意味着发现参数名称的这种方法将行不通。
//如果是debug模式
if (logger.isDebugEnabled()) {
//log:没有找到类[clazz]的'.class'文件-无法确定构造函数/方法的参数名
logger.debug("Cannot find '.class' file for class [" + clazz +
"] - unable to determine constructor/method parameter names");
}
//返回没有任何调试信息的类的标记对象
return NO_DEBUG_INFO_MAP;
}
try {
//ClassVisitor:访问Java类的访问者
//ClassReader:用来使{@link ClassVisitor}的解析器访问Java虚拟机规范(JVMS)中的定义的ClassFile结构。此类
// 解析ClassFile内容,并为遇到的每个字段,方法和字节码调用给定{@link ClassVisitor}的适当访问方法
ClassReader classReader = new ClassReader(is);
//初始化一个并发哈希映射,初始化容量为32,用于存储Method/Constructor对象-参数名数组
Map<Executable, String[]> map = new ConcurrentHashMap<>(32);
//ParameterNameDiscoveringVisitor:帮助类,它检查所有方法和构造函数,然后尝试查找给定的Executable的参数名
//ClassReader.accept:使用给定的方法者访问传递给此ClassReader的构造函数的JVMS ClassFile结构。
classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0);
//返回用于存储Method/Constructor对象-参数名数组映射
return map;
}
catch (IOException ex) {
//捕捉IO异常
//如果日志级别为DEBUG级别
if (logger.isDebugEnabled()) {
//日志描述:读取类[clazz]的'.class'文件时引发异常-无法确定构造函数/方法参数名称
logger.debug("Exception thrown while reading '.class' file for class [" + clazz +
"] - unable to determine constructor/method parameter names", ex);
}
}
catch (IllegalArgumentException ex) {
//捕捉非法参数异常
//如果日志级别为DEBUG级别
if (logger.isDebugEnabled()) {
//日志描述:ASM ClassReader无法解析类文件[clazz],可能是由于尚不支持Java类文件版本
// -无法确定构造函数/方法参数名称
logger.debug("ASM ClassReader failed to parse class file [" + clazz +
"], probably due to a new Java class file version that isn't supported yet " +
"- unable to determine constructor/method parameter names", ex);
}
}
finally {
try {
//关闭类流
is.close();
}
catch (IOException ex) {
// ignore -- 忽略关闭流是的所有IO异常
}
}
//返回没有任何调试信息的类的标记对象
return NO_DEBUG_INFO_MAP;
}
/**
* Helper class that inspects all methods and constructors and then
* attempts to find the parameter names for the given {@link Executable}.
* <p>帮助类,它检查所有方法和构造函数,然后尝试查找给定的{@link Executable}的
* 参数名</p>
*/
private static class ParameterNameDiscoveringVisitor extends ClassVisitor {
/**
* 静态类初始化
*/
private static final String STATIC_CLASS_INIT = "<clinit>";
/**
* 方法声明类
*/
private final Class<?> clazz;
/**
* 方法-参数名数组映射
*/
private final Map<Executable, String[]> executableMap;
/**
* 新建一个{@link ParameterNameDiscoveringVisitor}实例
* @param clazz 方法声明类
* @param executableMap 方法-参数名数组映射
*/
public ParameterNameDiscoveringVisitor(Class<?> clazz, Map<Executable, String[]> executableMap) {
//SpringAsmInfo.ASM_VERSION:用于Spring的ASM访问者实现的ASM兼容版本:当前为
// {@link Opcodes#ASM7},从SpringFramework5.1开始
super(SpringAsmInfo.ASM_VERSION);
this.clazz = clazz;
this.executableMap = executableMap;
}
@Override
@Nullable
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// exclude synthetic + bridged && static class initialization
// 排除合成+桥接 && 静态类初始化
if (!isSyntheticOrBridged(access) && !STATIC_CLASS_INIT.equals(name)) {
return new LocalVariableTableVisitor(this.clazz, this.executableMap, name, desc, isStatic(access));
}
return null;
}
/**
* 根据给定方法的访问标记确定是否是合成或者是桥接方法
* @param access 方法的访问标记
* @return 如果是合成或者是桥接方法,返回true;否则返回false
*/
private static boolean isSyntheticOrBridged(int access) {
return (((access & Opcodes.ACC_SYNTHETIC) | (access & Opcodes.ACC_BRIDGE)) > 0);
}
/**
* 根据给定方法的访问标记确定是否是静态方法
* @param access 方法的访问标记
* @return 如果是静态方法,返回true;否则返回false
*/
private static boolean isStatic(int access) {
return ((access & Opcodes.ACC_STATIC) > 0);
}
}
/**
* 本地变量表访问器
*/
private static class LocalVariableTableVisitor extends MethodVisitor {
/**
* 构造函数名
*/
private static final String CONSTRUCTOR = "<init>";
/**
* 方法声明类
*/
private final Class<?> clazz;
/**
* 方法-方法参数名数组
*/
private final Map<Executable, String[]> executableMap;
/**
* 方法名
*/
private final String name;
/**
* 方法参数对应的{@link org.springframework.asm.Type}
*/
private final Type[] args;
/**
* 方法参数名
*/
private final String[] parameterNames;
/**
* 是否是静态方法的标记
*/
private final boolean isStatic;
/**
* 有lvt信息标记
* <p>lVT 就是 LocalVariableTable 缩写,相关文章:https://www.jianshu.com/p/876eaa14a0a9</p>
*/
private boolean hasLvtInfo = false;
/**
* The nth entry contains the slot index of the LVT table entry holding the
* argument name for the nth parameter.
* <p>第n个条目包含LVT表条目的插槽索引,该条目保留第n个参数的参数名称。</p>
* <p>lVT 就是 LocalVariableTable 缩写,相关文章:https://www.jianshu.com/p/876eaa14a0a9</p>
*/
private final int[] lvtSlotIndex;
/**
*
* @param clazz 方法声明类
* @param map 方法-方法参数名数组
* @param name 方法名
* @param desc 方法的描述符(参见{@link Type})
* @param isStatic 是否是静态方法
*/
public LocalVariableTableVisitor(Class<?> clazz, Map<Executable, String[]> map, String name, String desc, boolean isStatic) {
//用于Spring的ASM访问者实现的ASM兼容版本:当前为Opcodes.ASM7,从 SpringFramework5.1开始
super(SpringAsmInfo.ASM_VERSION);
this.clazz = clazz;
this.executableMap = map;
this.name = name;
//获取desc的参数类型相对应的Type值
this.args = Type.getArgumentTypes(desc);
this.parameterNames = new String[this.args.length];
this.isStatic = isStatic;
//计算出lvt插槽指数数组的每个值
this.lvtSlotIndex = computeLvtSlotIndices(isStatic, this.args);
}
@Override
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
//当触发该方法,就表示有LocalVariableTable信息,所以设置为true
this.hasLvtInfo = true;
//遍历LocalVariableTable插槽索引数组
for (int i = 0; i < this.lvtSlotIndex.length; i++) {
//如果第i个LocalVariableTable插槽索引等于该局部变量的索引
if (this.lvtSlotIndex[i] == index) {
//第i个参数名就为该局部变量名
this.parameterNames[i] = name;
}
}
}
@Override
public void visitEnd() {
//如果存在LocalVariableTable的信息 或者 该方法为静态方法且该方法没有参数
if (this.hasLvtInfo || (this.isStatic && this.parameterNames.length == 0)) {
// visitLocalVariable will never be called for static no args methods
// which doesn't use any local variables.
// 不会使用任何局部变量的静态无参方法从不调用visitLocalVariable
// This means that hasLvtInfo could be false for that kind of methods
// even if the class has local variable info.
// 这意味着即使类具有局部变量信息,hasLvtInfo对于这种方法也可能为false
// 解析出Constructor/Method对象,并将其与参数名数组的关系映射添加到
// 方法-方法参数名数组映射中
this.executableMap.put(resolveExecutable(), this.parameterNames);
}
}
/**
* 解析出Constructor/Method对象
* @return Constructor/Method对象
*/
private Executable resolveExecutable() {
//获取方法声明类的类加载器
ClassLoader loader = this.clazz.getClassLoader();
//初始化参数类型数组,长度为args的长度
Class<?>[] argTypes = new Class<?>[this.args.length];
//遍历args
for (int i = 0; i < this.args.length; i++) {
//将第i个arg的全类名解析成Class实例,支持原始类型(如'int')和数组类型名 (如'String[]').
// 这实际等效于具有相投参数的forName方法,唯一的区别 是在类加载的情况下引发异常
argTypes[i] = ClassUtils.resolveClassName(this.args[i].getClassName(), loader);
}
try {
//如果方法名是构造函数名
if (CONSTRUCTOR.equals(this.name)) {
//获取方法声明类中的调用argTypes参数类型数组的Constructor对象并返回出去
return this.clazz.getDeclaredConstructor(argTypes);
}
//到这一步表示获取的是方法,则获取在方法声明类中方法名为name,参数类型数组为
// argTypes的Method对象并返回出去
return this.clazz.getDeclaredMethod(this.name, argTypes);
}
catch (NoSuchMethodException ex) {
//捕捉没有这样方法异常,抛出非法状态异常,异常信息为:方法[this.name]在.class文件中
// 发现,但无法在class对象中解析
throw new IllegalStateException("Method [" + this.name +
"] was discovered in the .class file but cannot be resolved in the class object", ex);
}
}
/**
* 计算lvt插槽指数
* @param isStatic 是否静态
* @param paramTypes 参数类型
* @return
*/
private static int[] computeLvtSlotIndices(boolean isStatic, Type[] paramTypes) {
//lvt插槽指数索引数组
int[] lvtIndex = new int[paramTypes.length];
//下一个索引位置,如果是静态就为0,否则为1,因为实例方法,前面第0个位置还有个this
int nextIndex = (isStatic ? 0 : 1);
//遍历参数类型数组
for (int i = 0; i < paramTypes.length; i++) {
//设置第i个lvt索引为nextIndex
lvtIndex[i] = nextIndex;
//如果参数类型为LONG类型或者是DOUBLE类型,因为如果是long和double需要
// 2个连续的局部变量表来保存
if (isWideType(paramTypes[i])) {
//nextIndex+2位
nextIndex += 2;
}
else {
//nextIndex+1
nextIndex++;
}
}
//lvt插槽指数索引数组
return lvtIndex;
}
/**
* 确定给定的{@link Type}是否为LONG类型或者是DOUBLE类型
* @param aType 参数类型
* @return 给定的{@link Type}为LONG类型或者是DOUBLE类型时返回true;否则返回false
*/
private static boolean isWideType(Type aType) {
// float is not a wide type -- float不是宽类型
return (aType == Type.LONG_TYPE || aType == Type.DOUBLE_TYPE);
}
}
}