[java] 프로그램이 런타임이 아닌 컴파일 중에 라이브러리에 의존 할 수 있습니까?

나는 런타임과 컴파일 시간의 차이와 둘을 구별하는 방법을 이해하지만 컴파일 시간과 런타임 종속성을 구분할 필요성을 느끼지 못합니다 .

내가 질식하고있는 것은 이것이다 : 어떻게 프로그램이 컴파일 동안 의존했던 런타임에 어떤 것에 의존하지 않을 수 있는가? 내 Java 앱이 log4j를 사용하는 경우 컴파일 (log4j 내부에서 멤버 메서드와 통합 및 호출하는 내 코드) 및 런타임 (내 코드는 log4j 내부의 코드가 한 번 발생하는 일을 제어 할 수 없음)을 위해 log4j.jar 파일이 필요합니다. .jar가 실행 됨).

Ivy 및 Maven과 같은 종속성 해결 도구를 읽고 있으며 이러한 도구는 이러한 두 가지 유형의 종속성을 명확하게 구분합니다. 나는 그것의 필요성을 이해하지 못한다.

누구든지 간단한 “King ‘s English”형식의 설명을 해주실 수 있습니까? 가급적 저처럼 가난한 수액도 이해할 수있는 실제 예제를 사용하여 설명 할 수 있습니까?



답변

컴파일 타임 종속성은 일반적으로 런타임에 필요합니다. Maven에서는 compile범위 가 지정된 종속성이 런타임의 클래스 경로에 추가됩니다 (예 : wars에서는 WEB-INF / lib에 복사 됨).

그러나 반드시 필요한 것은 아닙니다. 예를 들어, 우리는 특정 API에 대해 컴파일하여 컴파일 타임 종속성을 만들 수 있지만 런타임에는 API도 포함하는 구현을 포함합니다.

프로젝트를 컴파일하는 데 특정 종속성이 필요하지만 해당 코드가 실제로 필요하지 않은 경우가있을 수 있지만 드물게 발생합니다.

반면에 컴파일 타임에 필요하지 않은 런타임 종속성을 포함하는 것은 매우 일반적입니다. 예를 들어, Java EE 6 애플리케이션을 작성하는 경우 Java EE 6 API에 대해 컴파일하지만 런타임에는 모든 Java EE 컨테이너를 사용할 수 있습니다. 구현을 제공하는 것은이 컨테이너입니다.

리플렉션을 사용하여 컴파일 시간 종속성을 피할 수 있습니다. 예를 들어 JDBC 드라이버는로로드 할 수 있으며로드 된 Class.forName실제 클래스는 구성 파일을 통해 구성 할 수 있습니다.


답변

각 Maven 종속성에는 종속성을 사용할 수있는 클래스 경로를 정의하는 범위가 있습니다.

프로젝트에 대한 JAR을 작성할 때 종속성은 생성 된 아티팩트와 함께 번들로 제공되지 않습니다. 컴파일에만 사용됩니다. (그러나 빌드 된 jar에 종속성을 포함하도록 maven을 만들 수 있습니다. Maven을 사용하여 jar에 종속성 포함을 참조하십시오. )

Maven을 사용하여 WAR 또는 EAR 파일을 생성 할 때 생성 된 아티팩트와 종속성을 번들링하도록 Maven을 구성 할 수 있으며 제공된 범위를 사용하여 WAR 파일에서 특정 종속성을 제외하도록 구성 할 수도 있습니다.

가장 일반적인 범위 인 Compile Scope 는 컴파일 클래스 경로, 단위 테스트 컴파일 및 실행 클래스 경로, 애플리케이션을 실행할 때 최종 런타임 클래스 경로에서 프로젝트에 종속성을 사용할 수 있음을 나타냅니다. Java EE 웹 애플리케이션에서 이는 종속성이 배포 된 애플리케이션에 복사됨을 의미합니다. 그러나 .jar 파일에서는 종속성이 컴파일 범위에 포함되지 않습니다.

