在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

一起單測引起的項目加載失敗慘案

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2024-08-23 13:47 ? 次閱讀

一、前言

最近在開發一個功能模塊時,在功能自測階段,通過使用單測測試功能的完整性,在測試單測聯通性使用到靜態方法測試時,發現單測報錯,通過查閱解決方案發現需要對Javaassist包進行排包或者升版本處理。通過排包解決掉單測報錯,在部署項目時發現頻繁報bean注入失敗問題,最終定位發現是因為對Javaassist包排包引起的bean加載失敗。故而對Javaassist包相關知識進行學習整理文章如下。

單測相關報錯信息如下:

Powermock - java.lang.IllegalStateException: Failed to transform class

解決單測報錯的文章鏈接:

https://stackoverflow.com/questions/32854688/powermock-java-lang-illegalstateexception-failed-to-transform-class

二、問題復現

1、前期準備

首先使用了Spring框架新建一個demo,并寫一個簡單測試類對問題進行復現。

UserService的定義:

public interface UserService {
    void save(User user);
}

UserServiceImpl的實現代碼:

@Service
public class UserServiceImpl implements UserService {
    private UserDao userDao;

    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save(User user) {
        userDao.save(user);
    }
}

這里我們使用了Spring框架的@Service@Autowired注解,以便讓Spring框架自動裝配UserDao實例。

但是,在我們的POM文件中,雖然我們添加了對Spring框架的依賴,但是并沒有添加Javaassist庫的依賴。而UserServiceImpl中確實使用了Javaassist庫來進行字節碼操作, UserServiceImpl的具體實現代碼:

public class UserServiceImpl implements UserService {
    // ...
    private static final String USER_CLASS_NAME = "com.example.User";

    private static final Class USER_CLASS;

