Java에서 컴파일 시간과 런타임 종속성의 차이점은 무엇입니까? 클래스 경로와 관련이 있지만 어떻게 다릅니 까?
답변:
컴파일 시간 종속성 : CLASSPATH
아티팩트를 컴파일 하려면에 종속성이 필요합니다 . new
클래스 호출 , 무언가 확장 또는 구현 (직접 또는 간접), 직접 reference.method()
표기법을 사용한 메서드 호출 과 같이 코드에 하드 코딩 된 종속성에 대한 일종의 "참조"가 있기 때문에 생성 됩니다.
런타임 종속성 : CLASSPATH
아티팩트를 실행 하려면에 종속성이 필요합니다 . 종속성에 액세스하는 코드를 실행하기 때문에 생성됩니다 (하드 코딩 된 방식 또는 리플렉션 등을 통해).
컴파일 타임 종속성은 일반적으로 런타임 종속성을 의미하지만 컴파일 타임에만 종속성을 가질 수 있습니다. 이는 Java가 해당 클래스에 대한 첫 번째 액세스에서만 클래스 종속성을 링크한다는 사실을 기반으로합니다. 따라서 코드 경로가 순회되지 않기 때문에 런타임에 특정 클래스에 액세스하지 않는 경우 Java는 클래스와 해당 종속성을 모두 무시합니다.
이것의 예
C.java에서 (C.class 생성) :
package dependencies;
public class C { }
A.java에서 (A.class 생성) :
package dependencies;
public class A {
public static class B {
public String toString() {
C c = new C();
return c.toString();
}
}
public static void main(String[] args) {
if (args.length > 0) {
B b = new B();
System.out.println(b.toString());
}
}
}
이 경우 A
를 C
통해 컴파일 시간 종속성이 B
있지만 java dependencies.A
JVM이 실행될 때 B
의 종속성 을 해결하려고 시도하므로 실행할 때 일부 매개 변수를 전달하면 C에만 런타임 종속성이 있습니다. . 이 기능을 사용하면 코드 경로에서 사용하는 클래스의 종속성 만 런타임에 제공하고 아티팩트에있는 나머지 클래스의 종속성을 무시할 수 있습니다.C
B b = new B()
쉬운 예는 서블릿 API와 같은 API를 보는 것입니다. 서블릿을 컴파일하려면 servlet-api.jar가 필요하지만 런타임시 서블릿 컨테이너는 서블릿 API 구현을 제공하므로 런타임 클래스 경로에 servlet-api.jar을 추가 할 필요가 없습니다.
컴파일러는 라이브러리에 대한 호출을 컴파일하기 위해 올바른 클래스 경로가 필요합니다 (컴파일 시간 종속성).
JVM은 호출중인 라이브러리에서 클래스를로드하기 위해 올바른 클래스 경로가 필요합니다 (런타임 종속성).
다음과 같은 두 가지 측면에서 다를 수 있습니다.
1) 클래스 C1이 라이브러리 클래스 L1을 호출하고 L1이 라이브러리 클래스 L2를 호출하는 경우 C1은 L1 및 L2에 대한 런타임 종속성이 있지만 L1에 대한 컴파일 시간 종속성 만 있습니다.
2) 클래스 C1이 Class.forName () 또는 다른 메커니즘을 사용하여 인터페이스 I1을 동적으로 인스턴스화하고 인터페이스 I1에 대한 구현 클래스가 클래스 L1 인 경우 C1은 I1 및 L1에 대한 런타임 종속성이 있지만 컴파일 시간 종속성 만 있습니다. I1에서.
컴파일 타임과 런타임에 대해 동일한 기타 "간접"종속성 :
3) 클래스 C1은 라이브러리 클래스 L1을 확장하고 L1은 인터페이스 I1을 구현하고 라이브러리 클래스 L2를 확장합니다. C1은 L1, L2 및 I1에 대한 컴파일 시간 종속성이 있습니다.
4) 클래스 C1에는 I1이 인터페이스이고 L1이 인터페이스 I1 인 매개 변수를 취하는 클래스 인 메소드 foo(I1 i1)
와 메소드 bar(L1 l1)
가 있습니다. C1은 I1 및 L1에 대한 컴파일 시간 종속성을 갖습니다.
기본적으로 흥미로운 작업을 수행하려면 클래스가 클래스 경로의 다른 클래스 및 인터페이스와 인터페이스해야합니다. 해당 라이브러리 인터페이스 집합에 의해 형성된 클래스 / 인터페이스 그래프 는 컴파일 시간 종속성 체인을 생성합니다. 라이브러리 구현 은 런타임 종속성 체인을 생성합니다. 런타임 종속성 체인은 런타임 종속적이거나 실패 속도가 느립니다. L1 구현이 때때로 클래스 L2의 개체를 인스턴스화하는 데 의존하고 해당 클래스가 하나의 특정 시나리오에서만 인스턴스화되는 경우 다음을 제외하고 종속성이 없습니다. 그 시나리오.
Java는 실제로 컴파일 타임에 어떤 것도 링크하지 않습니다. CLASSPATH에서 찾은 일치하는 클래스를 사용하여 구문 만 확인합니다. 모든 것이 합쳐지고 그 당시 CLASSPATH를 기반으로 실행되는 것은 런타임까지입니다.
컴파일 시간 종속성은 컴파일하는 클래스에서 직접 사용하는 종속성 (다른 클래스) 일뿐 입니다. 런타임 종속성은 실행중인 클래스의 직접 및 간접 종속성을 모두 포함합니다. 따라서 런타임 종속성에는 종속성의 종속성과 .NET에서 사용되는 클래스 이름과 같은 리플렉션 종속성 String
이 포함됩니다 Class#forName()
.
A
, B.jar과 함께 B extends A
그리고 C.jar와 함께 C extends B
있다면 C.jar는 A에 대한 C 종속성이 간접적이지만 A.jar의 컴파일 시간에 의존한다고 생각합니다.
Java의 경우 컴파일 시간 종속성은 소스 코드의 종속성입니다. 예를 들어, 클래스 A가 클래스 B의 메서드를 호출하면 A가 컴파일 할 B (B 유형)에 대해 알아야하므로 컴파일 시간에 A가 B에 종속됩니다. 여기서 트릭은 다음과 같아야합니다. 컴파일 된 코드는 아직 완전하고 실행 가능한 코드가 아닙니다. 아직 컴파일되지 않았거나 외부 jar에 존재하지 않는 소스에 대한 교체 가능한 주소 (기호, 메타 데이터)가 포함됩니다. 연결 중에 이러한 주소는 메모리의 실제 주소로 대체되어야합니다. 제대로하려면 올바른 기호 / 주소를 만들어야합니다. 그리고 이것은 클래스 (B)의 유형으로 할 수 있습니다. 컴파일 타임에 이것이 주요 의존성이라고 생각합니다.
런타임 종속성은 실제 제어 흐름과 더 관련이 있습니다. 실제 메모리 주소를 포함합니다. 프로그램이 실행 중일 때 갖는 종속성입니다. 여기에는 유형 정보뿐만 아니라 구현과 같은 클래스 B 세부 정보가 필요합니다. 클래스가 없으면 RuntimeException이 발생하고 JVM이 종료됩니다.
일반적으로 그리고해서는 안되는 두 종속성은 동일한 방향으로 흐릅니다. 이것은 OO 디자인의 문제입니다.
C ++에서 컴파일은 (Just-In-Time이 아니라) 약간 다르지만 링커도 있습니다. 따라서 프로세스는 내가 생각하는 Java와 유사하다고 생각할 수 있습니다.