日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

在舊版 JVM 上使用 J2SE 5.0 特性

 SportyBabe 2006-01-19

如果試圖在早期的 JVM 上運行J2SE 5.0 生成的代碼,將會得到 java.lang.UnsupportedClassVersionError 錯誤。開放源代碼的 Retroweaver 項目可以消除 JDK 5.0 (Set JDK Compliance = 5.0) 編譯器限制.Retroweaver 使用 classworking 技術(shù)來修改由 JDK 5.0 編譯器生成的二進制類表示,以便這些類可以與早期的 JVM (e.g j2sdk1.4.2) 一起使用。


Retroweaver 包含兩個邏輯組件:一個字節(jié)碼增強器和一個運行時庫。字節(jié)碼增強器使用 classworking 技術(shù)來修改由 JDK 5.0 編譯器生成的類文件,使得這些類可以用于舊版 JVM。作為類文件修改的一部分,Retroweaver 可能需要替換對添加到 J2SE 5.0 中的標(biāo)準(zhǔn)類的引用。實際的替換類包含在運行時庫中,以便在您執(zhí)行修改過的代碼時它們是可用的。

按照標(biāo)準(zhǔn)開發(fā)周期來說,字節(jié)碼增強器需要在 Java 代碼編譯之后、類文件為部署而打包之前運行。在您使用一個 IDE 時,該更改是一個問題 ——“集成”一個類轉(zhuǎn)換工具到“開發(fā)環(huán)境”是很痛苦的事情,因為 IDE 一般假設(shè)它們擁有類文件。限制這一痛苦的一種方式是,只對 IDE 中的大多數(shù)測試使用 JDK 5.0。這樣,您只需要在想要為部署打包文件或者想要測試實際的部署 JVM 時轉(zhuǎn)換類文件。如果使用 Ant 風(fēng)格的構(gòu)建過程,就沒有問題;只添加 Retroweaver 字節(jié)碼增強器作為編譯之后的一個步驟。

Retroweaver 具有一個小小的限制:它并不支持也包含在 J2SE 5.0 中的所有添加到標(biāo)準(zhǔn) Java 類的特性。如果您的代碼使用任何添加到 J2SE 5.0 中的類或方法,那么就將在試圖加載舊版 JVM 中的代碼時得到錯誤,哪怕是在 Retroweaver 處理完成之后也如此。


J2SE 5.0 的更改既發(fā)生在 JVM 中,也發(fā)生在實際的 Java 語言,但是 JVM 更改相當(dāng)小。有一個新的字符可以用于字節(jié)碼中的標(biāo)識符中 ("+"),一些處理類引用的指令發(fā)生了修改,還有一個不同的方法用于處理合成組件。 Retroweaver 在字節(jié)碼增強步驟中處理這些 JVM 更改,方法是把這些更改返回原樣,即替換成用于 J2SE 5.0 之前相同目的的方法(比如標(biāo)識符中的 + 字符,就是用 $ 取代它)。

包含在 J2SE 5.0 中的語言更改要稍微復(fù)雜一點。一些最有趣的更改,比如增強的 for 循環(huán),基本上只是語法更改,即為表示編程操作提供快捷方式。比如泛型更改 —— 泛型類型信息 —— 由編譯器用于實施編譯時安全,但是生成的字節(jié)碼仍然到處使用強制轉(zhuǎn)換。但是大多數(shù)更改使用了添加到核心 Java API 中的類或方法,所以您不能直接使用為 JDK 5.0 生成的字節(jié)碼并將它直接運行在早期的 JVM 上。Retroweaver 為支持 J2SE 5.0 語言更改所需的新類提供其自己的等價物,并且用對其自己的類的引用替換對標(biāo)準(zhǔn)類的引用,這是字節(jié)碼增強步驟的一部分。

Retroweaver 字節(jié)碼增強不能對所有的 J2SE 5.0 語言更改提供完全支持。例如,沒有對處理注釋的運行時支持,因為運行時支持涉及到對基本 JVM 類加載器實現(xiàn)的更改。但是一般來說,只是不支持那些不會影響普通用戶的小特性。

