下面是分析过程,最后有结果。
环境:
JDK_1.8
spring 4.3.16.RELEASE
目录结构:
分析过程
在Controller中设置断点,启动项目后,发送GET请求http://localhost:8080/test/demo/user?name=goaler
观察方法调用栈,可以看到请求处理的过程
通过查看每个方法的内容,发现getU方法的实际调用处在ServletInvocableHandlerMethod(InvocableHandlerMethod).doInvoke(Object...) line: 205
方法内容:
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
//调用方法,此处args已有值
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
//省略。。。
}
catch (InvocationTargetException ex) {
//省略。。。
}
}
调用方法处,args参数已经有值,往上一个方法寻找
ServletInvocableHandlerMethod(InvocableHandlerMethod).invokeForRequest(NativeWebRequest, ModelAndViewContainer, Object...) line: 133
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//此处获取方法参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments " + Arrays.toString(args));
}
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}
return returnValue;
}
进入getMethodArgumentValues(request, mavContainer, providedArgs);这个方法是获取方法参数的
在149行设置断点重新调试,然后单行执行。执行到160行时args中已有值。
进入158行的resolveArgument方法
再121行设置断点单步调试,进入
然后单行执行,发现返回值arg在103行被赋值。
回退,单行执行到103行,单步执行进入resolveName方法
同理,返回值arg在177行被赋值,而paramValues是通过request.getParameterValues(name)直接取值的,那么这个 方法参数名是如何获得的呢?
下面寻找这个name的来源
后退到resolveArgument(此处往上数第二个截图),从97,103行看出name值来源于namedValueInfo.name
那么进入getNamedValueInfo方法查看namedValueInfo的来源
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
//此处从缓存获取,因为是第一次执行所以获取不到
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
if (namedValueInfo == null) {
//根据参数前的注解@RequestParam获取参数名称,本demo没有注解所以创建了一个名称为""的namedValueInfo
namedValueInfo = createNamedValueInfo(parameter);
//通过其他方式获取参数名称
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
进入updateNameValueInfo方法
/**
* Create a new NamedValueInfo based on the given NamedValueInfo with sanitized values.
*/
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
String name = info.name;
//如果没有获得参数名称
if (info.name.isEmpty()) {
//参数名称在这里取得
name = parameter.getParameterName();
if (name == null) {
throw new IllegalArgumentException(
"Name for argument type [" + parameter.getNestedParameterType().getName() +
"] not available, and parameter name information not found in class file either.");
}
}
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
进入parameter.getParameterName();
/**
* Return the name of the method/constructor parameter.
* @return the parameter name (may be {@code null} if no
* parameter name metadata is contained in the class file or no
* {@link #initParameterNameDiscovery ParameterNameDiscoverer}
* has been set to begin with)
*/
public String getParameterName() {
ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
if (discoverer != null) {
//此处this.method 不为null
String[] parameterNames = (this.method != null ?
discoverer.getParameterNames(this.method) : discoverer.getParameterNames(this.constructor));
if (parameterNames != null) {
this.parameterName = parameterNames[this.parameterIndex];
}
this.parameterNameDiscoverer = null;
}
return this.parameterName;
}
进入discoverer.getParameterNames(this.method)
public String[] getParameterNames(Method method) {
for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) {
String[] result = pnd.getParameterNames(method);
if (result != null) {
return result;
}
}
return null;
}
此处Discoverers中有两个discoverer
org.springframework.core.StandardReflectionParameterNameDiscoverer,
jdk8可以通过反射获取参数名称,但是需要使用-parameters参数开启这个功能
org.springframework.core.LocalVariableTableParameterNameDiscoverer
解析字节码文件获取参数名称
这里是通过解析字节码文件获取参数名称
进入LocalVariableTableParameterNameDiscoverer的getParameterNames方法
public String[] getParameterNames(Method method) {
Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);
Class> declaringClass = originalMethod.getDeclaringClass();
//缓存获取
Map map = this.parameterNamesCache.get(declaringClass);
if (map == null) {
map = inspectClass(declaringClass);
this.parameterNamesCache.put(declaringClass, map);
}
if (map != NO_DEBUG_INFO_MAP) {
return map.get(originalMethod);
}
return null;
}
inspectClass方法就是从字节码中读取方法的参数
该方法读取declaringClass类所有方法的参数名,并返回map。map的key是方法方法,value是参数名构成的string数组。
获取到方法参数名后依次返回,最终通过获取到的参数名称,从request中获取参数的值,然后invoke(obj,args)调用。
执行过程:
获取方法参数值
/**
* Get the method argument values for the current request.
*/
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//这里的参数还没有拿到参数名称
MethodParameter[] parameters = getMethodParameters();
//存储方法参数值
Object[] args = new Object[parameters.length];
//循环获取参数值
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
//获取参数值
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
throw new IllegalStateException("Could not resolve method parameter at index " +
parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
}
}
return args;
}
getNamedValueInfo(parameter);
获取方法参数名称
resolveName(resolvedName.toString(), nestedParameter, webRequest);
根据方法参数名称获取参数值
@Override
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//获取方法参数名称
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
//根据方法参数名称从request中获取参数值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
获取方法参数名称过程
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
if (namedValueInfo == null) {
//根据注解获取参数名称
namedValueInfo = createNamedValueInfo(parameter);
//再次尝试获取参数名称
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
String name = info.name;
if (info.name.isEmpty()) {
//获取参数名称
name = parameter.getParameterName();
if (name == null) {
throw new IllegalArgumentException(
"Name for argument type [" + parameter.getNestedParameterType().getName() +
"] not available, and parameter name information not found in class file either.");
}
}
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
public String getParameterName() {
ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
if (discoverer != null) {
String[] parameterNames = (this.method != null ?
discoverer.getParameterNames(this.method) : discoverer.getParameterNames(this.constructor));
if (parameterNames != null) {
this.parameterName = parameterNames[this.parameterIndex];
}
this.parameterNameDiscoverer = null;
}
return this.parameterName;
}
public String[] getParameterNames(Method method) {
Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);
Class> declaringClass = originalMethod.getDeclaringClass();
Map map = this.parameterNamesCache.get(declaringClass);
if (map == null) {
//从字节码文件中获取参数名称
map = inspectClass(declaringClass);
this.parameterNamesCache.put(declaringClass, map);
}
if (map != NO_DEBUG_INFO_MAP) {
return map.get(originalMethod);
}
return null;
}
总结
spring mvc先获取参数注解中的参数名称,如果没有获取到在通过反射或解析字节码文件获取参数名称,
获取到参数名称后,根据参数名称从request中获取参数的值,然后调用方法的invoke(obj,args)执行方法