Spring源码阅读之数据自动绑定

来源:互联网 发布:java开源门户网站系统 编辑:程序博客网 时间:2024/06/03 11:55

SpringMVC的调用过程:
DispatcherServlet ——> 根据url找到相应的Controller,反射方式调用Controller相应的方法。

研究下面两种调用方式:

    @RequestMapping("/baseType")    public User baseType(int count, long id) {        User user = new User();        user.setId(id);        user.setName("zhang");        return user;    }    @RequestMapping("/object")    public User object(User user) {        return user;    }

上述代码中,基本类型参数以及Java Bean参数,spring都能进行自动数据绑定。

对于第一种基本数据类型的,假如请求url为http://localhost:8080/user/baseType?count=2&id=5,url参数能自动绑定到后端方法相应名字的参数,正如上面所说的,Controller的方法是反射调用的,因此通过反射取得的方法是不会保存方法参数的参数名的,那么在反射调用的时候是怎么能够对应参数名进行传递的呢?
通过debug方式阅读spring的源码(请求过程中debug,以及容器启动过程中debug),重点查看了
DefaultParameterNameDiscoverer,
ConstructorResolver,
AspectJAdviceParameterNameDiscoverer, LocalVariableTableParameterNameDiscoverer,
PrioritizedParameterNameDiscoverer这几个类发现,spring在启动的时候通过类似asm的方式(没采用asm库)从class文件中读取了方法的参数名,并保存到相应的对象缓存中。这里我还没有仔细研究,我的猜测是class文件是有保存方法参数的参数名的,另外我记得javaassist也是通过读取class文件获取方法的参数名的(但是自己用javac生成的class文件貌似没保存参数名)。后续需要研究一下原生java如何解析class文件内容。

在idea的java compiler中加入 -g:none ,再跑起来,调用url,会报错:
java.lang.IllegalArgumentException: Name for argument type [int] not available, and parameter name information not found in class file either.

由此说明,使用maven编译打包时回把方法的参数名信息加入class文件的,也就是说asm读取参数名字时从class文件获取的。


spring-core中有个ParameterNameDiscoverer就是用来获取参数名的,底层用的是asm解析,但是接口方法的参数名无法得到,即只能是非接口类的方法参数名可以
ParameterNameDiscoverer pnd=new DefaultParameterNameDiscoverer();
String[] parameterNames=pnd.getParameterNames(用反射获取到的方法对象);//返回的就是方法中的参数名列表了
http://bbs.csdn.net/topics/391054482?page=1

Java运行时通过asm读取方法参数名:
http://www.oschina.net/code/snippet_2438265_54195
asm获取参数名的原理??

利用javassist获取java的方法参数名:
http://www.tuicool.com/articles/jMjaIr

java如何获取方法参数名:http://blog.csdn.net/mhmyqn/article/details/47294485

/**
* javac -g:none ,加上这个参数就没有了
* —
* javac -g:vars
* 以及
* mvn clean install 都可以
*/
反射是取不到的,如果在编译时使用默认选项的话。javac带有-g:vars编译参数的话,局部变量及方法形式参数名会在字节码中保存着,但是Java代码是访问不到的。
如果需要抽取字节码中的形式参数名称需要自己去了解JVM字节码规范自己去解析!


对于第二种Java Bean参数自动绑定的,因为是是对象,并不需要上述的参数名,但是需要通过反射方式设值。通过查看源码,发现确实是通过反射方式设值的,但是,正如大家一直所强调的,反射的效率较低,能不用反射尽量别用,但是spring为什么频繁用反射,而且现在每一次请求都是通过反射来赋值,那岂不是效率很低?
反射方法缓存
通过进一步查看代码发现,虽然第一次请求同过反射方式生成方法,但是所生成的方法会被保存到相应的对象缓存,当请求再次发生,调用相同的方法时,方法并不需要重新通过反射生成,而是直接从缓存中取(其实就是Map或List),不过还是通过此方法代理调用。

其实无论是DispatcherServlet通过反射调用Controller的方法,还是JavaBean参数反射调用赋值,需要反射调用的方法都是只生成一次(之后保存在缓存),而反射调用才是多次重复的。所以我的理解是生成反射方法才是效率低的,而方法的代理调用并不会太影响性能。

0 0
原创粉丝点击