クラスローダプラスリビング例
まず、クラスローダと両親デリゲートメカニズムを導入します
JVMでは、クラスは、仮想マシンプロセスにロードされた三つのステップ、すなわちローディング、接続および初期化を含みます。クラスローダによって、このプロセスのロードが、されてClassLoader
ロードされ、クラスローダは、この義務のために、本質的に責任があります。
ジャワ自体はクラスローダ、ブートクラスローダのいくつかの種類を提供してくれBootstrap ClassLoader
、拡張クラスローダーExtension ClassLoader
は、アプリケーションクラスローダApp ClassLoader
上記の3種類に加えて、我々は我々自身のクラスローダを定義することができ、その後、私達はちょうどに必要継承したClassLoader
クラスを、そのオーバーライドするfindClass()
方法をすることができます。
もちろん、時には、あなたは私たちがクラスパスにロードするカスタムクラスローダを使用する場合とは思いませ考えるClassPath
にA class
ファイルを、次に呼び出すClass
オブジェクトをgetClassLoader()
メソッドは、私たちはクラスローダがクラスをロードすることを見つけたとき私たちのカスタムローダーは理由として、ではないのですか?これは後述します。図は、JVMのクラスローダのメカニズムであります
-
1、ブートクラスローダ
Bootstrap ClassLoader
スタートクラスローダが担当する
%JRE_HOME/lib/
たとえば、ディレクトリ内の関連するクラスファイルrt.jar
、tools.jar
およびように、例えば、私たちのString
クラスは中に格納されrt.jar
、ローダがありますBootstrap ClassLoader
-
2、拡張クラスローダー
Extension ClassLoader
拡張クラスローダは責任がある
%JRE_HOME/lib/ext
たとえば、ディレクトリ内の関連するクラスファイルrt.jar
、tools.jar
など -
3、アプリケーションクラスローダ
App ClassLoader
アプリケーションのクラスパスにロードを担当するアプリケーションクラスローダ
class
ファイル。
:それはクラスパスの問題に来るとき、私たちは、クラスパスのjavaは、我々が構成されたシステム環境変数の値を参照することを説明するCLASSPATH
が、この値に限定されません。最初に私たちのローカルプリントの小さなシリーズclasspath
の値:
.;D:\development\jdk\lib\dt.jar;D:\development\jdk\lib\tools.jar;
あなたは、これは単に私たちはカタログに使用するツールはありませんが、その後がわかりますJVM
、それは我々がどのようにロードされeclipse
、それが記述されたクラス?答えはということであるeclipse
私たちに何とかすべてを支援してきました。
我々は例を与えるの下には、見てString
いるクラスローダの種類:
System.out.println(String.class.getClassLoader());
上記文章コード出力null
。なぜ?クラスがあることがある場合ためBootstrap ClassLoader
、またはExtension ClassLoader
負荷、getClassLoader()
所定の出力null
。ため、そしてString
クラスが存在しrt.jar
、それがされますBootstrap ClassLoader
読み込まれ、その出力null
。
クラスローダに目を向けると、私たちは両親のデリゲートメカニズムを述べ、そのクラスの親デリゲートメカニズムを言わなければならない、小扁は、一つの文章にまとめました:
カスタムローダーローダーは、親のP P1を持っている場合、親ローダP1 P2が存在する場合、最上位の更なるブートストラップクラスローダ場合、それは、P2に割り当てられたタスクをロードする、父親のP1にデリゲートタスクをロードする前にロードされますクラスがロードされるまでロードされた順序に対する負荷少なく、再び~~
第二に、ClassLoader
クラスの機能を導入します
-
1.各
したがって、我々は常に可能Class
インスタンスが含まClassLoader
参照Class
オブジェクトgetClassLoader()
のクラスローダを取得する方法を。もちろん、上記のようにクラスがあることがある場合、前記Bootstrap ClassLoader
、またはExtension ClassLoader
ローディング、getClassLoader()
所定の出力null
。これは、私たちが注意を払う必要がポイントです。 -
2は、配列型のための
[]
オブジェクト、彼らはクラスローダではありませんClassLoader
ロードされるが、必要に応じて、Java仮想マシンが自動的に作成します。アレイのタイプを介してClass
オブジェクトgetClassLoader()
クラスローダ内で使用される素子のアレイを持つメソッドの戻り値として、しかし配列要素タイプは、元である場合int
ハン、getClassLoader()
メソッドが戻りますnull
// sun.misc.Launcher$AppClassLoader@73d16e93 ClassLoaderTest[] array = new ClassLoaderTest[5]; System.out.println(array.getClass().getClassLoader()); // null int[] intArray = new int[3]; System.out.println(intArray.getClass().getClassLoader()); 复制代码
-
3、我々は継承でき
ClassLoader
、独自のクラスローダを実装するクラスを
/**
* 自定义类加载器
* @Author jiawei huang
* @Since 2019年8月7日
* @Version 1.0
*/
public class MyClassLoader extends ClassLoader {
// 如果我们从其他地方进行加载,我们可以指定路径
private String classpath;
public MyClassLoader(String classPath) {
classpath = classPath;
}
public MyClassLoader() {
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = null;
// 获取该class文件字节码数组
byte[] classData = getClassData();
if (classData != null) {
// 将class的字节码数组转换成Class类的实例
clazz = defineClass(name, classData, 0, classData.length);
}
return clazz;
}
private byte[] getClassData() {
byte[] bytes = null;
File file = new File(classpath);
if (file.exists()) {
// 从文件中读取class字节数据
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int size = 0;
while ((size = in.read(buffer)) != -1) {
out.write(buffer, 0, size);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
bytes = out.toByteArray();
}
return bytes;
}
}
复制代码
我々は仮定ex
パスの下のパッケージを持ってMain.java
、我々は次のような出力を見てみましょう:
A.java
MyClassLoader loader = new MyClassLoader();
Class<?> clazzClass = loader.loadClass("ex.Main");
// 1
System.out.println(clazzClass.getClassLoader());
复制代码
上記の1
出力でsun.misc.Launcher$AppClassLoader@73d16e93
、我々は明らかに私がロードするために自分自身のクラスローダを使用していた、頼むかもしれないex.Main
理由の出力だった、うんAppClassLoader
クラスローダは、それは?実際には、これは親委譲メカニズムは、あるMyClassLoader
タスクがその親ローダにロードされているApp ClassLoader
だけで、ex.Main
クラスパスに回すので、App ClassLoader
直接返さロードした後。
-
4、ライフサイクル全体クラスは一度だけロードされ、そして一度だけでしょう
-
図5は、並列ロードローダーのサポートは、並列ローダーと呼ばれるが、私たちは、カスタムローダの初期化を持っている場合にのみ、呼び出し
ClassLoader.registerAsParallelCapable();
、どのようにそれを行うために自分自身を登録する方法を?私たちは見とりURLClassLoader
、ソースコードを。
static {
// 注册自己
ClassLoader.registerAsParallelCapable();
}
复制代码
- 6は、委員会のモデルの場合には厳密に階層的ではない、カスタムクラスローダは、平行する能力を持っている必要がある、またはローダーロックは、クラスのロード処理で開催されているため、クラスローディングは、デッドロックにつながることができます。
私たちは、詳細を確認ClassLoader
するloadClass()
方法出典:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 这里有个锁
synchronized (getClassLoadingLock(name)) {
1、先检查这个名称的类是否已经被加载过,如果是,就不再加载了,这也印证了我们第4点说的一个类只会被加载一次
Class<?> c = findLoadedClass(name);
// 2、如果该类没有被加载,那么就进行加载
if (c == null) {
long t0 = System.nanoTime();
try {
// 3、如果存在父加载器,就进行委派,这就是双亲委派机制的原理
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 4、去`Bootstrap classloader`加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 5、如果3,4都没有加载到,那就执行我们自定义的classloader,这也是为什么我们要重写findClass方法的原因所在
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
复制代码
- 7、我々だけでなく、ファイルシステムからロードされたクラスに、我々はまた、ネットワークから得ることができることができます
class
アクセス、ロードファイルbyte[]
、およびその後によるだろうに変換するオブジェクト、そして最終的に通じ~~~ Javaオブジェクトにそれを回すためにClassLoader
defineClass()
byte[]
Class
Class.newInstance()
ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
复制代码
第三に、SpringBoot
コンストラクタの中で、なぜ追加しClassLoader
たパラメータを?
初期化プロセスなので、load()
この方法は、異なる場所からクラスファイルをロードする必要があります。
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug(
"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
复制代码