[java] Java, Classpath, Classloading => 동일한 jar / 프로젝트의 여러 버전
숙련 된 코더에게는 이것이 어리석은 질문 일 수 있다는 것을 알고 있습니다. 하지만 내 프로젝트에 사용 된 다른 프레임 워크 / 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
있습니까?
답변
클래스 로더 관련 문제는 매우 복잡한 문제입니다. 어떤 경우에도 몇 가지 사실을 기억해야합니다.
-
응용 프로그램의 클래스 로더는 일반적으로 하나 이상입니다. 부트 스트랩 클래스 로더는 적절하게 위임합니다. 새 클래스를 인스턴스화하면보다 구체적인 클래스 로더가 호출됩니다. 로드하려는 클래스에 대한 참조를 찾지 못하면 부트 스트랩 클래스 로더에 도달 할 때까지 부모 등으로 위임합니다. 로드하려는 클래스에 대한 참조를 찾지 못하면 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);
나는 항상 클래스 로더 사용자 정의가 까다로운 작업이라는 것을 알았습니다. 가능하면 호환되지 않는 여러 종속성을 피하는 것이 좋습니다.
답변
각 클래스로드는 정확히 하나의 클래스를 선택합니다. 일반적으로 처음 발견되었습니다.
OSGi 는 동일한 jar의 여러 버전 문제를 해결하는 것을 목표로합니다. Equinox 및 Apache Felix 는 OSGi의 일반적인 오픈 소스 구현입니다.
답변
Classloader는 먼저 classpath에 있던 jar에서 클래스를로드합니다. 일반적으로 호환되지 않는 라이브러리 버전은 패키지에 차이가 있지만, 드물게 실제로 호환되지 않고 하나로 교체 할 수 없습니다. jarjar를 사용해보세요.
답변
클래스 로더는 요청시 클래스를로드합니다. 즉, 응용 프로그램 및 관련 라이브러리에서 먼저 필요한 클래스가 다른 클래스보다 먼저로드됩니다. 종속 클래스로드 요청은 일반적으로 종속 클래스의로드 및 링크 프로세스 중에 발행됩니다.
LinkageError
클래스 로더에 대해 중복 된 클래스 정의가 발견 되었다는 s가 발생할 가능성이 높습니다 (로더의 클래스 경로에 동일한 이름의 클래스가 둘 이상있는 경우). 때때로, 클래스 로더는 클래스 경로에서 발생하는 첫 번째 클래스를로드하고 중복 클래스를 무시하지만 이는 로더의 구현에 따라 다릅니다.
이러한 종류의 오류를 해결하기 위해 권장되는 방법은 종속성이 충돌하는 각 라이브러리 집합에 대해 별도의 클래스 로더를 사용하는 것입니다. 이렇게하면 클래스 로더가 라이브러리에서 클래스를로드하려고하면 다른 라이브러리 및 종속성에 액세스 할 수없는 동일한 클래스 로더가 종속 클래스를로드합니다.
답변
URLClassLoader
for 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();