부모 대표 모델
클래스 로딩의 개념은 Java 언어의 혁신으로 간주되어야합니다. 목적은 가상 머신에서 클래스로드 프로세스를 분리하고 "클래스의 완전한 자격있는 이름을 통해이 클래스를 설명하는 이진 바이트 스트림을 얻는"목적을 달성하는 것입니다. 이 기능을 구현하는 코드 모듈은 클래스 로더입니다. 클래스 로더의 기본 모델은 유명한 부모 대의원 모델입니다. 굉장히 들리지만 논리는 실제로 매우 간단합니다. 클래스를로드해야 할 때 먼저 클래스가로드되었는지 여부를 결정합니다. 그렇지 않은 경우 부모 로더에 의해로드되었는지 여부를 결정합니다. 로드를 시도하기 위해 자신의 FindClass 메소드를 부르지 않은 경우. 이것은 기본 모델입니다 (도난 이미지 침해 삭제) :
구현이 매우 간단합니다. 키는 클래스 로더 클래스의로드 클래스 메소드입니다. 소스 코드는 다음과 같습니다.
보호 클래스 <?> loadClass (문자열 이름, 부울 resolve)는 classNotFoundException {synchronized (getClassLoadingLock (name)) {// 클래스가 이미로드되었는지 확인 <?> c = findloadedClass (name); if (c == null) {long t0 = system.nanoTime (); try {if (parent! = null) {c = parent.loadclass (이름, false); } else {c = findBootStrapClassorNull (이름); }} catch (classNotFoundException e) {// 클래스를 찾을 수없는 경우 null 부모 클래스 로더에서 //를 찾을 수없는 경우 classNotFoundException} if (c == null) {// 아직 찾을 수없는 경우 findclass를 주문하여 클래스를 찾으십시오. long t1 = system.nanoTime (); c = findClass (이름); // 이것은 정의 클래스 로더입니다. 통계를 기록하십시오 Sun.misc.perfcounter.getParentDelegationTime (). AddTime (T1 -T0); sun.misc.perfcounter.getfindclasstime (). addelapsedtimefrom (t1); sun.misc.perfcounter.getfindclasses (). increment (); }} if (resolve) {class (c); } 반환 c; }}갑자기 나는 놀리는 느낌이 들었습니다. 왜 내가 예외를 직접 던지는가? 실제로 클래스 로더 클래스가 추상 클래스이기 때문입니다. 실제로 사용하면 서브 클래스를 작성합니다. 이 방법은 비즈니스가 요구하는 로딩 프로세스를 완료하기 위해 필요에 따라 다시 작성됩니다.
맞춤형 클래스 로더
클래스 로더의 서브 클래스를 사용자 정의 할 때는 두 가지 공통 방법이 있습니다. 하나는로드 클래스 메소드를 다시 작성하고 다른 하나는 findclass 메소드를 다시 작성하는 것입니다. 실제로이 두 가지 방법은 본질적으로 동일합니다. 결국 LoadClass는 FindClass를 호출하지만 논리적으로 말하면 LoadClass의 내부 논리를 직접 수정하지 않는 것이 가장 좋습니다.
개인적으로 더 좋은 방법은 FindClass에서 사용자 정의 클래스로드 방법을 다시 작성하는 것입니다.
이것이 더 나은 이유는 무엇입니까? 또한 loadclass 메소드는 부모가 제기 한 모델의 논리를 구현하기위한 장소라고 앞서 말했기 때문입니다. 승인 없이이 방법을 수정하면 모델이 파괴되고 쉽게 문제가 발생합니다. 따라서 원래 안정적인 구조를 파괴하지 않기 위해 상위 대표 모델의 프레임 워크 내에서 소규모를 변경하는 것이 가장 좋습니다. 동시에로드 클래스 메소드를 다시 작성하는 과정에서 부모가 위임 한 중복 코드를 작성할 필요가 없습니다. 코드 재사용 성의 관점 에서이 방법을 직접 수정하지 않는 것이 항상 더 나은 선택입니다.
물론, 부모 커미션 모델을 의도적으로 파괴하면 다를 것입니다.
부모 대표 모델을 파괴하십시오
왜 부모 대표 모델을 파괴합니까?
실제로, 어떤 경우에는 두 개의 다른 클래스를로드해야 할 수도 있지만 불행히도이 두 클래스의 이름은 정확히 동일합니다. 현재 부모 대의원 모델은 우리의 요구 사항을 충족시킬 수 없습니다. 부모 대의원 모델을 파괴하고 동일한 클래스 이름을 여러 번로드하기 위해로드 클래스 방법을 다시 작성해야합니다. 물론 여기에 언급 된 파괴는 지역적 의미에서만 파괴되는 것입니다.
그러나 클래스 이름은 동일합니다. JVM은 어떻게이 두 클래스를 구별 할 수 있습니까? 분명히, 이것은 세계관의 붕괴를 일으키지 않을 것입니다. 실제로 클래스는 JVM의 클래스 이름에 의해 제한 될뿐만 아니라로드하는 클래스 로더에 속합니다. 다른 클래스 로더에 의해로드 된 클래스는 실제로 서로 영향을 미치지 않습니다.
실험을하십시오.
먼저 두 수업을 작성하겠습니다.
package com.mythsman.test; public class hello {public void say () {System.out.println ( "이것은 hello v1에서 나온"); }} package com.mythsman.test; public class hello {public void say () {System.out.println ( "이것은 hello v2에서 나온 것"); }} 두 클래스 이름은 동일합니다. 유일한 차이점은 메소드의 구현이 다르다는 것입니다. 먼저 별도로 컴파일 한 다음 생성 된 클래스 파일의 이름을 Hello.class.1 및 hello.class.2로 바꿉니다.
우리의 목표는 테스트 클래스 에서이 두 클래스의 인스턴스를 만드는 것입니다.
그런 다음 새로운 테스트 클래스 com.mythsman.test.main을 만들고 주요 기능에서 두 개의 사용자 정의 클래스 로더를 만듭니다.
ClassLoader ClassLoader1 = new ClassLoader () {@Override public class <?> loadclass (String S)는 classNotFoundException {try (s.equals ( "com.mythsman.test.hello") {byte [] classBytes = files.readallbytes (paths.get/helly/helly/gets/helly/get.1)); return defineclass (s, classbytes, 0, classBytes.length); } else {return super.loadclass (s); }} catch (ioexception e) {throw new classnotfoundException (s); }}}; ClassLoader classLoader2 = new ClassLoader () {@Override public class <?> loadClass (String S)는 classNotFoundException {try (s.equals ( "com.mythsman.test.hello")) {byte [] classBytes = files.readAllBytes (paths.get ( "/home/myths/desktop/test/hello.class.2")); return defineclass (s, classbytes, 0, classBytes.length); } else {return super.loadclass (s); }} catch (ioexception e) {throw new classnotfoundException (s); }}};
이 두 클래스 로더의 목적은 hello 클래스의 두 가지 다른 바이트 코드를 별도로 연관시키는 것입니다. 바이트 코드 파일을 읽고 정의 메소드를 통해 클래스로로드해야합니다. 로드 클래스 메소드를 과부하시킵니다. FindClass 메소드를 과부하 시키면 LoadClass 메소드의 상위 대의원 처리 메커니즘으로 인해 두 번째 클래스 로더의 FindClass 메소드가 호출되지 않습니다.
그렇다면 어떻게 인스턴스를 생성합니까? 분명히, 우리는 클래스 이름 (이름 충돌)을 직접 언급 할 수 없으므로 반사 만 사용할 수 있습니다.
Object Hellov1 = classloader1.loadclass ( "com.mythsman.test.hello"). NewInstance (); 개체 hellov2 = classload2.loadclass ( "com.mythsman.test.hello"). newInstance (); hellov1.getClass (). getMethod ( "say"). invoke (hellov1); hellov2.getClass (). getMethod ( "say"). invoke (hellov2);
산출:
이것은 hello v1에서 나온 것입니다
좋아, 두 가지 부하를 완료하더라도 여전히주의를 기울여야 할 몇 가지 포인트가 있습니다.
두 클래스의 관계는 무엇입니까?
분명히이 두 클래스는 같은 클래스는 아니지만 이름은 동일합니다. 따라서 iSinstance와 같은 운영자의 결과는 다음과 같습니다.
System.out.println ( "클래스 :"+hellov1.getClass ()); System.out.println ( "class :"+hellov2.getClass ()); System.out.println ( "Has hcode : "+hellov2.getclass (). hashcode ()); system.out.println ("클래스 로더 : "+hellov1.getClass (). getClassLoader ()); System.out.println ("classLoader : "+hellov2.getClass (). getClassLoader ()); 산출:
class : com.mythsman.test.helloclass : com.mythsman.test.hellohashcode : 1581781576hashcode : 1725154839classloader : com.mythsman.test.main$1@5e2de80cclassloader : com.mythsman.test.test.test.test.test.test.test.test.test.test.test.test
그들의 클래스 이름은 실제로 동일하지만 클래스의 해시 코드는 다릅니다. 즉,이 두 가지는 본질적으로 동일한 클래스가 아니며 클래스 로더도 다릅니다 (실제로는 두 가지 내부 클래스입니다).
이 두 클래스 로더와 시스템의 3 층 클래스 로더의 관계는 무엇입니까?
첫 번째 커스텀 클래스 로더를 예로 들어 보겠습니다.
System.out.println (classloader1.getParent (). getParent (). getParent ()); system.out.println (classloader1.getParent (). getParent ()); System.out.println (classlo ader1.getParent ()); system.out.println (classloader1.getParent ()); system.out.println (classLoader1); system.out.println (classLoader.getSystemClassLoader ());
산출:
nullsun.misc.launcher $ [email protected] $ [email protected] $ [email protected] $ appclassloader@18b4aac2
물론 여기에 언급 된 부모 아들 관계는 상속 관계가 아니라 조합 관계입니다. 아동 클래스 로더는 부모 클래스 로더의 참조 (부모)를 저장합니다.