概述
Javassist是一款字節碼編輯工具,可以直接編輯和生成Java生成的字節碼,以達到對.class文件進行動態修改的效果。熟練使用這套工具,可以讓Java編程更接近與動態語言編程。
下面一個方法的目的是獲取一個類加載器(ClassLoader),以加載指定的.jar或.class文件,在之後的代碼中會使用到。
-
private static ClassLoader getLocaleClassLoader() throws Exception {
-
List<URL> classPathURLs = new ArrayList<>();
-
-
classPathURLs.add(classesPath.toURI().toURL());
-
-
-
File[] jarFiles = libPath.listFiles(new FilenameFilter() {
-
@Override
-
public boolean accept(File dir, String name) {
-
return name.endsWith(".jar");
-
}
-
});
-
Assert.assertFalse(ObjectHelper.isArrayNullOrEmpty(jarFiles));
-
-
-
for (File jarFile : jarFiles) {
-
classPathURLs.add(jarFile.toURI().toURL());
-
}
-
-
-
return new URLClassLoader(classPathURLs.toArray(new URL[classPathURLs.size()]));
-
}
獲取類型信息
-
@Test
-
public void test() throws NotFoundException {
-
-
ClassPool classPool = ClassPool.getDefault();
-
-
-
CtClass ctClass = classPool.get("java.lang.String");
-
-
System.out.println(ctClass.getName());
-
System.out.println("\tpackage " + ctClass.getPackageName());
-
System.out.print("\t" + Modifier.toString(ctClass.getModifiers()) + " class " + ctClass.getSimpleName());
-
System.out.print(" extends " + ctClass.getSuperclass().getName());
-
-
if (ctClass.getInterfaces() != null) {
-
System.out.print(" implements ");
-
boolean first = true;
-
for (CtClass c : ctClass.getInterfaces()) {
-
if (first) {
-
first = false;
-
} else {
-
System.out.print(", ");
-
}
-
System.out.print(c.getName());
-
}
-
}
-
System.out.println();
-
}
修改類方法
-
@Test
-
public void test() throws Exception {
-
-
ClassLoader classLoader = getLocaleClassLoader();
-
-
Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");
-
-
-
ClassPool classPool = ClassPool.getDefault();
-
-
classPool.appendClassPath(new ClassClassPath(clazz));
-
-
CtClass ctClass = classPool.get(clazz.getName());
-
-
-
CtClass[] paramTypes = {classPool.get(String.class.getName())};
-
-
CtMethod method = ctClass.getDeclaredMethod("show", paramTypes);
-
-
CtMethod newMethod = CtNewMethod.copy(method, ctClass, null);
-
-
String oldName = method.getName() + "$Impl";
-
method.setName(oldName);
-
-
-
newMethod.setBody("{System.out.println(\"執行前\");" + oldName + "($$);System.out.println(\"執行後\");}");
-
-
ctClass.addMethod(newMethod);
-
-
-
clazz = ctClass.toClass();
-
-
clazz.getMethod("show", String.class).invoke(clazz.newInstance(), "hello");
-
ctClass.defrost();
-
}
動態創建類
-
@Test
-
public void test() throws Exception {
-
ClassPool classPool = ClassPool.getDefault();
-
-
-
CtClass ctClass = classPool.makeClass("edu.alvin.reflect.DynamiClass");
-
-
-
-
-
CtField field = new CtField(classPool.get(String.class.getName()), "value", ctClass);
-
field.setModifiers(Modifier.PRIVATE);
-
-
ctClass.addMethod(CtNewMethod.setter("setValue", field));
-
ctClass.addMethod(CtNewMethod.getter("getValue", field));
-
ctClass.addField(field);
-
-
-
-
CtConstructor constructor = new CtConstructor(null, ctClass);
-
constructor.setModifiers(Modifier.PUBLIC);
-
constructor.setBody("{}");
-
ctClass.addConstructor(constructor);
-
-
constructor = new CtConstructor(new CtClass[] {classPool.get(String.class.getName())}, ctClass);
-
constructor.setModifiers(Modifier.PUBLIC);
-
constructor.setBody("{this.value=$1;}");
-
ctClass.addConstructor(constructor);
-
-
-
CtMethod method = new CtMethod(CtClass.voidType, "run", null, ctClass);
-
method.setModifiers(Modifier.PUBLIC);
-
method.setBody("{System.out.println(\"執行結果\" + this.value);}");
-
ctClass.addMethod(method);
-
-
-
Class<?> clazz = ctClass.toClass();
-
Object obj = clazz.newInstance();
-
clazz.getMethod("setValue", String.class).invoke(obj, "hello");
-
clazz.getMethod("run").invoke(obj);
-
-
obj = clazz.getConstructor(String.class).newInstance("OK");
-
clazz.getMethod("run").invoke(obj);
-
}
創建代理類
-
@Test
-
public void test() throws Exception {
-
-
ProxyFactory factory = new ProxyFactory();
-
-
-
factory.setSuperclass(TestProxy.class);
-
-
-
factory.setFilter(new MethodFilter() {
-
@Override
-
public boolean isHandled(Method m) {
-
return m.getName().startsWith("get");
-
}
-
});
-
-
Class<?> clazz = factory.createClass();
-
TestProxy proxy = (TestProxy) clazz.newInstance();
-
((ProxyObject)proxy).setHandler(new MethodHandler() {
-
@Override
-
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
-
-
-
System.out.println(thisMethod.getName() + "被調用");
-
try {
-
Object ret = proceed.invoke(self, args);
-
System.out.println("返回值: " + ret);
-
return ret;
-
} finally {
-
System.out.println(thisMethod.getName() + "調用完畢");
-
}
-
}
-
});
-
-
proxy.setName("Alvin");
-
proxy.setValue("1000");
-
proxy.getName();
-
proxy.getValue();
-
}
其中,TestProxy類內容如下:
-
public class TestProxy {
-
private String name;
-
private String value;
-
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
public String getValue() {
-
return value;
-
}
-
public void setValue(String value) {
-
this.value = value;
-
}
-
}
獲取方法名稱
-
@Test
-
public void test() throws Exception {
-
-
ClassLoader classLoader = getLocaleClassLoader();
-
-
Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");
-
-
-
ClassPool classPool = ClassPool.getDefault();
-
classPool.appendClassPath(new ClassClassPath(clazz));
-
CtClass ctClass = classPool.get(clazz.getName());
-
-
-
CtMethod method = ctClass.getDeclaredMethod("show", ObjectHelper.argumentsToArray(CtClass.class, classPool.get("java.lang.String")));
-
-
int staticIndex = Modifier.isStatic(method.getModifiers()) ? 0 : 1;
-
-
-
MethodInfo methodInfo = method.getMethodInfo();
-
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
-
LocalVariableAttribute localVariableAttribute = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);
-
-
for (int i = 0; i < method.getParameterTypes().length; i++) {
-
System.out.println("第" + (i + 1) + "個參數名稱爲: " + localVariableAttribute.variableName(staticIndex + i));
-
}
-
}
關於“獲取方法名稱”,其主要作用是:當Java虛擬機加載.class文件後,會將類方法“去名稱化”,即丟棄掉方法形參的參數名,而是用形參的序列號來傳遞參數。如果要通過Java反射獲取參數的參數名,則必須在編輯是指定“保留參數名稱”。Javassist則不存在這個問題,對於任意方法,都能正確的獲取其參數的參數名。
Spring MVC就是通過方法參數將請求參數進行注入的,這一點比struts2 MVC要方便很多,Spring也是藉助了Javassist來實現這一點的。