런타임 범위 는 단위 테스트 실행 및 런타임 실행 클래스 경로에서 프로젝트에 종속성을 사용할 수 있음을 나타내지 만 컴파일 범위와 달리 애플리케이션 또는 단위 테스트 를 컴파일 할 때 사용할 수 없습니다 . 런타임 종속성은 배포 된 응용 프로그램에 복사되지만 컴파일 중에는 사용할 수 없습니다! 특정 라이브러리에 실수로 의존하지 않도록하는 데 유용합니다.

마지막으로 제공된 범위 는 애플리케이션이 실행되는 컨테이너가 사용자를 대신하여 종속성을 제공함을 나타냅니다. Java EE 애플리케이션에서 이는 종속성이 이미 서블릿 컨테이너 또는 애플리케이션 서버의 클래스 경로에 있으며 배포 된 애플리케이션에 복사되지 않음을 의미합니다. 또한 프로젝트를 컴파일 할 때이 종속성이 필요함을 의미합니다.


답변

런타임에 필요할 수있는 컴파일 타임 종속성이 필요합니다. 그러나 많은 라이브러리가 가능한 모든 종속성없이 실행됩니다. 즉, 4 개의 다른 XML 라이브러리를 사용할 수 있지만 작동하려면 하나만 필요한 라이브러리입니다.

많은 도서관은 차례로 다른 도서관을 필요로합니다. 이러한 라이브러리는 컴파일 타임에는 필요하지 않지만 런타임에는 필요합니다. 즉, 코드가 실제로 실행될 때.


답변

일반적으로 당신이 옳고 아마도 런타임과 컴파일 시간 종속성이 동일한 경우 이상적인 상황입니다.

이 규칙이 틀렸을 때 두 가지 예를 들겠습니다.

클래스 A가 클래스 D에 의존하는 클래스 B에 의존하는 경우 A가 클래스이고 B이고, C와 D가 다른 타사 라이브러리의 클래스 인 경우 컴파일 타임에 B와 C 만 필요하고 D도 필요합니다. 실행 시간. 종종 프로그램은 동적 클래스 로딩을 사용합니다. 이 경우 컴파일 타임에 사용중인 라이브러리에 의해 동적으로로드 된 클래스가 필요하지 않습니다. 또한 라이브러리는 런타임에 사용할 구현을 선택하는 경우가 많습니다. 예를 들어 SLF4J 또는 Commons Logging은 런타임에 대상 로그 구현을 변경할 수 있습니다. 컴파일 타임에는 SSL4J 자체 만 필요합니다.

런타임보다 컴파일 시간에 더 많은 종속성이 필요한 경우 반대의 예입니다. 다른 환경이나 운영 체제에서 작동해야하는 애플리케이션을 개발하고 있다고 생각하십시오. 컴파일 타임에는 모든 플랫폼 별 라이브러리가 필요하고 런타임에는 현재 환경에 필요한 라이브러리 만 필요합니다.

내 설명이 도움이되기를 바랍니다.


답변

일반적으로 정적 종속성 그래프는 동적 그래프의 하위 그래프입니다 (예 : NDepend 작성자의 블로그 항목 참조) .

즉, 런타임에 표시되지 않는 컴파일러 지원을 추가하는 종속성, 주로 종속성이 있습니다. 예를 들어 Lombok 을 통한 코드 생성 또는 (pluggable type-) Checker Framework 를 통한 추가 검사의 경우 입니다.


답변

귀하의 질문에 답변하는 문제가 발생했습니다. servlet-api.jar내 웹 프로젝트의 일시적인 종속성이며 컴파일 타임과 런타임 모두에 필요합니다. 하지만 servlet-api.jar내 Tomcat 라이브러리에도 포함되어 있습니다.

여기서 해결책은 servlet-api.jar컴파일 타임에만 maven을 사용하고 내 전쟁 파일에 패키징되지 않도록 만드는 것이므로 servlet-api.jarTomcat 라이브러리에 포함 된 것과 충돌하지 않습니다 .

