カスタムjava.lang.Object上位を作成します。
まず、プロジェクトに独自のjava.lang.Objectクラスを作成しよう:
package java.lang;
/**
* 自己创建的java.lang.Object
*
* @author hujy
* @version 1.0
* @date 2020-01-30 01:12
*/
public class Object {
static {
System.out.println("hello");
}
public static void main(String[] args) {
Object o = new Object();
}
}
mainメソッドを実行し、印刷エラーが見つかりました:
私たちは、プロセスが最上位のBootstrapClassLoaderまで高いクラスローダにターンデリゲートで、クラスが最初にロードされるときに、親クラスローダ委譲の原則に従うことを知っています。java.lang.Objectのシステムクラス、優先順位は、最終的にシステムやネイティブjava.lang.Objectクラスをロードするため、エラーが主な方法を見つけることができないと報告され、BootstrapClassLoaderによってロードされます。
カスタムクラスローダを作成します。
この前提で、我々は、カスタムクラスローダを実装し、両親の委任メカニズムのバイパスにカスタムクラスローダを必要としています。
クラスローダ、オーバーライドのloadClassメソッドを継承MyClassLoaderカスタムクラスローダは、カスタムの負荷を実装します。
package com.hujy.classloader;
import java.io.IOException;
import java.io.InputStream;
/**
* 自定义类加载器
*
* @author hujy
* @date 2020-01-30 00:56
*/
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name == null || "".equals(name)) {
throw new ClassNotFoundException();
}
InputStream is = null;
try {
String className = "/" + name.replace('.', '/') + ".class";
System.out.println(className);
// 在classpath路径下加载java/lang/Object.class文件
is = getClass().getResourceAsStream(className);
System.out.println(is);
if (is == null) {
throw new ClassNotFoundException();
}
byte[] bytes = new byte[is.available()];
is.read(bytes);
// 调用父类classLoader的defineClass方法
// 将字节数组转换为Class实例
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
MyClassLoader myClassLoader = new MyClassLoader();
try {
Class<?> clazz = Class.forName("java.lang.Object", true, myClassLoader);
System.out.println("自定义类加载器:" + clazz.newInstance().getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}
主な機能を実行します。
ヒント:「java.langの」:DOはパッケージ名を使用しません。
defineClassソースに従ってください:
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(name, b, off, len, null);
}
/* Determine protection domain, and check that:
- not define java.* class,
- signer of this class matches signers for the rest of the classes in
package.
*/
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;
}
禁止されて上書きdefineClass論理パケットでは、我々はにカスタムクラスローダでは説明できなかった、「Javaの。」プロセスの開始後に、最終的なメソッドの名前を、禁止されている「Javaの。」クラスを開始します。
私たちは、カスタムjava.lang.Objectのmyjava.lang.Objectに変更され、その後、カスタムクラスローダloadClassメソッドは、いくつかの変更を行うには:「Javaの」負荷への親クラスローダの先頭に。
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name == null || "".equals(name)) {
throw new ClassNotFoundException();
} else if (name.startsWith("java.")) {
// 由父类加载java.开头的类
return super.loadClass(name);
}
InputStream is = null;
try {
String className = "/" + name.replace('.', '/') + ".class";
System.out.println(className);
// 在classpath路径下加载java/lang/Object.class文件
is = getClass().getResourceAsStream(className);
System.out.println(is);
if (is == null) {
throw new ClassNotFoundException();
}
byte[] bytes = new byte[is.available()];
is.read(bytes);
// 调用父类classLoader的defineClass方法
// 将字节数组转换为Class实例
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
MyClassLoader myClassLoader = new MyClassLoader();
try {
Class<?> clazz = Class.forName("myjava.lang.Object", true, myClassLoader);
System.out.println("自定义类加载器:" + clazz.newInstance().getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}
再びmainメソッドを実行します。
概要
- 通常の状況下では、プロセスは、それ自体がロードされ、負荷への高いクラスローダに委託順番に、高いロードされていない、親クラスローダ委譲メカニズムに従います。
- あなたは両親のデリゲートメカニズム、オーバーライドするクラスのクラスローダを必要としているloadClassメソッドバイパスしたい場合は、一般的には推奨されません。
- 最後のメソッドdefineClassの制限として、我々はとシステムクラスをロードすることはできません「のjava。」以降。
- findClass一般的な方法ので、オーバーライドはloadClassが回避セキュリティリスクにシステムを持って、両親のデリゲートを弱体化させるのではなく、唯一のクラスパスの自己定義を達成ClassLoaderをロードするためにクラスローダを定義しました。