程序可以在编译期间依赖于库而不是运行时吗?

IAm*_*aja 106 java build-process runtime compile-time

我理解运行时和编译时之间的区别以及如何区分这两者,但我没有看到需要区分编译时和运行时依赖.

我正在窒息的是:程序如何在编译期间不依赖运行时依赖的东西?如果我的Java应用程序使用log4j,那么它需要log4j.jar文件才能编译(我的代码集成并调用log4j内部的成员方法)以及运行时(我的代码完全无法控制log4j中的代码后发生的事情) .jar跑了).

我正在阅读依赖解析工具,如Ivy和Maven,这些工具清楚地区分了这两种类型的依赖关系.我只是不明白它的必要性.

任何人都可以给出一个简单的,"国王的英语"式的解释,最好有一个实际的例子,即使像我这样的可怜的人也能理解?

Art*_*cto 61

运行时通常需要编译时依赖项.在maven中,一个compile作用域依赖项将在运行时添加到类路径中(例如,在战争中它们将被复制到WEB-INF/lib).

但是,并非严格要求; 例如,我们可以针对某个API进行编译,使其成为编译时依赖项,但是在运行时包含一个也包含API的实现.

可能存在边缘情况,其中项目需要一定的依赖性来编译,但实际上并不需要相应的代码,但这些很少见.

另一方面,包括编译时不需要的运行时依赖性是非常常见的.例如,如果您正在编写Java EE 6应用程序,则可以针对Java EE 6 API进行编译,但在运行时,可以使用任何Java EE容器; 这个容器提供了实现.

使用反射可以避免编译时依赖性.例如,可以使用a加载JDBC驱动程序,Class.forName并且可以通过配置文件配置加载的实际类.

  • 关于Java EE API - 不是"提供的"依赖范围是什么? (17认同)
  • 在运行时需要编译但不需要依赖的示例是lombok(www.projectlombok.org).jar用于在编译时转换java代码,但在运行时根本不需要.指定范围"提供"会导致jar不包含在war/jar中. (13认同)
  • @Kevin是的,好的一点,`provided`作用域添加了一个编译时依赖项,而没有添加运行时依赖性,期望依赖项将在运行时通过其他方式(例如容器中的共享库)提供.另一方面,`runtime`添加了运行时依赖性,而没有使它成为编译时依赖项. (2认同)

Kor*_*gay 27

每个Maven依赖项都有一个范围,用于定义依赖项可用的类路径.

为项目创建JAR时,依赖项不会与生成的工件捆绑在一起; 它们仅用于编译.(但是,您仍然可以使maven包含构建jar中的依赖项,请参阅:使用Maven在jar中包含依赖项)

使用Maven创建WAR或EAR文件时,可以将Maven配置为将依赖项与生成的工件捆绑在一起,还可以将其配置为使用提供的范围从WAR文件中排除某些依赖项.

最常见的范围 - 编译范围 - 表示您的项目在编译类路径,单元测试编译和执行类路径以及执行应用程序时的最终运行时类路径时可用.在Java EE Web应用程序中,这意味着将依赖关系复制到已部署的应用程序中.但是在.jar文件中,编译范围不包含依赖项.

运行时作用域指示项目可以在单元测试执行和运行时执行类路径上使用依赖项,但与编译范围不同,它在编译应用程序或其单元测试时不可用.运行时依赖项将复制到已部署的应用程序中,但在编译期间不可用!这有助于确保您不会错误地依赖于特定的库.(参见例如:http://www.tugay.biz/2016/12/apache-commons-logging-log4j-maven.html)

最后,提供的Scope表示应用程序执行的容器代表您提供依赖.在Java EE应用程序中,这意味着依赖关系已经存在于Servlet容器或应用程序服务器的类路径中,并且不会复制到已部署的应用程序中.这也意味着您需要此依赖项来编译项目.


Pet*_*rey 9

您需要在运行时可能需要的编译时依赖项.但是,许多库运行时没有所有可能的依赖项.即一个可以使用四个不同XML库但只需要一个工作的库.

许多库依次需要其他库.编译时不需要这些库,但在运行时需要这些库.即代码实际运行时.


dav*_*xxx 7

我了解运行时和编译时之间的区别以及如何区分两者,但我只是不认为有必要区分编译时和运行时依赖项。

一般的编译时和运行时概念以及 Maven 特定compileruntime范围依赖关系是两个截然不同的东西。您无法直接比较它们,因为它们不具有相同的框架:一般编译和运行时概念很广泛,而 Mavencompileruntime作用域概念则具体涉及根据时间的依赖项可用性/可见性:编译或执行。
不要忘记,Maven 首先是一个javac/java包装器,而在 Java 中,您有一个用 指定的编译时类路径javac -cp ... 和一个用 指定的运行时类路径java -cp ...。将 Maven作用域视为在 Java 编译和运行时 classppath (和) 中添加依赖项的一种方式
并没有错,而 Maven作用域可以被视为仅在 Java 运行时 classppath ( )中添加依赖项的一种方式。 compilejavacjavaruntimejavac

我感到窒息的是:程序如何在运行时不依赖于它在编译期间所依赖的东西?

runtime你所描述的与范围没有任何关系compile
它看起来更像是provided您为依赖项指定的范围,以在编译时而不是在运行时依赖。
您使用它是因为您需要编译依赖项,但您不想将其包含在打包组件(JAR、WAR 或任何其他组件)中,因为依赖项已由环境提供:它可以包含在服务器或任何其他组件中Java 应用程序启动时指定的类路径的路径。

如果我的 Java 应用程序使用 log4j,那么它需要 log4j.jar 文件才能编译(我的代码与 log4j 内部集成并调用成员方法)以及运行时(我的代码完全无法控制 log4j 内的代码发生的情况) .jar 已运行)。

在这种情况下是的。但是假设您需要编写一个依赖 slf4j 作为 log4j 前面的可移植代码,以便稍后能够切换到另一个日志记录实现(log4J 2、logback 或任何其他)。
在这种情况下,在 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>
Run Code Online (Sandbox Code Playgroud)

这样,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>
Run Code Online (Sandbox Code Playgroud)

范围的常见用法runtime是 JDBC 依赖项声明。要编写可移植代码,您不希望客户端代码引用特定 DBMS 依赖项的类(例如:PostgreSQL JDBC 依赖项),但您希望所有这些都将其包含在您的应用程序中,因为在运行时需要这些类JDBC API 与该 DBMS 配合使用。


Agu*_*hez 5

runtime范围是为了防止程序员在代码中添加对实现库的直接依赖项,而不是使用抽象或 fa\xc3\xa7ades。

\n\n

换句话说,它强制使用接口。

\n\n

具体例子:

\n\n

1) 您的团队正在使用 SLF4J 而不是 Log4j。您希望您的程序员使用 SLF4J API,而不是 Log4j API。Log4j 仅由 SLF4J 在内部使用。\n解决方案:

\n\n
    \n
  • 将 SLF4J 定义为常规编译时依赖项
  • \n
  • 将 log4j-core 和 log4j-api 定义为运行时依赖项。
  • \n
\n\n

2) 您的应用程序正在使用 JDBC 访问 MySQL。您希望程序员针对标准 JDBC 抽象进行编码,而不是直接针对 MySQL 驱动程序实现。

\n\n
    \n
  • mysql-connector-java(MySQL JDBC 驱动程序)定义为运行时依赖项。
  • \n
\n\n

运行时依赖项在编译期间隐藏(如果您的代码“直接”依赖于它们,则会引发编译时错误),但在执行时和创建可部署工件(WAR 文件、SHADED jar 文件等)期间包含运行时依赖项。

\n