有时,我们只有.jar 文件而无法得到获取源码(.java),但又需要对代码进行修改,这时可以考虑以下几种方法:
一、.jar 文件反编译成.java ,构建工程,修改后,再生成.jar
对于比较复杂的项目,这种方法是非常困难的,因为反编译之后或多或少会与源码有不同,并且涉及refer 一些库,以及原编译工具的版本问题,所以,要想真正恢复源代码,需要一些时间。
推荐反编译工具 jd-gui,C++编写的小工具,简单,方便https://github.com/java-decompiler/jd-gui/releases
二、直接修改.class
1、修改.class的方法网上有许多,如使用一些工具直接修改等,但最终本人使用Javassist 获得了成功。
我们知道 Java 字节码以二进制的形式存储在 class 文件中,每一个 class 文件包含一个 Java 类或接口。Javassist 就是一个用来处理 Java 字节码的类库。
import java.io.IOException;
import javassist.*;
import javassist.ClassPool;
public class InsertCodeToMethod {
private static final boolean True = false;
public static void main(String args[]) throws NotFoundException, CannotCompileException, IOException{
//获取class文件
ClassPool cPool = new ClassPool(true);
cPool.insertClassPath("C:\\Users\\503061752\\Desktop\\New folder (3)");
cPool.importPackage("com.ge.dspmicro.robot.subscriptionmachineadapter.RobotSubscriptionListener");
//获取该class对象
CtClass clas = cPool.get("com.ge.dspmicro.robot.subscriptionmachineadapter.RobotSubscriptionMachineAdapterImpl");
if(clas==null){
//方法未找到
System.out.println("classname "+clas+" not found");
}else{
insertToLine(clas,"initrobotstatus");
clas.writeFile();
//替换原有的文件,必须写,否则文件不更新
clas.writeFile("C:\\Users\\503061752\\Desktop\\New folder (3)");
}
}
public static void insertToLine (CtClass ccl,String method) throws NotFoundException, CannotCompileException{
//获取方法信息,如果方法不存在,则抛出异常
CtMethod ctMethod = ccl.getDeclaredMethod(method);
ctMethod.insertAt(576,true,"discrete_readaddr.put(\"PhmHotISO\", Integer.valueOf(23));\n");
}
}
以上是示例代码,主要记录一下过程中的坑:
(1) 必须使用 clas.writeFile("C:\\Users\\503061752\\Desktop\\New folder (3)");更新原java 文件
(2) 编译报错,如找不到某class 或把引用类的成员变量认成class,且不能识别,考虑是否可以用cPool.importPackage 显示的把外部类导入
(3) 对于范型符号需要特殊处理,否则程序编译报错
如"Map
"J1-1554-003 " 写成\"J1-1554-003\"
(4) javassist 接口查询 https://www.javassist.org/html/javassist/CtBehavior.html
void |
insertAfter(java.lang.String src) |
Inserts bytecode at the end of the body. |
---|---|---|
void |
insertAfter(java.lang.String src, boolean asFinally) |
Inserts bytecode at the end of the body. |
int |
insertAt(int lineNum, boolean modify, java.lang.String src) |
Inserts bytecode at the specified line in the body. |
int |
insertAt(int lineNum, java.lang.String src) |
Inserts bytecode at the specified line in the body. |
void |
insertBefore(java.lang.String src) |
Inserts bytecode at the beginning of the body. |
void |
setBody(java.lang.String src) |
Sets a method/constructor body. |
---|---|---|
void |
setBody(java.lang.String src, java.lang.String delegateObj, java.lang.String delegateMethod) |
Sets a method/constructor body. |
(5) 换行格式 + " this.phm_sn.put(Short.valueOf((short)113), \"J1-1550-002\");"
(6)不能写成ctMethod.insertAt(240,true,"this.phm_sn.put((short)22, \"J2-1241-001\");\n");
应写成ctMethod.insertAt(240,true,"this.phm_sn.put(Short.valueOf((short)22), \"J2-1241-001\");\n");
否则报错:Exception in thread "main" javassist.CannotCompileException: [source error] put(short,java.lang.String) not found in java.util.Map
使用体会,javassist 有自己的一些小规则,只能对.class 进行简单的修改,如果涉及逻辑变动,最好还是修改源码
2、简单的类也可以直接反编译成.java 源码,用eclipse 修改,然后自动更新生成.class。但稍微复杂一些,便会编译错误。