Java Java.lang.ArrayListというクラスをカスタマイズできますか?


この問題については、ネット上では諸説あるが、似たような問題はjava.lang.System、java.lang.String、java.lang.Mathが自分でjdkの同名のクラスを書いたかどうかである.
大まかな答えは主に以下の2派に分かれています.
  • では、カスタムclassloaderは親委任メカニズムを破壊することができます(システムが持参した3つのクラスローダは特定のディレクトリの下のクラスをロードしているため、私たち自身のクラスローダが1つの特殊なディレクトリに置かれている場合、システムのローダはロードできません.つまり、結局は私たち自身のローダによってロードされます).コメント:これは明らかに実践したことのない一派の発言で、実践するとカスタムclassloaderは通用しないことがわかります.
  • はできません.classloaderをカスタマイズしても、loadClassメソッドまたはfindClassメソッドを書き換えても、自分の指定したディレクトリの下のclassファイルにロードできますが、カスタムclassloaderはClassLoaderを継承する必要があるため、loadClassメソッドは親のdefineClassメソッドを呼び出します.親のこのdefineClassはfinalメソッドであり、書き換えることはできません.
  • 
    
        protected final Class> defineClass(String name, byte[] b, int off, int len,
                                             ProtectionDomain protectionDomain)
            throws ClassFormatError
        {
            protectionDomain = preDefineClass(name, protectionDomain);
            String source = defineClassSourceLocation(protectionDomain);
            Class> c = defineClass1(name, b, off, len, protectionDomain, source);
            postDefineClass(c, protectionDomain);
            return c;
        }
        
        private ProtectionDomain preDefineClass(String name, ProtectionDomain pd)
        {
            if (!checkName(name))
                throw new NoClassDefFoundError("IllegalName: " + name);
    
            // Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
            // relies on the fact that spoofing is impossible if a class has a name
            // of the form "java.*"
            if ((name != null) && name.startsWith("java.")) {
                throw new SecurityException
                    ("Prohibited package name: " +
                     name.substring(0, name.lastIndexOf('.')));
            }
            if (pd == null) {
                pd = defaultDomain;
            }
    
            if (name != null) checkCerts(name, pd.getCodeSource());
    
            return pd;
        }
    
    

    復号化
    Java.lang.System,java.lang.String,java.lang.Mathを自分で書くこともできますが、通常の方法では、自分が書いたこのクラスがJVMにロードされる機会がないことがわかります.JVMにおけるクラスロードは親委任メカニズムを採用し、BootStrapClassLoaderは最上位の親クラス、ExtClassLoaderはBootStrapClassLoaderクラスの子クラス、AppClassLoaderはExtClassLoaderの子クラスである.
    ここではjava.lang.Stringを例にとり、私がこのクラスを使用している場合、Java仮想機会はjava.lang.Stringクラスのバイトコードをメモリにロードします.このときjdkにはjava.lang.Stringが付いていますが、私たち自身はjava.lang.Stringと書いています.JVMはどれをロードしますか?
  • クラスをロードする場合、親ローダを使用して使用するクラスを優先的にロードします.Java.lang.Stringというクラスをカスタマイズした場合、
  • このカスタムStringクラスをロードします.このカスタムStringクラスで使用するローダはAppClassLoaderで、親ローダを優先的に使用する原理に基づいて、
  • です.
  • AppClassLoaderローダの親はExtClassLoaderなので、このときStringをロードするために使用されるクラスローダはExtClassLoaderですが、クラスローダExtClassLoaderはjre/lib/extディレクトリの下にString.classクラスが見つかりません.次にExtClassLoader親のローダBootStrapClassLoader,
  • を使用します.
  • 親ローダBootStrapは、JRE/libディレクトリのrt.jarでString.classを見つけ、メモリにロードします.これがクラスローダの依頼メカニズムです.

  • 参考記事https://www.cnblogs.com/guweiwei/p/6641785.html https://www.cnblogs.com/alexlo/p/5664543.html
    究極の結論
    上の半分だけが正しい(「java.lang.System,java.lang.String,java.lang.Mathを自分で書くこともできますが、通常の方法では、自分が書いたこのクラスがJVMにロードされる機会がないことがわかります.」)jdk classloaderはカスタムclassloaderとは定かではありませんが、他に方法がないわけではありません.bootstrapclassloaderがjava.lang.ArrayListをロードできる以上、bootstrapclasscloaderがスキャンしたパケットパスはどのように指定されているのでしょうか.
    ソリューションJavaコマンドラインはbootStrapレベルclassを拡張する簡単な方法を提供する.
  • -Xbootclasspath:基本コアのJava class検索パスを完全に置き換えます.一般的ではありません.そうしないと、Javaコアclass
  • をすべて書き直します.
  • -Xbootclasspath/a:接尾辞はコアclass検索パスの後ろにあります.よく使います!!
  • -Xbootclasspath/p:プレフィックスはコアclass検索パスの前にあります.あまり使われず、不要な衝突を避けることができます.
  • まずjava.lang.JavaAliooのjavaファイルを作成し、jar javademo-javapackage-1.0.0-SNAPSHOT.jarにコンパイルしてパッケージします.
    % more java/lang/JavaAlioo.java 
    package java.lang;
    
    public class JavaAlioo {
        static {
            System.out.println("JavaAlioo     ,    ");
        }
    
        public void sayhi() {
            System.out.println("HELLO WORLD");
        }
    
        public static void main(String[] args) {
            new JavaAlioo().sayhi();
        }
    }
    

    次にテスト用例JavaTest.javaを作成する
    
    % more JavaTest.java 
    package com.alioo;
    
    public class JavaTest {
    
        public static void main(String[] args) throws Exception {
    
            Class a = Class.forName("java.lang.JavaAlioo");
    
        }
    }
    
    

    通常の実行では、次のエラーが表示されます.
    Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
    	at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)
    	at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
    	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    	at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
    	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
    	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
    	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    	at java.lang.Class.forName0(Native Method)
    	at java.lang.Class.forName(Class.java:264)
    	at com.alioo.JavaTest.main(JavaTest.java:7)
    

    javademo-javapackage-1.0.0-SNAPSHOT.jarをbootstrappathに指定して再実行して発見すればいい
    起動コマンドに次のパラメータを追加します:-Xbootclasspath/p:/Users/mac/work/gitstudy/javademo/javademo-javapackage/target/javademo-javapackage-1.0.0-SNAPSHOT.jar
    説明ここでは-Xbootclasspath/pと-Xbootclasspath/aを使用してもよい
    
    JavaAlioo     ,    
    
    

    さらにjdkの既存クラスjava.lang.ArrayListを検証して効果を見てみましょう
    package java.lang;
    
    public class ArrayList {
        static {
            System.out.println("ArrayList     ,    ");
        }
    
        public void sayhi() {
            System.out.println("ArrayListArrayListArrayListArrayList");
        }
    
    }
    
    

    起動コマンドに次のパラメータを追加します:-Xbootclasspath/p:/Users/mac/work/gitstudy/javademo/javademo-javapackage/target/javademo-javapackage-1.0.0-SNAPSHOT.jar
    ここでは-Xbootclasspath/pしか使用できません.-Xbootclasspath/aを使用するとjdkが持参したjava.lang.ArrayListがロードされます.
    テスト結果
    ArrayList     ,    
    

    これで、私たちは自分が書いたjava.lang.JavaAlioo、java.lang.ArrayListの実行効果を成功させました.驚いて驚きません.意外ではありません.もう一つの方法を提供してもいいですが、bootclasspathを増やすことで、apiは以下のように考えられます.
    instrumentation.appendToBootstrapClassLoaderSearch(new JarFile(spyJarFile));
    

    オープンソースのarthasでは、興味のある方は読むことができます.
    参考記事https://www.cnblogs.com/duanxz/p/3482311.html