Java编译成.class 有两种方式
使用javac,默认使用的release方式。
release模式下对于函数参数会改变
使用不同的编译方法编译类文件,可以发现,使用debug方法编译产生的Test.class(如下)和用release方法编译(如下)出来的参数名上有所区别。
debug方法:
release方法: 这也可以 解释为什么在spring MVC 中controller的注解初始化参数建议指定名称:
项目部署如果使用的是release版本,这样str(参数中的str发生变化)而非RequestMapping中的{str}这样就对应不起来了。。。然而dubug模式下函数参数不会发生变化。。
---------------------------------------------------------------------------------------------------------------------------------------------
在springMVC有如下代码:
public Boolean employeeHasRole(Integer employeeId, Integer roleId) {
//
}
请求为XX/XXX.do?employeeId=1&roleId=1
在编写MVC框架时,想通过反射获取类方法的参数,也就是不通过注解,将http请求中的参数和方法中的参数进行映射。参考链接中有如下三种方法:
- compile with debug information(以debug模式编译)。
- The default implementation uses asm
ClassReader
to do so(asm字节码增强实现)。
- java8 method.getParameters()方法。
以下是方法2的具体实现,代码地址:
package com.bwx.mvc.xmltools;
import com.bwx.test.controller.UserAction;
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;
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;
/**
* Created by bwx on 2017/9/6.
*/
public class MethodParamNamesScanner {
/**
* 获取方法参数名列表
*
* @param clazz
* @param m
* @return
* @throws IOException
*/
public static List<String> getMethodParamNames(Class<?> clazz, Method m) throws IOException {
try (InputStream in = clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class")) {
return getMethodParamNames(in,m);
}
}
public static List<String> getMethodParamNames(InputStream in, Method m) throws IOException {
try (InputStream ins=in) {
return getParamNames(ins,
new EnclosingMetadata(m.getName(),Type.getMethodDescriptor(m), m.getParameterTypes().length));
}
}
/**
* 获取构造器参数名列表
*
* @param clazz
* @param constructor
* @return
*/
public static List<String> 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<String>();
}
public static List<String> 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<String>();
}
/**
* 获取参数名列表辅助方法
*
* @param in
* @param m
* @return
* @throws IOException
*/
private static List<String> 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树接口形式访问
List<MethodNode> methods = cn.methods;
List<String> list = new ArrayList<String>();
for (int i = 0; i < methods.size(); ++i) {
List<LocalVariable> varNames = new ArrayList<LocalVariable>();
MethodNode method = methods.get(i);
// 验证方法签名
if (method.desc.equals(m.desc)&&method.name.equals(m.name)) {
// System.out.println("desc->"+method.desc+":"+m.desc);
List<LocalVariableNode> local_variables = method.localVariables;
for (int l = 0; l < local_variables.size(); l++) {
String varName = local_variables.get(l).name;
// index-记录了正确的方法本地变量索引。(方法本地变量顺序可能会被打乱。而index记录了原始的顺序)
int index = local_variables.get(l).index;
if (!"this".equals(varName)) // 非静态方法,第一个参数是this
varNames.add(new LocalVariable(index, varName));
}
LocalVariable[] tmpArr = varNames.toArray(new LocalVariable[varNames.size()]);
// 根据index来重排序,以确保正确的顺序
Arrays.sort(tmpArr);
for (int j = 0; j < m.size; j++) {
list.add(tmpArr[j].name);
}
break;
}
}
return list;
}
/**
* 方法本地变量索引和参数名封装
* @author xby Administrator
*/
static class LocalVariable implements Comparable<LocalVariable> {
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;
}
}
/**
* 封装方法描述和参数个数
*
* @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 : UserAction.class.getDeclaredMethods()) {
List<String> list = getMethodParamNames(UserAction.class, m);
System.out.println(m.getName() + ":");
for (String str : list) {
System.out.println(str);
}
System.out.println("------------------------");
}
}
}
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>${asm.version}</version>
</dependency>
其中 这篇代码,虽然在psvm中运行正常,但是将代码用到web项目(tomcat)中,会出现class not found,因为
ClassReader cr = new ClassReader(className);
调用了classloader,
public ClassReader(String var1) throws IOException { this(a(ClassLoader.getSystemResourceAsStream(var1.replace('.', '/') + ".class"), true)); }
该方法中少写了斜杠“/”,特此记录。