Retroweaver 發(fā)揮作用
使用 Retroweaver 簡直是太容易了。可以使用一個簡單的 GUI 界面或者控制臺應(yīng)用程序來在應(yīng)用程序類文件上運行字節(jié)碼增強。兩種方式都只要在將要轉(zhuǎn)換的類文件樹的根目錄指出 Retroweaver 即可。在運行時,如果使用任何需要運行時支持的特性(比如 enums),那么就需要在類路徑中包含 Retroweaver 運行時 jar。

清單 1. 簡單的 J2SE 5.0 enum 示例

package com.sosnoski.dwct;

public enum Primitive
{
    BOOLEAN, BYTE, CHARACTER, DOUBLE, FLOAT, INT, LONG, SHORT;
    
    public static void main(String[] args) {
        for (Primitive p : Primitive.values()) {
            int size = -1;
            switch (p) {
                case BOOLEAN:
                case BYTE:
                    size = 1;
                    break;
                case CHARACTER:
                case SHORT:
                    size = 2;
                    break;
                case FLOAT:
                case INT:
                    size = 4;
                    break;
                case DOUBLE:
                case LONG:
                    size = 8;
                    break;
            }
            System.out.println(p + " is size " + size);
        }
    }
}

使用 JDK 5.0 編譯并運行清單 1 代碼會給出清單 2 中的輸出。但是不能在早期的 JDK 下編譯或運行清單 1 代碼;由于特定于 J2SE 5.0 的特性會導(dǎo)致編譯失敗,而運行失敗會拋出 java.lang.UnsupportedClassVersionError 異常。

清單 2. enum 示例輸出

[dennis@notebook code]$ java -cp classes com.sosnoski.dwct.Primitive
BOOLEAN is size 1
BYTE is size 1
CHARACTER is size 2
DOUBLE is size 8
FLOAT is size 4
INT is size 4
LONG is size 8
SHORT is size 2

清單 3 展示了在 Primitive 類上運行 Retroweaver。這個類實際上編譯為兩個類文件,一個用于 enum 類,另一個支持在 switch 語句中使用 enum。(注意,清單代碼換行是為了適應(yīng)頁面寬度。)

清單 3. enum 示例輸出

[dennis@notebook code]$ java -cp retro/release/retroweaver.jar:retro/lib/bcel-5.1.jar:retro/lib/
  jace.jar:retro/lib/Regex.jar com.rc.retroweaver.Weaver -source classes
[RetroWeaver] Weaving /home/dennis/writing/articles/devworks/series/may05/code/
  classes/com/sosnoski/dwct/Primitive$1.class
[RetroWeaver] Weaving /home/dennis/writing/articles/devworks/series/may05/code/
  classes/com/sosnoski/dwct/Primitive.class

在運行 Retroweaver 之后,這些類就可以用于 JDK 5.0 和 JDK 1.4 JVM 上了。當(dāng)使用 1.4 JVM 運行修改后的類時,輸出與 清單 2 中的相同。Retroweaver 提供命令行選項來指定舊的 1.3 和 1.2 JVM 以取代默認(rèn)的 1.4 目標(biāo),但是我下載的運行時 jar 版本需要 1.4,我不想重新構(gòu)建它以檢查對早期 JVM 的支持。


我用于對 JDK 5.0 實現(xiàn) toString() 方法生成的 com.sosnoski.asm.ToStringAgent 類對于舊版 JVM 有一個小小的問題:它使用 J2SE 5.0 中新增的 instrumentation API 來在運行時截取類加載和修改類。在早期 JVM 中截取類加載不太靈活,但是并不是不可能 —— 只需要用您自己的版本來取代用于應(yīng)用程序的類加載器就可以了。由于所有的應(yīng)用程序類都是通過您的自定義類加載器加載的,所以在它們被實際提供給 JVM 之前,您可以自由地修改類表示。

清單 4. ToStringLoader 代碼

package com.sosnoski.asm;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

