写在前面:
由于公司目前系统07年开始开发的,完全由servlet+htm开发的,无设计模式,全if else ,while 构成,有的客户还是用JDK1.5,上过一次spring报了100多个bug,又下架了。现在开始自己写类似spring MVC ,BeanFactory,Aop,myBatis框架。在开发的过程中,对SpringMVC,myBatis等框架进行了研究。我的新框架是简单的实现,基本够用。
本次先写对SpringMVC参数映射的一点认识。
http://127.0.0.1/login?username=admin&password=!# ! #!#
public String login(String username,String password){
}
SpringMVC是如何把参数名称username,password对应上,且把值传给对应方法的?
带着上面的问题,逐步来了解
先看一下,请求过程的堆栈:
从doService 一路到 LocalVariableTableParameterNameDiscoverer.getParameterNames,再调用 ClassReader 读取class文件解析出参数名称。
请求概要 时序图
接上图
从图二开始解析参数,
图二中的 ParameterNameDiscoverer 是接口,LocalVariableTableParameterNameDiscoverer是其实现类。
二、路径到Controller方法映射
略……
重点开始:
三、方法参数映射
LocalVariableTableParameterNameDiscoverer.java
@Override
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;
}
先从参数缓存中查找,
MapString[]> map = this.parameterNamesCache.get(declaringClass);
如果没有解析过(本次重点方法)
map = inspectClass(declaringClass);
详细方法如下:(我去掉了注释和异常日志打印)
private Map inspectClass(Class> clazz) {
InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));
if (is == null) {
return NO_DEBUG_INFO_MAP;
}
try {
ClassReader classReader = new ClassReader(is);
Map map = new ConcurrentHashMap(32);
classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0);
return map;
} catch (IOException ex) {}
catch (IllegalArgumentException ex) {}
finally {
is.close();
}
return NO_DEBUG_INFO_MAP;
}
分析:
1 加载class文件流
2 构造ClassReader
3 调用ClassReader.accept
具体对应方法:
public void accept(final ClassVisitor classVisitor,final Attribute[] attrs, final int flags)
接着调用 :
private int readMethod(final ClassVisitor classVisitor,final Context context, int u)
这个方法中,读取class文件,根据java规范解析该文件,获取方法名称,参数类型,参数名称
至此,获取到对应的Controller对应方法的参数名称 如:username,password,
再调用request.getParameter(“username”)获取参数值
一路返回至 InvocableHandlerMethod.doInvoke(),MappingRequest对应的方法
四、总结
由于时间紧迫,语言表达能力有限,仅供参考。
等稍空闲再写 ClassReader读取过程。