沒有.java 源程序 的情況下修改代碼

有時,我們只有.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。但稍微複雜一些,便會編譯錯誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章