你以为反射真的无所不能?至少JDK8以后很强大
之前我们已经介绍了Java中框架常用的技术---反射。可以这么说反射方便了我们的开发。今天我们来说说他的短板,或者说我们今天在反射的基础上在进行方便化。
反射操作方法
在上一章节中我们学会了通过反射去调用方法。
public class App {
    public void test(String str, Integer integer) {
        System.out.println(str);
        System.out.println(integer);
    }
}
这个时候如果我想获取test方法对象的话应该这么做
Method testMethod = App.class.getMethod("test", String.class, Integer.class);
这里就不在赘述如何通过Method对象调用方法了。文章末尾会给出上一章节的地址。今天我们要研究的是Method如何获取方法参数这一块。看似简单却又是那么的传奇。我们看看下面一段代码执行的效果
public static void main(String[] args) throws ParseException, NoSuchMethodException {
        Method[] methods = App.class.getMethods();
        Method testMethod = App.class.getMethod("test", String.class, Integer.class);
        Class<?>[] parameterTypes = testMethod.getParameterTypes();
        Parameter[] parameters = testMethod.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter.getName());
        }
    }
那么输出的两个参数名称是什么呢?一开始笔者这里想当然的认为是 str , name 。 相信此时的你应该和我一样认为是str , name 。

对的,你没看错返回的居然是无意义的名称 , arg0 , arg1.这就奇怪了。至于为什么呢?我现在还不想告诉你。下面会慢慢告诉你。
Spring的方法的优点
做过Javaweb开发的肯定都用过spring,springmvc , 在写controller层的时候我们都会在方法里直接写key值的名称,然后在请求地址中给相应的key赋值。
@RequestMapping(value = "/deptId", method = RequestMethod.GET)
public PagedResult<SysDept> selectSysDeptsByPK(Integer pageNumber, Integer pageSize) {
    return sysDeptService.selectSysDeptsByPK(deptId, pageNumber,  pageSize);
}
上述的controller我们在前端发送请求后会这样发送
http://{ip}:{port}/{projectName}/deptId?pageNumber=1&pageSize=5
这里我问一下你们有没有想过为什么springmvc它能够通过你传递的参数一一进行对应呢?我们上面已经尝试过通过反射是无法获取方法参数名称的。而springmvc无非就是反射操作方法的。这里是不是很神奇,不得不佩服springmvc的强大。强大到让人害怕。
反射如何实现Spring的方法
上面两个案例揭露了反射的缺点以及springmvc的强大。这里需要借助springmvc提供的一个工具ParameterNameDiscoverer 。 这个类顾名思义就是发现参数名称。在使用这个类之前我们先来了解下为什么反射获取不到方法名称。
这里需要简单说说Java执行过程,Java之所以可以跨容器是因为Java针对各个系统提供了不同jvm,所以我们开发前都需要安装不同版本的jdk,jdk里面提供了jvm,java 代码运行期间是通过jvm去操作class文件的。但是我们平时都是开发java文件的。所以在jvm执行之前会有一个编译期间。javac就是用来变异java文件为class文件的。
package com.zxhtom.test;
/**
 * Hello world!
 */
public class App {
    public void test(String str, Integer integer) {
    }
}
针对上述代码我们通过javac进行编译下试试看看效果。
javac App.java
编译完成之后会出现一个同名的class文件

然后我们在通过命令查看下这个class文件
javap -verbose App.class

通过查看App.java对应的字节码发现在javac编译的时候对于方法的名称根本不会去记录的。想想也对我执行方法的时候只需要按顺序将参数放进去就行了。根本不需要关心参数名称是什么。那么问题显而易见了jvm不需要参数名所以编译时过率了。但是我们反射想通过参数名称一一对应这样效率更快。那么是springmvc是如何解决的呢。
private static final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
public static void main(String[] args) throws ParseException, NoSuchMethodException {
    java.lang.reflect.Method testMethod = App.class.getMethod("test", String.class, Integer.class);
    String[] parameterNames = parameterNameDiscoverer.getParameterNames(testMethod);
    for (String parameterName : parameterNames) {
        System.out.println(parameterName);
    }
}

