摘要:據(jù)說(shuō)已經(jīng)原生支持參數(shù)名讀取了。本文以為例進(jìn)行說(shuō)明通過(guò)字節(jié)碼操作工具我們可以實(shí)現(xiàn)運(yùn)行時(shí)參數(shù)名的讀寫。簡(jiǎn)單說(shuō)說(shuō)原理字節(jié)碼為每個(gè)方法保存了一份方法本地變量列表。
據(jù)說(shuō)Java8已經(jīng)原生支持參數(shù)名讀取了。具體不是很清楚。本文以java7為例進(jìn)行說(shuō)明.
通過(guò)ASM字節(jié)碼操作工具我們可以實(shí)現(xiàn)運(yùn)行時(shí)參數(shù)名的讀寫。
簡(jiǎn)單說(shuō)說(shuō)原理:java字節(jié)碼為每個(gè)方法保存了一份方法本地變量列表。可以通過(guò)ASM獲取這個(gè)列表。但是可能會(huì)獲得列表順序與期望的不一致。我們獲取的本地變量了列表使用不同的編譯器編譯得到的順序可能不同。一般而言,通過(guò)javac編譯出來(lái)的列表順序是按照本地變量使用的順序。而我們期望的是聲明的順序。如下面這個(gè)方法:
public String handle(String a,String b){ String c; c="str"; return a+b; }
得到的localVariable列表順序:我們期望的是this,a,b,c.而實(shí)際上讀取到的會(huì)是this ,c ,a,b
這個(gè)this是非靜態(tài)方法中的第一個(gè)本地變量。所以我們會(huì)在下面的代碼中對(duì)這個(gè)順序進(jìn)行重排序以解決該問(wèn)題。
不多說(shuō),直接看代碼:
package net.xby1993.springmvc.controller; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.LocalVariableNode; import org.objectweb.asm.tree.MethodNode; public class MethodParamNamesScanner { /** * 獲取方法參數(shù)名列表 * * @param clazz * @param m * @return * @throws IOException */ public static ListgetMethodParamNames(Class> clazz, Method m) throws IOException { try (InputStream in = clazz.getResourceAsStream("/" + clazz.getName().replace(".", "/") + ".class")) { return getMethodParamNames(in,m); } } public static List getMethodParamNames(InputStream in, Method m) throws IOException { try (InputStream ins=in) { return getParamNames(ins, new EnclosingMetadata(m.getName(),Type.getMethodDescriptor(m), m.getParameterTypes().length)); } } /** * 獲取構(gòu)造器參數(shù)名列表 * * @param clazz * @param constructor * @return */ public static List getConstructorParamNames(Class> clazz, Constructor> constructor) { try (InputStream in = clazz.getResourceAsStream("/" + clazz.getName().replace(".", "/") + ".class")) { return getConstructorParamNames(in, constructor); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return new ArrayList (); } public static List getConstructorParamNames(InputStream ins, Constructor> constructor) { try (InputStream in = ins) { return getParamNames(in, new EnclosingMetadata(constructor.getName,Type.getConstructorDescriptor(constructor), constructor.getParameterTypes().length)); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return new ArrayList (); } /** * 獲取參數(shù)名列表輔助方法 * * @param in * @param m * @return * @throws IOException */ private static List getParamNames(InputStream in, EnclosingMetadata m) throws IOException { ClassReader cr = new ClassReader(in); ClassNode cn = new ClassNode(); cr.accept(cn, ClassReader.EXPAND_FRAMES);// 建議EXPAND_FRAMES // ASM樹接口形式訪問(wèn) List methods = cn.methods; List list = new ArrayList (); for (int i = 0; i < methods.size(); ++i) { List varNames = new ArrayList (); MethodNode method = methods.get(i); // 驗(yàn)證方法簽名 if (method.desc.equals(m.desc)&&method.name.equals(m.name)) { // System.out.println("desc->"+method.desc+":"+m.desc); List local_variables = method.localVariables; for (int l = 0; l < local_variables.size(); l++) { String varName = local_variables.get(l).name; // index-記錄了正確的方法本地變量索引。(方法本地變量順序可能會(huì)被打亂。而index記錄了原始的順序) int index = local_variables.get(l).index; if (!"this".equals(varName)) // 非靜態(tài)方法,第一個(gè)參數(shù)是this varNames.add(new LocalVariable(index, varName)); } LocalVariable[] tmpArr = varNames.toArray(new LocalVariable[varNames.size()]); // 根據(jù)index來(lái)重排序,以確保正確的順序 Arrays.sort(tmpArr); for (int j = 0; j < m.size; j++) { list.add(tmpArr[j].name); } break; } } return list; } /** * 方法本地變量索引和參數(shù)名封裝 * @author xby Administrator */ static class LocalVariable implements Comparable { public int index; public String name; public LocalVariable(int index, String name) { this.index = index; this.name = name; } public int compareTo(LocalVariable o) { return this.index - o.index; } } /** * 封裝方法描述和參數(shù)個(gè)數(shù) * * @author xby Administrator */ static class EnclosingMetadata { //method name public String name; // method description public String desc; // params size public int size; public EnclosingMetadata(String name,String desc, int size) { this.name=name; this.desc = desc; this.size = size; } } public static void main(String[] args) throws IOException { for (Method m : AdminController.class.getDeclaredMethods()) { List list = getMethodParamNames(AdminController.class, m); System.out.println(m.getName() + ":"); for (String str : list) { System.out.println(str); } System.out.println("------------------------"); } } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65802.html
摘要:但是這種方式對(duì)于接口和抽象方法是不管用的,因?yàn)槌橄蠓椒]有方法體,也就沒有局部變量,自然也就沒有局部變量表了是通過(guò)接口跟語(yǔ)句綁定然后生成代理類來(lái)實(shí)現(xiàn)的,因此它無(wú)法通過(guò)解析字節(jié)碼來(lái)獲取方法參數(shù)名。 聲明:本文屬原創(chuàng)文章,首發(fā)于公號(hào):程序員自學(xué)之道,轉(zhuǎn)載請(qǐng)注明出處! 發(fā)現(xiàn)問(wèn)題 對(duì)Java字節(jié)碼有一定了解的朋友應(yīng)該知道,Java 在編譯的時(shí)候,默認(rèn)會(huì)將方法參數(shù)名丟棄,因此我們無(wú)法在運(yùn)行時(shí)獲取...
每篇一句 胡適:多談些問(wèn)題,少聊些主義 前言 Spring MVC和MyBatis作為當(dāng)下最為流行的兩個(gè)框架,大家平時(shí)開發(fā)中都在用。如果你往深了一步去思考,你應(yīng)該會(huì)有這樣的疑問(wèn): 在使用Spring MVC的時(shí)候,你即使不使用注解,只要參數(shù)名和請(qǐng)求參數(shù)的key對(duì)應(yīng)上了,就能自動(dòng)完成數(shù)值的封裝 在使用MyBatis(接口模式)時(shí),接口方法向xml里的SQL語(yǔ)句傳參時(shí),必須(當(dāng)然不是100%的必須,...
摘要:動(dòng)態(tài)編程使用場(chǎng)景通過(guò)配置生成代碼,減少重復(fù)編碼,降低維護(hù)成本。動(dòng)態(tài)生成字節(jié)碼操作字節(jié)碼的工具有,其中有兩個(gè)比較流行的,一個(gè)是,一個(gè)是。 作者簡(jiǎn)介 傳恒,一個(gè)喜歡攝影和旅游的軟件工程師,先后從事餓了么物流蜂鳥自配送和蜂鳥眾包的開發(fā),現(xiàn)在轉(zhuǎn)戰(zhàn) Java,目前負(fù)責(zé)物流策略組分流相關(guān)業(yè)務(wù)的開發(fā)。 什么是動(dòng)態(tài)編程 動(dòng)態(tài)編程是相對(duì)于靜態(tài)編程而言的,平時(shí)我們討論比較多的靜態(tài)編程語(yǔ)言例如Java, 與動(dòng)態(tài)...
摘要:什么是字節(jié)碼程序通過(guò)編譯之后生成文件就是字節(jié)碼集合正是有這樣一種中間碼字節(jié)碼,使得等函數(shù)語(yǔ)言只用實(shí)現(xiàn)一個(gè)編譯器即可運(yùn)行在上。 什么是字節(jié)碼? java程序通過(guò)javac編譯之后生成文件.class就是字節(jié)碼集合,正是有這樣一種中間碼(字節(jié)碼),使得scala/groovy/clojure等函數(shù)語(yǔ)言只用實(shí)現(xiàn)一個(gè)編譯器即可運(yùn)行在JVM上。看看一段簡(jiǎn)單代碼。 public long ...
摘要:原文如果覺得我的文章對(duì)你有用,請(qǐng)隨意贊賞本文整理運(yùn)行時(shí)獲取方法參數(shù)名的兩種方法,的最新的方法和之前的方法。文件中的調(diào)試信息上文介紹了通過(guò)新增的反射運(yùn)行時(shí)獲取方法參數(shù)名。 原文:http://nullwy.me/2017/04/java...如果覺得我的文章對(duì)你有用,請(qǐng)隨意贊賞 本文整理 Java 運(yùn)行時(shí)獲取方法參數(shù)名的兩種方法,Java 8 的最新的方法和 Java 8 之前的方法。 ...
閱讀 1002·2021-09-30 09:58
閱讀 2829·2021-09-09 11:55
閱讀 2001·2021-09-01 11:41
閱讀 991·2019-08-30 15:55
閱讀 3350·2019-08-30 12:50
閱讀 3495·2019-08-29 18:37
閱讀 3295·2019-08-29 16:37
閱讀 2011·2019-08-29 13:00