이것이 컴파일 시간과 런타임 의존성을 설명하기를 바랍니다.


답변

나는 런타임과 컴파일 시간의 차이와 둘을 구별하는 방법을 이해하지만 컴파일 시간과 런타임 종속성을 구분할 필요가 없다고 생각합니다.

일반적인 컴파일 시간 및 런타임 개념과 Maven 특정 compileruntime범위 종속성은 매우 다른 두 가지입니다. 프레임이 같지 않기 때문에 직접 비교할 수 없습니다. 일반적인 컴파일 및 런타임 개념은 광범위하지만 maven compileruntime범위 개념은 컴파일 또는 실행 시간에 따른 종속성 가용성 / 가시성에 관한 것입니다.
Maven은 무엇보다도 javac/ java래퍼이며 Java에서는 지정하는 컴파일 시간 클래스 경로와 지정 javac -cp ... 하는 런타임 클래스 경로가 있음을 잊지 마십시오 java -cp ....
Maven compile범위를 Java 컴파일 및 런타임 클래스 경로에 종속성을 추가하는 방법으로 고려하는 것은 잘못이 아닙니다 (javacjava) Maven runtime범위는 Java 런타임 클래스 경로 ( javac) 에만 종속성을 추가하는 방법으로 볼 수 있습니다 .

내가 질식하고있는 것은 이것이다 : 어떻게 프로그램이 컴파일 동안 의존했던 런타임에 어떤 것에 의존하지 않을 수 있는가?

설명하는 내용은 범위 runtimecompile범위 와 관련이 없습니다 . 종속성이 런타임이 아닌 컴파일 타임에 종속되도록 지정
하는 provided범위에 더 가깝습니다.
컴파일하는 데 종속성이 필요하기 때문에 사용하지만 패키지 구성 요소 (JAR, WAR 또는 기타)에는 종속성이 이미 환경 의해 제공 되기 때문에 포함하지 않으려 고 합니다. 서버 또는 모든 서버에 포함될 수 있습니다. Java 애플리케이션으로 지정된 클래스 경로의 경로가 시작됩니다.

내 Java 앱이 log4j를 사용하는 경우 컴파일 (log4j 내부에서 멤버 메서드와 통합 및 호출하는 내 코드) 및 런타임 (내 코드는 log4j 내부의 코드가 한 번 발생하는 일을 제어 할 수 없음)을 위해 log4j.jar 파일이 필요합니다. .jar가 실행 됨).

이 경우에는 그렇습니다. 그러나 나중에 다른 로깅 구현 (log4J 2, logback 또는 기타)으로 전환 할 수 있도록 slf4j에 의존하는 이식 가능한 코드를 log4j 앞에 파사드로 작성해야한다고 가정합니다.
이 경우 pom에서 slf4j를 compile종속성 (기본값)으로 지정해야하지만 log4j 종속성을 종속성으로 지정합니다 runtime.

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>...</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>...</version>
    <scope>runtime</scope>
</dependency>

이런 식으로 컴파일 된 코드에서 log4j 클래스를 참조 할 수 없지만 slf4j 클래스는 계속 참조 할 수 있습니다. 시간
과 함께 두 개의 종속성을 지정하면 compile컴파일 된 코드에서 log4j 클래스를 참조하는 것을 막을 수 없으며 로깅 구현과 원하지 않는 결합을 만들 수 있습니다.

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>...</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>...</version>
</dependency>

runtime범위 의 일반적인 사용은 JDBC 종속성 선언입니다. 이식 가능한 코드를 작성하려면 클라이언트 코드가 특정 DBMS 종속성 (예 : PostgreSQL JDBC 종속성)의 클래스를 참조하는 것을 원하지 않지만 런타임에 클래스를 만드는 데 필요한 것처럼 애플리케이션에 모두 포함하기를 원합니다. JDBC API는이 DBMS와 함께 작동합니다.