Javassist
这个库和asm经常使用,它的性能稍差一点
<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.27.0-GA</version> </dependency> |
获取一个类加载器(ClassLoader),以加载指定的.jar或.class文件
private ClassLoader getLocaleClassLoader() throws Exception { List<URL> classPathURLs = new ArrayList<>(); // 加载.class文件路径 File classesPath = new File("com.assist.TestHelloWorld2"); classPathURLs.add(classesPath.toURI().toURL()); // 获取所有的jar文件 File libPath = new File("com.assist.TestHelloWorld2"); File[] jarFiles = libPath.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jar"); } }); // 将jar文件路径写入集合 for (File jarFile : jarFiles) { classPathURLs.add(jarFile.toURI().toURL()); } // 实例化类加载器 return new URLClassLoader(classPathURLs.toArray(new URL[classPathURLs.size()])); } |
获取类的类型信息
public static 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(); } |
修改类的信息
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()); // 获取String类型参数集合 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(); // 解冻一个类,对应freeze方法 } |
获取方法名称
public void test1() 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",new CtClass[]{}); // 判断是否为静态方法 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)); } } |
动态创建类
public void test() throws Exception { ClassPool classPool = ClassPool.getDefault(); // 创建一个类 CtClass ctClass = classPool.makeClass("edu.alvin.reflect.DynamiClass"); // 为类型设置接口 //ctClass.setInterfaces(new CtClass[] {classPool.get(Runnable.class.getName())}); // 为类型设置字段 CtField field = new CtField(classPool.get(String.class.getName()), "value", ctClass); field.setModifiers(Modifier.PRIVATE); // 添加getter和setter方法 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); } |
创建代理类
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; } } public static void test() throws Exception { // 实例化代理类工厂 ProxyFactory factory = new ProxyFactory(); //设置父类,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 { //拦截后前置处理,改写name属性的内容 //实际情况可根据需求修改 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(); } |
创建一个类并添加方法
public class TestProxy { public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { // 用于取得字节码类,必须在当前的classpath中,使用全称 ClassPool pool = ClassPool.getDefault(); /** * makeClass() cannot create a new interface; makeInterface() in * ClassPool can do. Member methods in an interface can be created with * abstractMethod() in CtNewMethod. Note that an interface method is an * abstract method. */ CtClass ccClass = pool.makeClass("Point"); String bodyString = "{System.out.println(\"Call to method \");}"; //为新创建的类新加一个方法execute,无任何参数 CtMethod n1 = CtNewMethod.make(CtClass.voidType, "execute", null, null, bodyString, ccClass); ccClass.addMethod(n1); //新加第二个方法 bodyString = "public Integer getNumber(Integer num);"; CtMethod n2 = CtNewMethod.make(bodyString, ccClass);//直接创建一个方法,带有一个int的参数和返回值 n2.setBody("{System.out.println(\"Point Call to method \");return $1;}"); ccClass.addMethod(n2); /** * 这里无法用new的形式来创建一个对象,因为已经classloader中不能有两个相同的对象,否则会报异常如下: *Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): *attempted duplicate class definition for name: "Point" **/ Object oo = ccClass.toClass().newInstance(); Method mms = oo.getClass().getMethod("execute", null); System.out.println("new class name is : " + oo.getClass().getName()); System.out.println("new class's method is : " + mms.invoke(oo, null)); System.out.println("---------------------------------------------"); //这一行代码将class冻结了,下面无法再对类多编辑或者修改,下面的setName会报异常如: //Exception in thread "main" java.lang.RuntimeException: Point class is frozen ccClass.freeze(); try{ ccClass.setName("Point2"); }catch (Exception e) { System.out.println(e); } //对已经冻结的class解冻之后还可以继续编辑修改 ccClass.defrost(); System.out.println("------------- 上面的代码是对的,下面的代码将会无法执行出结果,会报错------------------------"); Class[] params = new Class[1]; Integer num = new Integer(0); params[0] = num.getClass(); mms = oo.getClass().getMethod("getNumber",params); System.out.println("new class name is : " + oo.getClass().getName()); System.out.println("new class's method is : " + mms.invoke(oo, 100)); System.out.println("---------------------------------------------"); } } |