public class ToStringLoader extends URLClassLoader
{
    private ToStringLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    // override of ClassLoader method
    protected Class findClass(String name) throws ClassNotFoundException {
        String resname = name.replace(‘.‘, ‘/‘) + ".class";
        InputStream is = getResourceAsStream(resname);
        if (is == null) {
            System.err.println("Unable to load class " + name +
                " for annotation checking");
            return super.findClass(name);
        } else {
            System.out.println("Processing class " + name);
            try {
                
                // read the entire content into byte array
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] buff = new byte[1024];
                int length;
                while ((length = is.read(buff)) >= 0) {
                    bos.write(buff, 0, length);
                }
                byte[] bytes = bos.toByteArray();
                
                // scan class binary format to find fields for toString() method
                ClassReader creader = new ClassReader(bytes);
                FieldCollector visitor = new FieldCollector();
                creader.accept(visitor, true);
                FieldInfo[] fields = visitor.getFields();
                if (fields.length > 0) {
                    
                    // annotated fields present, generate the toString() method
                    System.out.println("Modifying " + name);
                    ClassWriter writer = new ClassWriter(false);
                    ToStringGenerator gen = new ToStringGenerator(writer,
                            name.replace(‘.‘, ‘/‘), fields);
                    creader.accept(gen, false);
                    bytes = writer.toByteArray();
                }
                
                // return the (possibly modified) class
                return defineClass(bytes, 0, bytes.length);
                
            } catch (IOException e) {
                throw new ClassNotFoundException("Error reading class " + name);
            }
        }
    }

    public static void main(String[] args) {
        if (args.length >= 1) {
            try {
                
                // get paths to be used for loading
                ClassLoader base = ClassLoader.getSystemClassLoader();
                URL[] urls;
                if (base instanceof URLClassLoader) {
                    urls = ((URLClassLoader)base).getURLs();
                } else {
                    urls = new URL[] { new File(".").toURI().toURL() };
                }
                
                // load the target class using custom class loader
                ToStringLoader loader =
                    new ToStringLoader(urls, base.getParent());
                Class clas = loader.loadClass(args[0]);
                    
                // invoke the "main" method of the application class
                Class[] ptypes = new Class[] { args.getClass() };
                Method main = clas.getDeclaredMethod("main", ptypes);
                String[] pargs = new String[args.length-1];
                System.arraycopy(args, 1, pargs, 0, pargs.length);
                Thread.currentThread().setContextClassLoader(loader);
                main.invoke(null, new Object[] { pargs });
                
            } catch (Exception e) {
                e.printStackTrace();
            }
            
        } else {
            System.out.println("Usage: com.sosnoski.asm.ToStringLoader " +
                "report-class main-class args...");
        }
    }
}

為了使用清單 4 代碼,我仍然需要使用 JDK 5.0 編譯與注釋相關(guān)的代碼,然后在產(chǎn)生的類集合上運行 Retroweaver。我也需要在類路徑中包含 retroweaver.jar 運行時代碼(因為 Retroweaver 對已轉(zhuǎn)換的注釋使用它自己的類)。

清單 5. JDK 1.4 上的 ToString 注釋

[dennis@notebook code]$ java -cp classes:retro/release/retroweaver-rt.jar:lib/
  asm-2.0.RC1.jar:lib/asm-commons-2.0.RC1.jar
  com.sosnoski.asm.ToStringLoader com.sosnoski.dwct.Run
Processing class com.sosnoski.dwct.Run
Processing class com.sosnoski.dwct.Name
Modifying com.sosnoski.dwct.Name
Processing class com.sosnoski.dwct.Address
Modifying com.sosnoski.dwct.Address
Processing class com.sosnoski.dwct.Customer
Modifying com.sosnoski.dwct.Customer
Customer: #=12345
 Name: Dennis Michael Sosnoski
 Address: street=1234 5th St. city=Redmond state=WA zip=98052
 homePhone=425 555-1212 dayPhone=425 555-1213

清單 5 顯示了生成的 toString() 方法的輸出. 用于 JDK 1.4 的自定義類加載器方法不提供 JDK 5.0 instrumentation API 的完全靈活性,但是它適用于所有最近的 JVM,并允許您修改任何應(yīng)用程序類。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多