对,就是ParameterNameDiscoverer这个方法帮助了我们。这里简单说说ParameterNameDiscoverer作用。springmvc中会有一个默认的ParameterNameDiscoverer解释器DefaultParameterNameDiscoverer该类继承PrioritizedParameterNameDiscoverer。PrioritizedParameterNameDiscoverer这个类就是getParameterNames去获取方法名的。在springmvc中通过addDiscoverer方法有三个类注册到PrioritizedParameterNameDiscoverer

- KotlinReflectionParameterNameDiscoverer : Spring5.0提供 ,但是也得jdk8及以上版本使用
 - StandardReflectionParameterNameDiscoverer : Spring4.0提供 ,但是也得jdk8及以上版本使用
 - LocalVariableTableParameterNameDiscoverer :Spring2.0就有了,对JDK版本没啥要求,完全Spring自己实现的获取字段名称,逻辑复杂些,效率稍微低一点
 
总结一下就是在springmvc4.0之前springmvc都是通过自己实现的一套代码去获取字节码然后分析的。这里能力有限就不分析了。
在4.0以后因为Java8的推出弥补了这个bug.springmvc也就都采用jdk提供的功能获取参数名了。下面我们来看看jdk8是如何解决这个问题的。
Java字节码
在上一节我们通过javac , javap命令进行了Java的编译了查看。我们发现class字节码中记录的信息有【常量区,类,方法】其中对于代码的记录有位置,堆,栈,行号等等。这也是我们jvm调优的依据。但是这仅仅是我们使用简单的javac的编译。
javac -g : 编译更加全面点

经过对比发现javac 和javac -g 的区别好像是javac -g 编译信息多出LocalVariableTable信息。
| 名称 | 解释 | 
|---|---|
| LineNumberTable | 属性表存放方法的行号信息 | 
| LocalVariableTable | 属性表中存放方法的局部变量信息 | 
上图中通过javac -g 编译的信息中LocalVarableTable有三条数据,是因为在编译期间每个非静态方法第一个参数都是this.去除第一条剩下的其实就是我们需要的参数信息。但是我们这个时候去执行一下看看效果。

高级反射注意点
所谓的高级反射其实就是对jdk版本的要求,只要是jdk8的版本,就可以用jdk提供的parameter方法获取参数名了。在编译的时候需要加上 -parameters

javac的彩蛋

续点
每日一笑
今天公司放假,和老婆商量好一起去她家,出发前夕,老婆说:给我家里人的礼物买好了么?然后我去卧室全搬出来了;给她说:这是你舅的,这是你叔的,这是你爷爷的,这是你奶奶的,这是你爸的,这是你妈的,这是…………然后我俩就打起来了!
上期答案
无
# 加入战队
微信公众号

你以为反射真的无所不能?至少JDK8以后很强大的更多相关文章
- 值得推荐的C/C++框架和库 (真的很强大)  c
		
http://m.blog.csdn.net/mfcing/article/details/49001887 值得推荐的C/C++框架和库 (真的很强大) 发表于2015/10/9 21:13:14 ...
 - ReSharper的功能真的很强大主要是针对代码规范和优化,园子里介绍的也不少,如果你没有安装,那我只能表示你们会相见恨晚
		
二.ReSHarper 代码规范.单元测试.... ReSharper的功能真的很强大,主要是针对代码规范和优化,园子里介绍的也不少,如果你没有安装,那我只能表示你们会相见恨晚! 1.像命名不规范,f ...
 - 【转】 值得推荐的C/C++框架和库 (真的很强大)
		
[转] 值得推荐的C/C++框架和库 (真的很强大) 值得学习的C语言开源项目 - 1. Webbench Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个 ...
 - 真的可以啊,用C语言实现面向对象编程O O P!C语言真的无所不能~
		
