Java, Classpath, Classloading => 동일한 jar / 프로젝트의 여러 버전


117

숙련 된 코더에게는 이것이 어리석은 질문 일 수 있다는 것을 알고 있습니다. 하지만 내 프로젝트에 사용 된 다른 프레임 워크 / jar에 필요한 라이브러리 (http 클라이언트)가 있습니다. 그러나 모두 다음과 같은 다른 주요 버전이 필요합니다.

httpclient-v1.jar => Required by cralwer.jar
httpclient-v2.jar => Required by restapi.jar
httpclient-v3.jar => required by foobar.jar

클래스 로더는 어떻게 든 그들을 분리 할 수있을만큼 지능적입니까? 그렇지 않습니까? 클래스가 세 jar 모두에서 동일한 경우 클래스 로더는이를 어떻게 처리합니까? 어느 것이로드되고 그 이유는 무엇입니까?

Classloader는 정확히 하나의 jar 만 픽업합니까? 아니면 임의로 클래스를 혼합합니까? 예를 들어 클래스가 Version-1.jar에서로드 된 경우 동일한 클래스 로더에서로드 된 다른 모든 클래스가 모두 동일한 jar로 이동합니까?

이 문제를 어떻게 처리합니까?

에 의해 "하나의 단위 / 패키지"로 표시되도록 항아리를 "required.jar"에 "통합"하는 방법이 Classloader있습니까?

답변:


57

클래스 로더 관련 문제는 매우 복잡한 문제입니다. 어떤 경우에도 몇 가지 사실을 기억해야합니다.

  • 응용 프로그램의 클래스 로더는 일반적으로 하나 이상입니다. 부트 스트랩 클래스 로더는 적절하게 위임합니다. 새 클래스를 인스턴스화하면보다 구체적인 클래스 로더가 호출됩니다. 로드하려는 클래스에 대한 참조를 찾지 못하면 부트 스트랩 클래스 로더에 도달 할 때까지 부모 등으로 위임합니다. 로드하려는 클래스에 대한 참조를 찾지 못하면 ClassNotFoundException이 발생합니다.

  • 동일한 이진 이름을 가진 두 개의 클래스가 있고 동일한 클래스 로더로 검색 할 수 있고로드중인 클래스를 알고 싶다면 특정 클래스 로더가 클래스 이름을 확인하는 방식 만 검사 할 수 있습니다.

  • 자바 언어 사양에 따르면 클래스 바이너리 이름에 대한 고유성 제약 조건이 없지만 내가 볼 수있는 한 각 클래스 로더에 대해 고유해야합니다.

동일한 바이너리 이름을 가진 두 개의 클래스를로드하는 방법을 알아낼 수 있으며 기본 동작을 재정의하는 두 개의 다른 클래스 로더에 의해로드 (및 모든 종속성)가 포함됩니다. 대략적인 예 :

    ClassLoader loaderA = new MyClassLoader(libPathOne);
    ClassLoader loaderB = new MyClassLoader(libPathTwo);
    Object1 obj1 = loaderA.loadClass("first.class.binary.name", true)
    Object2 obj2 = loaderB.loadClass("second.class.binary.name", true);

나는 항상 클래스 로더 사용자 정의가 까다로운 작업이라는 것을 알았습니다. 가능하면 호환되지 않는 여러 종속성을 피하는 것이 좋습니다.


13
부트 스트랩 클래스 로더는 적절하게 위임합니다. 새 클래스를 인스턴스화하면보다 구체적인 클래스 로더가 호출됩니다. 로드하려는 클래스에 대한 참조를 찾지 못하면 부모에게 위임 하지만 기본적으로 Parent First 인 클래스 로더 정책에 따라 다릅니다. 즉, 자식 클래스는 먼저 부모에게 클래스를로드하도록 요청하고 전체 계층 구조가로드하지 못한 경우에만로드됩니다.
deckingraj 2011

5
아니요-일반적으로 클래스 로더는 클래스 자체를 찾기 전에 부모에게 위임합니다. Classloader에 대한 클래스 javadoc을 참조하십시오.
Joe Kearney

1
나는 바람둥이가 여기에 설명 된 방법으로 그것을 않습니다 생각하지만, "기존의"대표단이 먼저 부모를 요청하는 것입니다
rogerdpack

@deckingraj : 인터넷 검색을 한 후 오라클 문서에서 이것을 찾았습니다. "위임 설계에서 클래스 로더 는 클래스 자체를로드 하기 전에 클래스로드를 부모에게 위임 합니다. [...] 부모 클래스 로더가 클래스를로드 할 수없는 경우, 클래스 로더는 클래스 자체를로드하려고 시도합니다. 실제로 클래스 로더는 부모가 사용할 수없는 클래스 만로드해야합니다. " 더 조사하겠습니다. 이것이 기본 구현으로 나타나면 그에 따라 응답을 업데이트합니다. ( docs.oracle.com/cd/E19501-01/819-3659/beadf/index.html )
Luca Putzu

20

각 클래스로드는 정확히 하나의 클래스를 선택합니다. 일반적으로 처음 발견되었습니다.

OSGi 는 동일한 jar의 여러 버전 문제를 해결하는 것을 목표로합니다. EquinoxApache Felix 는 OSGi의 일반적인 오픈 소스 구현입니다.


6

Classloader는 먼저 classpath에 있던 jar에서 클래스를로드합니다. 일반적으로 호환되지 않는 라이브러리 버전은 패키지에 차이가 있지만, 드물게 실제로 호환되지 않고 하나로 교체 할 수 없습니다. jarjar를 사용해보세요.


6

클래스 로더는 요청시 클래스를로드합니다. 즉, 응용 프로그램 및 관련 라이브러리에서 먼저 필요한 클래스가 다른 클래스보다 먼저로드됩니다. 종속 클래스로드 요청은 일반적으로 종속 클래스의로드 및 링크 프로세스 중에 발행됩니다.

LinkageError클래스 로더에 대해 중복 된 클래스 정의가 발견 되었다는 s가 발생할 가능성이 높습니다 (로더의 클래스 경로에 동일한 이름의 클래스가 둘 이상있는 경우). 때때로, 클래스 로더는 클래스 경로에서 발생하는 첫 번째 클래스를로드하고 중복 클래스를 무시하지만 이는 로더의 구현에 따라 다릅니다.

이러한 종류의 오류를 해결하기 위해 권장되는 방법은 종속성이 충돌하는 각 라이브러리 집합에 대해 별도의 클래스 로더를 사용하는 것입니다. 이렇게하면 클래스 로더가 라이브러리에서 클래스를로드하려고하면 다른 라이브러리 및 종속성에 액세스 할 수없는 동일한 클래스 로더가 종속 클래스를로드합니다.


1

URLClassLoaderfor require를 사용하여 diff-2 버전의 jar에서 클래스를로드 할 수 있습니다 .

URLClassLoader loader1 = new URLClassLoader(new URL[] {new File("httpclient-v1.jar").toURL()}, Thread.currentThread().getContextClassLoader());
URLClassLoader loader2 = new URLClassLoader(new URL[] {new File("httpclient-v2.jar").toURL()}, Thread.currentThread().getContextClassLoader());

Class<?> c1 = loader1.loadClass("com.abc.Hello");

Class<?> c2 = loader2.loadClass("com.abc.Hello");

BaseInterface i1 = (BaseInterface) c1.newInstance();

BaseInterface i2 = (BaseInterface) c2.newInstance();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.