    static {
        try {
            USER_CLASS = Class.forName(USER_CLASS_NAME);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public void save(User user) {
        try {
            // 創建一個ClassPool對象
            ClassPool cp = ClassPool.getDefault();

            // 從ClassPool中獲取一個CtClass對象
            CtClass ctClass = cp.get(USER_CLASS_NAME);

            // 獲取無參構造器
            CtConstructor ctConstructor = ctClass.getDeclaredConstructor(new CtClass[]{});

            // 獲取save方法
            CtMethod saveMethod = ctClass.getDeclaredMethod("save");

            // 生成代碼
            saveMethod.insertBefore("{System.out.println("插入代碼前");}");
            saveMethod.insertAfter("{System.out.println("插入代碼后");}");

            // 生成新的字節碼并裝載到內存
            Class targetClass = ctClass.toClass();
            Object instance = targetClass.newInstance();

            // 調用save方法
            Method method = targetClass.getMethod("save", USER_CLASS);
            method.invoke(instance, user);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

在這段代碼中,我們通過Javaassist庫生成了一個新的字節碼,并使用反射機制將其實例化,并在調用save()方法前后插入了一些代碼。但是,由于Javaassist庫缺失,導致項目在啟動過程中無法正確加載UserServiceImpl的實例,從而出現了下述錯誤信息。

2、報錯信息

在部署程序時發現,應用無法正常啟動,并出現如下錯誤信息:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in file [C:workspaceprojecttargetclassescomexampleUserServiceImpl.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.UserService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.example.UserService.()

從錯誤信息中我們可以看到,應用在創建UserService的實例時遇到了問題,無法實例化成功。

3、解決方案

為了修復這個問題,我們需要在POM文件中加入對Javaassist庫的依賴:


    org.javassist
    javassist
    3.27.0-GA


添加依賴后,重新編譯并部署應用程序即可正常運行

三、Javaassist包

1、什么是Javaassist?

Javaassist 是由東京工業大學數學和計算機科學系的 Shigeru Chiba (千葉滋)教授創造的。Javaassist 作為實現動態字節碼生成的一個開源類庫,極大地簡化了 Java 開發者對底層字節碼操作的難度,讓開發者能夠更加輕松地在運行時動態生成類、修改類文件來達到輕量級 AOP、ORM、基于代理的遠程方法調用等功能。

(Javaassist已加入了開放源代碼JBoss 應用服務器項目,通過使用Javaassist對字節碼操作為JBoss實現動態AOP框架。)

2、什么是動態編程

動態編程是相對于靜態編程而言的,平時我們討論比較多的就是靜態編程語言,例如Java,與動態編程語言,例如JavaScript。那二者有什么明顯的區別呢?簡單的說就是在靜態編程中,類型檢查是在編譯時完成的,而動態編程中類型檢查是在運行時完成的。所謂動態編程就是繞過編譯過程在運行時進行操作的技術,在Java中有如下幾種方式:

?反射

這個搞Java的應該比較熟悉,原理也就是通過在運行時獲得類型信息然后做相應的操作。由于Java執行過程中是將類型載入虛擬機中的,在運行時我們就可以動態獲取到所有類型的信息。只能獲取卻不能修改類型信息。

?動態編譯

動態編譯是從Java 6開始支持的,主要是通過一個JavaCompiler接口來完成的。通過這種方式我們可以直接編譯一個已經存在的java文件,也可以在內存中動態生成Java代碼,動態編譯執行。

?調用JavaScript引擎

早在Java 6就加入了對Script(JSR223)的支持。這是一個腳本框架,提供了讓腳本語言來訪問Java內部的方法。你可以在運行的時候找到腳本引擎,然后調用這個引擎去執行腳本。這個腳本API允許你為腳本語言提供Java支持。

?動態生成字節碼

這種技術通過操作Java字節碼的方式在JVM中生成新類或者對已經加載的類動態添加元素。

3、動態編程解決什么問題?

在靜態語言中引入動態特性,主要是為了解決一些使用場景的痛點。其實完全使用靜態編程也辦的到,只是付出的代價比較高,沒有動態編程來的優雅。例如依賴注入框架Spring使用了反射,而Dagger2 卻使用了代碼生成的方式(APT)。

例如:

a: 在那些依賴關系需要動態確認的場景: b: 需要在運行時動態插入代碼的場景,比如動態代理的實現。 c: 通過配置文件來實現相關功能的場景

4、Javassit使用方法

javassistjboss的一個子項目,其主要的優點,在于簡單,而且快速。直接使用java編碼的形式,而不需要了解虛擬機指令,就能動態改變類的結構,或者動態生成類。

操作java字節碼的工具有兩個比較流行,一個是ASM,一個是Javassit

?ASM直接操作字節碼指令,執行效率高,要求使用者掌握Java類字節碼文件格式及指令,對使用者的要求比較高。

?Javassit 提供了更高級的API,執行效率相對較,但無需掌握字節碼指令的知識,對使用者要求較低。

應用層面來講一般使用建議優先選擇Javassit,如果后續發現Javassit 成為了整個應用的效率瓶頸的話可以再考慮ASM。當然如果開發的是一個基礎類庫,或者基礎平臺,還是直接使用ASM吧,相信從事這方面工作的開發者能力應該比較高。

Javassist中最為重要的是ClassPoolCtClass CtMethod 以及 CtField這幾個類。

wKgaombIIl6AHddMAADIgDZAmU4088.png

?ClassPool:一個基于HashMap實現的CtClass對象容器,其中是類名稱,是表示該類的CtClass對象。默認的ClassPool使用與底層JVM相同的類路徑,因此在某些情況下,可能需要向ClassPool添加類路徑或類字節。

? getDefault (): 返回默認的ClassPool ,單例模式,一般通過該方法創建我們的ClassPool

? appendClassPath(ClassPath cp), insertClassPath(ClassPath cp) : 將一個ClassPath加到類搜索路徑的末尾位置或插入到起始位置。通常通過該方法寫入額外的類搜索路徑,以解決多個類加載器環境中找不到類問題;

? importPackage(String packageName):導入包;

? makeClass(String classname):創建一個空類,沒有變量和方法,后序通過CtClass的函數進行添加;

? get(String classname)、getCtClass(String classname) : 根據類路徑名獲取該類的CtClass對象,用于后續的編輯。

?CtClass:表示一個類,這些CtClass對象可以從ClassPool獲得。

?debugDump; String類型,如果生成.class文件,保存在這個目錄下。

?setName(String name): 給類重命名;

?setSuperclass(CtClass clazz): 設置父類;

?addField(CtField f, Initializer init): 添加字段(屬性),初始值見CtField;

?addMethod(CtMethod m): 添加方法(函數);

?toBytecode(): 返回修改后的字節碼。需要注意的是一旦調用該方法,則無法繼續修改CtClass

?toClass(): 將修改后的CtClass加載至當前線程的上下文類加載器中,CtClasstoClass方法是通過調用本方法實現。需要注意的是一旦調用該方法,則無法繼續修改已經被加載的CtClass

?writeFile(String directoryName): 根據CtClass生成 .class 文件;

?defrost(): 解凍類,用于使用了toclass()toBytecodewriteFile(),類已經被JVM加載,Javassist凍結CtClass后;

?detach(): 避免內存溢出,從ClassPool中移除一些不需要的CtClass

?CtMethods:表示類中的方法。

?insertBefore(String src):在方法的起始位置插入代碼;

?insertAfter(String src):在方法的所有 return 語句前插入代碼以確保語句能夠被執行,除非遇到exception;

?insertAt(int lineNum, String src):在指定的位置插入代碼;

?addCatch(String src, CtClass exceptionType):將方法內語句作為try的代碼塊,插入catch代碼塊src;

?setBody(String src):將方法的內容設置為要寫入的代碼,當方法被 abstract修飾時,該修飾符被移除;

?setModifiers(int mod):設置訪問級別,一般使用Modifier調用常量;

?invoke(Object obj, Object... args):反射調用字節碼生成類的方法。

?CtFields :表示類中的字段。

?CtField(CtClass type, String name, CtClass declaring) :構造函數,添加字段類型,名稱,所屬的類;

?CtField.Initializer constant():CtClass使用addField時初始值的設置;

?setModifiers(int mod):設置訪問級別,一般使用Modifier調用常量。

?$開頭的特殊字符

符號 具體含義
$0, $1, $2, … $0=this,$1表示方法的第一個參數,依次類推,如果方法是靜態的,則 $0 不可用
$args 方法參數數組.它的類型為 Object[],$args[0]=1 , 1,1,args[1]=$2
$r 返回結果的類型,用于強制類型轉換
$w 包裝器類型,用于強制類型轉換,當返回值是包裝類型時,可以用此來強轉
$_ 返回值,一般在insertAfter中用到,用于得到原方法的返回值
$slg 參數類型數組,$sig[0]表示第一個參數類型
$type 返回值類型,一般在insertAfter中用到,即$_的類型
$class $0或this的類型
$e 異常類型

5、常用的Java插樁工具有哪些?

Java 插樁工具是一種能夠修改 Java 字節碼的工具,通過在應用程序運行時動態修改字節碼來實現對程序的監控跟蹤調試優化等功能。

工具 字節碼抽象級別 具體描述
ASM、BCEL 低級 庫需要直接在字節碼級別上進行操作。通常,它們提供大多數功能豐富的功能,但與其他字節碼操作工具相比,它們的使用也最復雜。
Javaassist 中級 庫提供了字節碼的某種抽象級別,并簡化了其修改。例如,代替修改字節碼,可以使用類似于Java的語法進行更改,然后將其編譯為字節碼,然后由使用的庫修改為原始字節碼。通常,它們缺少修改后的代碼驗證的功能-這意味著,錯誤可能在修改準備過程中被忽略,然后在運行時被發現。
AspectJ、CGLib 高級 庫使用高級指令進行操作,并且通常配備有用于語法驗證的工具集。不幸的是,從修改后的字節碼進行的最高抽象化通常會導致某些功能的喪失,這些功能僅在直接修改字節碼時可用。

四、總結

本文通過對由于Javaassist包缺失導致項目啟動過程中bean加載失敗的問題進行復現,并通過demo進行實例分析,解釋了因為缺失Javaassist庫導致的應用程序啟動失敗問題。并對Javaassist包相關知識進行介紹,后續會繼續對Javaassist相關知識進行學習補充。

建議大家在構建Maven項目時,仔細檢查POM文件中的依賴,確保沒有漏掉任何必要的庫,以免因為遺漏而引起不必要的問題。

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 測試
    +關注

    關注

    8

    文章

    5354

    瀏覽量

    126854
  • 編程
    +關注

    關注

    88

    文章

    3633

    瀏覽量

    93853
收藏 人收藏

    評論

    相關推薦

    AN-166:與Linduino一起飛行中更新

    電子發燒友網站提供《AN-166:與Linduino一起飛行中更新.pdf》資料免費下載
    發表于 01-12 10:09 ?0次下載
    AN-166:與Linduino<b class='flag-5'>一起</b>飛行中更新

    和Dr Peter一起學KiCad 4.8:設計規則檢查(DRC)

    和Dr Peter一起學KiCad 4.8:設計規則檢查(DRC)
    的頭像 發表于 12-25 14:55 ?245次閱讀
    和Dr Peter<b class='flag-5'>一起</b>學KiCad 4.8:設計規則檢查(DRC)

    采用THS4503驅動ADS1675,一起使用時ADC發燙的原因?

    參考設計采用THS4503驅動ADS1675,一起使用時,ADC發燙,但是能正常工作,去掉THS4503后,ADC不怎么燙,想問下,是THS4503輸出電流過大造成的,還是什么原因?目前我前面的放大電路是端輸出, 是否可以
    發表于 12-24 07:02

    請問tas5731m PBTL模式,單聲道輸出(AB連一起,CD連一起)如何實現左右聲道的混音輸出?

    請問tas5731m PBTL模式,單聲道輸出(AB連一起,CD連一起)如何實現左右聲道的混音輸出 還有開發軟件里能直接拉線么
    發表于 10-17 06:23

    隔離電源的地能接在一起嗎,隔離電源能不能直接共地使用

    不能接在一起。在使用隔離電源時,需要將隔離電源的輸入和輸出端的地線分別接在接地柱和接地線上,而不能將它們接在一起。實際上,如果將隔離電源兩端的地線接在一起,會導致接地系統的干擾,降低系統的工作穩定性
    的頭像 發表于 10-01 16:27 ?2912次閱讀

    模擬地和電源地能接在一起

    模擬地和電源地是否能接在一起,取決于電子系統的具體要求和設計。在電子系統中,地(Ground)是個共同的參考點,用于構建電位參考平面。電源地是所有電源網絡的參考點,用于確保電源的穩定性和系統的正常工作。模擬地則與模擬電路相關,用于提供參考電位。
    的頭像 發表于 09-15 11:43 ?1388次閱讀

    控件加載失敗

    labview2013自帶的個例子程序 NIHyperTrend Graph XML Configuration.vi 調試正常,打包成安裝程序到另臺電腦上安裝,提示控件加載失敗
    發表于 08-13 11:29

    exe文件運行,部分控件加載失敗

    labview2013自帶的個例子程序NIHyperTrend Graph XML Configuration.vi編譯后到另臺電腦上運行,顯示控件加載失敗。請大佬們幫研究
    發表于 08-13 11:19

    實際上手體驗maven面對沖突Jar包的加載規則

    、 問題背景 相信大家在日常的開發過程中都遇到過Jar包沖突的問題,emm,在最近處理業務需求時我也遇到了不同版本jar包沖突導致項目加載出錯的問題。主要是個完整的
    的頭像 發表于 08-08 11:22 ?252次閱讀
    實際上手體驗maven面對沖突Jar包的<b class='flag-5'>加載</b>規則

    DAC8771RGZ電流輸出端IOUT和電壓輸VOUT出端是連在一起的,是否可以不并在一起

    請教下DAC8771RGZ這款芯片,看官方demo板,電流輸出端IOUT和電壓輸VOUT出端是連在一起的,是否可以不并在一起,分成兩路,單獨分別輸出電流或電壓嗎?
    發表于 08-08 07:59

    可以將USB主機與Esp8266一起使用嗎?

    我可以將 USB 主機(USB A 型母頭)與 Esp8266 一起使用嗎? 為什么我不能使用它
    發表于 07-19 06:49

    image文件是make flash的時候一起制作并燒錄的嗎?

    :Failed to mount or format filesystem image文件是make flash的時候一起制作并燒錄的嗎,需不需要先用spiffsgen.py生成image文件
    發表于 06-26 08:16

    六類網線可以和強電一起走嗎

    六類網線理論上不建議和強電一起走。從布線規范的角度來看,弱電線路和強電線路通常不建議共用同橋架,以避免潛在的電磁干擾。然而,多年的施工經驗表明,在某些情況下,強電線和弱電網線可能一起
    的頭像 發表于 04-19 09:55 ?5914次閱讀

    #新開端、新起點,2024一起加油#

    \"新開端、新起點,2024一起加油\" 這句話充滿了積極向上的精神和對未來的期待。新開端和新起點意味著我們有機會摒棄過去的不足,以個全新的姿態開始新的旅程。而\"
    發表于 02-26 21:01

    一起聊聊faro orbis-它的商業價值有哪些

    Orbis 由 GeoSLAM 專有的 SLAM 算法提供支持,對于希望更快地提供高精度交付成果、減少人為錯誤并提高整體效率的行業專業人士來說,這是革命性的轉變。利用混合移動掃描與固定式 Flash 掃描功能的強大功能,與 Orbis(應對現代掃描挑戰的終極搭檔)一起踏上轉變項目
    的頭像 發表于 02-20 13:31 ?372次閱讀
    <b class='flag-5'>一起</b>聊聊faro orbis-它的商業價值有哪些
    主站蜘蛛池模板: 一级毛片不收费| 婷婷免费视频| 亚洲狠狠狠一区二区三区| 69pao强力打造在线| 欧美精品色精品一区二区三区| 四虎网站在线播放| 日本成片免费高清| 久久精品亚洲精品国产色婷| 三级黄色网址| 伊人久久大香线蕉综合bd高清| aaaaa级毛片免费视频| 亚洲综合区图片小说区| 免费a网址| 午色| 日本高清中文字幕在线观穿线视频 | 欧美日韩视频综合一区无弹窗| 久青草国产手机视频免费观看| 国产高清成人| 手机看片福利在线| 涩综合| 在线观看你懂的网站| 色88888久久久久久影院| 久久久久国产精品免费免费不卡| 爱爱小说视频永久免费网站| 日日干夜夜操视频| 国产欧美日韩haodiaose| 中文字幕一区精品欧美| 高h乱肉辣文辣书阁| 性free中国美女hd| 欧美三级影院| xx综合网| 久久久久免费观看| 亚洲夂夂婷婷色拍ww47| 亚洲国产精品综合久久网络| 欧美三级一级| 永久免费观看午夜视频在线| 又粗又硬又大久久久| 狠狠干夜夜草| 九九视频热| 欧洲综合网| 一级毛片免费不卡在线视频 |