解释区分一下C语言和OOP 我们经常说C语言是面向过程的,而C++是面向对象的,然而何为面向对象,什么又是面向过程呢?不管怎么样,我们最原始的目标只有一个就是实现我们所需要的功能,从这一点说它们是殊途 ...
 - 转:值得推荐的C/C++框架和库(真的很强大)
		
目录(?)[+] 值得学习的C语言开源项目 - 1 Webbench - 2 Tinyhttpd - 3 cJSON - 4 CMockery - 5 Libev - 6 Memcached - 7 ...
 - 值得推荐的C/C++框架和库 (真的很强大)
		
值得学习的C语言开源项目 - 1. Webbench Webbench是一个在Linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的 ...
 - Python小白绘图 哆唻A梦 turtle真的很强大!
		
# -*- coding: utf-8 -*- """ Created on Sat Nov 10 22:02:32 2018 @author: 10029 " ...
 - 值得推荐的C/C++框架和库 (真的很强大)〔转〕
		
http://m.blog.csdn.net/article/details?id=42541419
 - C/C++框架和库 (真的很强大) 转
		
http://blog.csdn.net/xiaoxiaoyeyaya/article/details/42541419 值得学习的C语言开源项目 - 1. Webbench Webbench ...
 
随机推荐
- Vue 中使用 typescript
			
Vue 中使用 typescript 什么是typescript typescript 为 javaScript的超集,这意味着它支持所有都JavaScript都语法.它很像JavaScript都强类 ...
 - python中下标和切片的使用
			
下标 所谓下标就是编号,就好比超市中存储柜的编号,通过这个编号就能找到相应的存储空间. Python中字符串,列表,元祖均支持下标索引. 例如: # 如果想取出部分字符,可使用下标 name=&quo ...
 - Java8 CompletableFuture 编程
			
一.简介 所谓异步调用其实就是实现一个无需等待被调用函数的返回值而让操作继续运行的方法.在 Java 语言中,简单的讲就是另启一个线程来完成调用中的部分计算,使调用继续运行或返回,而不需要等待计算结 ...
 - 前端项目优化 -Web 开发常用优化方案、Vue & React 项目优化
			
github github-myBlob 从输入URL到页面加载完成的整个过程 首先做 DNS 查询,如果这一步做了智能 DNS 解析的话,会提供访问速度最快的 IP 地址回来 接下来是 TCP 握手 ...
 - 【KakaJSON手册】04_JSON转Model_04_值过滤
			
在KakaJSON手册的第2篇文章中提过:由于JSON格式能表达的数据类型是比较有限的,所以服务器返回的JSON数据有时无法自动转换成客户端想要的数据类型 比如客户端想要的是Date类型,服务器返回的 ...
 - 写论文的第二天 Hbase集群搭建
			
日志______2019.1.24 Hbase分布式搭建 注意:hbase的使用基于hadoop,开启以及关闭需要注意顺序,由于我是的是自带的zookeeper,说以开启关闭顺序应如下 启动:hado ...
 - idea 开发中常用的26个快捷键!
			
------- udea中窗口最大化---->Windows 上: Ctrl+Shift+F12 -------- ctrl+z撤回代码 ------- c ...
 - 《MyCat分库分表策略详解》
			
在我们的项目发展到一定阶段之后,随着数据量的增大,分库分表就变成了一件非常自然的事情.常见的分库分表方式有两种:客户端模式和服务器模式,这两种的典型代表有sharding-jdbc和MyCat.所谓的 ...
 - GC回收算法&&GC回收器
			
GC回收算法 什么是垃圾? 类比日常生活中,如果一个东西经常没被使用,那么就可以说是垃圾. 同理,如果一个对象不可能再被引用,那么这个对象就是垃圾,应该被回收. 垃圾:不可能再被引用的对象. fina ...
 - Win10下安装python3.x+pycharm+autopep8
			
一.安装Python3.X 1.Pythong官方网站:http://python.org/getit/ 下载windows的安装包.有以下几个选项: 这里选择windows x86-64 exc ...