当前位置: 首页 > 科技观察

一种没有源代码文件的Java程序修改方法

时间:2023-03-14 13:53:19 科技观察

一、前言公司的一个老项目突然报错,发现跟踪代码有逻辑问题,但是由于公司代码管理不当,源代码被丢失的。目前只有可运行的jar包;如果要修复这个问题,只能修改字节码文件,然后重新打包部署。2、准备①:需要反编译xxx.jar包;②:反编译工具:JD-JUI.exe;③:代码编辑工具(IDEA);3、两种解决方案:方案一:第一步,在IDEA中新建一个maven项目步骤2,将xxx.jar导入到项目中步骤3,定位到要修改的xxx.class文件,在中创建同路径的包src-->main-->java,并新建一个xxx.java,然后将xxx.class文件的内容复制到当前的xxx.java中。注意:当前文件除了依赖第三方库依赖外,还可能依赖其他文件,需要同时复制。复制时,包名保持一致。第四步,找到xxx.jar包下的pom.xml文件,复制到当前项目的pom.xml文件中,解决依赖第三方库的问题。第五步,修改新建的java源码,修改完成后右键重新编译文件。Step6.编译完成后,在目标文件下找到新生成的xxx.class文件。Step7、用压缩包工具打开原来的xxx.jar包,找到xxx.class文件,用新生成的xxx.class文件覆盖即可。优点:如果修改文件的依赖少,操作简单快捷缺点:如果修改文件的依赖多,除了考虑依赖的第三方包外,还必须粘贴复制其他文件,这既费时又麻烦。本来只需要改一个文件,其他的都需要Documentation支持,造成依赖爆炸问题。解决方案二:JavaAssist简介:JavaAssist又称编译时类,是Jboss用于分析、编辑和创建Java字节码的开源类库。可以在不知道虚拟机指令的情况下,直接用java编码。可以动态改变类的结构,或者动态生成类。情况一:重新生成字节码文件publicstaticvoidmain(String[]args)throwsException{//CtClass对象容器ClassPoolclassPool=ClassPool.getDefault();//在CtClass对象容器中创建一个publicJATest类CtClassjATestClazz=classPool.makeClass("com.tyun.javaassist.MyTest");//***属性操作//添加privateintidCtFieldCtFieldctIdField=newCtField(classPool.getCtClass("int"),"id"toMyTestclass,jATestClazz);ctIdField.setModifiers(Modifier.PRIVATE);jATestClazz.addField(ctIdField);//将私有字符串用户名添加到MyTest类CtFieldctUserNameField=newCtField(classPool.getCtClass("java.lang.String"),"username",jATestClazz);ctUserNameField.setModifiers(Modifier.PRIVATE);jATestClazz.addField(ctUserNameField);//添加getter和setter方法jATestClazz.addMethod(CtNewMethod.getter("getId",ctIdField));jATestClazz.addMethod(CtNewMethod.getter("setId",ctIdField));jATestClazz.addMethod(CtNewMethod.getter("getUsername",ctUserNameFieldld));jATestClazz.addMethod(CtNewMethod.getter("setUsername",ctUserNameField));//添加构造函数CtConstructorctConstructor=newCtConstructor(newCtClass[]{},jATestClazz);//添加构造方法体StringBuffersb=newStringBuffer();sb.append("{\n").append("this.id=27;\n").append("this.username=\"Drogon\";\n}");ctConstructor.setBody(sb.toString());jATestClazz.addConstructor(ctConstructor);//添加自定义方法CtMethodmethod=newCtMethod(CtClass.voidType,"sayHello",newCtClass[]{},jATestClazz);方法.setModifiers(Modifier.PUBLIC);StringBufferprintSb=newStringBuffer();printSb.append("{\nSystem.out.println(\"begin!\");\n").append("System.out.println(id);\n").append("System.out.println(用户名);\n").append("System.out.println(\"end!\");\n").append("}");方法.setBody(printSb.toString());jATestClazz.addMethod(方法);//生成一个Class对象Classclazz=jATestClazz.toClass();对象对象=clazz.newInstance();//反射执行方法clazz.getMethod("sayHello",newClass[]{}).invoke(object,newObject[]{});//将生成的class写入文件FileOutputStreamfileOutputStream=newFileOutputStream(newFile("JATest.class"));fileOutputStream.write(jATestClazz.toBytecode());文件输出流。关闭();}运行代码生成MyTest.class文件;案例二:在不修改源码的情况下修改字节码文件中的指定方法publicclassTyunTest{publicstaticvoidmain(String[]args){sayHello();}publicstaticvoidsayHello(){System.out.println("Hello,world");}}将源文件输入到jar中;使用JavaAssist读取jar包,修改字节码文件;ClassPoolclassPool=ClassPool.getDefault();//设置jar包路径classPool.insertClassPath("/Users/wyw_yong/Desktop/tyun/Tiyun.jar");//获取修改后的类CtClassctClazz=classPool.getCtClass("TyunTest");//获取类中的CtMethod方法sayHelloMethod=ctClazz.getDeclaredMethod("sayHello");//修改类中的方法内容sayHelloMethod.setBody("System.out.println(\"hellojavaAssist\");");类newTestJarClass=ctClazz.toClass();//使用修改后的类创建对象对象newTestJar=newTestJarClass.newInstance();方法newPrintTestMethod=newTestJarClass.getDeclaredMethod("sayHello");newPrintTestMethod.invoke(newTestJar);//解锁代码并恢复可编辑状态ctClazz.defrost();//写出到外部存储器ctClazz.writeFile();执行代码,查看文件路径下的文本段代码文件;你可以看到方法“Hello,world”中打印的输出变成了“hellojavaAssist”。四、以上是丢失源码的情况,修改字节码文件只有两种方法。