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


110

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

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

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

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


2
리플렉션을 사용하고 컴파일 타임에 사용할 수 없었던 클래스를 사용할 수 있습니다. "플러그인"을 생각하십시오.
Per Alexandersson

답변:


64

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

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

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

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

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


17
Java EE API에 대해- "제공된"종속성 범위의 목적이 아닙니까?
Kevin

15
컴파일에는 종속성이 필요하지만 런타임에는 필요하지 않은 예가 lombok (www.projectlombok.org)입니다. jar는 컴파일 타임에 자바 코드를 변환하는 데 사용되지만 런타임에는 전혀 필요하지 않습니다. "provided"범위를 지정하면 jar가 war / jar에 포함되지 않습니다.
Kevin

2
@Kevin 예, 좋은 점입니다. provided스코프는 런타임에 다른 수단 (예 : 컨테이너의 공유 라이브러리)에 의해 종속성이 제공 될 것이라는 기대에 런타임 종속성을 추가하지 않고 컴파일 시간 종속성을 추가합니다. runtime반면에 컴파일 타임 종속성을 만들지 않고 런타임 종속성을 추가합니다.
Artefacto

그렇다면 일반적으로 "모듈 구성"(Ivy 용어 사용)과 프로젝트 루트 아래의 주요 디렉토리 사이에 1 : 1 상관 관계 가 있다고 말하는 것이 안전 합니까? 예를 들어, JUnit JAR에 의존하는 모든 JUnit 테스트는 테스트 / 루트 아래에있을 것입니다. 동일한 소스 루트에 패키징 된 동일한 클래스가 서로 다른 주어진 시간에 JAR. log4j가 필요하면 log4j가 필요합니다. 동일한 코드에 1 구성에서 log4j 호출을 호출하도록 지시 할 수있는 방법이 없지만 일부 "비 로깅"구성에서는 log4j 호출을 무시할 수 있습니다. 맞습니까?
IAmYourFaja

30

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

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

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

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

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

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


@Koray Tugay 답변이 더 정확합니다. :) 런타임 범위가있는 의존성 항아리가 있다고 빠른 질문이 있습니다. Maven이 컴파일 타임에 항아리를 찾고 있습니까?
gks

@gks 아니요, 컴파일 시간에 필요하지 않습니다.
Koray Tugay

9

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

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


컴파일에는 필요하지 않지만 런타임에는 필요한 라이브러리의 예를 제공 할 수 있습니까?
Cristiano

1
@Cristiano 모든 JDBC 라이브러리는 다음과 같습니다. 또한 표준 API를 구현하는 라이브러리.
Peter Lawrey 2014

4

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

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

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

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

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


예제에서 컴파일 타임에 C가 필요한 이유에 대해 설명해 주시겠습니까? 컴파일 타임에 C가 필요한지 여부는 A가 참조하는 메서드와 필드 (B에서)에 따라 달라진다 는 인상을 받았습니다 ( stackoverflow.com/a/7257518/6095334 ).
Hervian


2

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

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

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


3
귀하의 예제는 실제로 주어진 질문에 대해 올바르지 않습니다 . 과 사이가 아닌 compileprovided범위 의 차이점을 설명하기 때문 입니다. 둘 다 컴파일 타임에 필요하며 앱에 패키징됩니다. 컴파일 타임에만 필요하지만 다른 의미로 제공되므로 앱에 패키징되지 않습니다 (예 : 이미 Tomcat 서버에 있음). compileruntimeCompile scopeProvided scope
MJar

1
글쎄, 나는 이것이 좋은 예 라고 생각한다. 왜냐하면 질문은 about and maven 범위가 아니라 컴파일 시간과 런타임 의존성 에 관한 것이기 때문이다 . 범위 핸들 컴파일 시간 종속성 런타임 패키지에 포함되지 않아야하는 경우 받는다는 방법이다. compileruntime provided
Christian Gawron

1

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

일반적인 컴파일 시간 및 런타임 개념과 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와 함께 작동합니다.


0

컴파일 타임에 종속성에서 예상되는 계약 / api를 활성화합니다. (예 : 여기에서는 광대역 인터넷 제공 업체와 계약을 체결합니다.) 런타임에 실제로 종속성을 사용하고 있습니다. (예 : 여기에서는 실제로 광대역 인터넷을 사용하고 있습니다.)


0

"어떻게 프로그램이 컴파일 중에 의존했던 어떤 것에 의존하지 않을 수 있습니까?"라는 질문에 답하기 위해 주석 프로세서의 예를 살펴 보겠습니다.

자체 주석 프로세서를 작성했으며 .NET Framework com.google.auto.service:auto-service를 사용할 수 있도록 컴파일 시간 종속성이 있다고 가정합니다 @AutoService. 이 종속성은 주석 프로세서를 컴파일하는 데만 필요하지만 런타임 에는 필요 하지 않습니다. 주석 처리를 위해 주석 프로세서에 의존하는 다른 모든 프로젝트 com.google.auto.service:auto-service는 런타임에 종속성이 필요 하지 않습니다. .

이것은 흔하지는 않지만 발생합니다.


0

runtime범위는 코드에서 구현 라이브러리에 직접 종속성을 추가하는 대신 추상화 또는 외관을 사용하는 프로그래머를 방지 할 수있다.

즉, 인터페이스를 사용하도록 강제합니다.

구체적인 예 :

1) 팀이 Log4j를 통해 SLF4J를 사용하고 있습니다. 프로그래머가 Log4j API가 아닌 SLF4J API를 사용하기를 원합니다. Log4j는 SLF4J에서 내부적으로 만 사용됩니다. 해결책:

  • SLF4J를 정규 컴파일 시간 종속성으로 정의
  • log4j-core 및 log4j-api를 런타임 종속성으로 정의하십시오.

2) 애플리케이션이 JDBC를 사용하여 MySQL에 액세스하고 있습니다. 프로그래머가 MySQL 드라이버 구현에 대해 직접적으로가 아니라 표준 JDBC 추상화에 대해 코딩하기를 원합니다.

  • mysql-connector-java(MySQL JDBC 드라이버)를 런타임 종속성으로 정의하십시오 .

런타임 종속성은 컴파일 중에 숨겨 지지만 (코드에 "직접"종속성이있는 경우 컴파일 타임 오류 발생) 실행 시간 및 배포 가능한 아티팩트 (WAR 파일, SHADED jar 파일 등)를 만들 때 포함됩니다.

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