有時,我們只有.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<String, String> tags = new HashMap<>();"報錯時可以試着寫成"java.util.Map/*<String, String>*/ tags = new java.util.HashMap/*<>*/();"
"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\");"
使用體會,javassist 有自己的一些小規則,只能對.class 進行簡單的修改,如果涉及邏輯變動,最好還是修改源碼
2、簡單的類也可以直接反編譯成.java 源碼,用eclipse 修改,然後自動更新生成.class。但稍微複雜一些,便會編譯錯誤。