Java 程序是否可以在编译期间依赖库,而不是在运行时依赖库?

Java 程序是否可以在编译期间依赖库,而不是在运行时依赖库?,java,build-process,runtime,compile-time,Java,Build Process,Runtime,Compile Time,我理解运行时和编译时之间的区别以及如何区分两者,但我不认为有必要区分编译时和运行时依赖关系 让我窒息的是:一个程序怎么能在运行时不依赖于它在编译过程中依赖的东西呢?如果我的Java应用程序使用log4j,那么它需要log4j.jar文件来编译(我的代码与log4j集成并从log4j内部调用成员方法)以及运行时(我的代码完全无法控制log4j.jar内部的代码运行后会发生什么) 我正在阅读诸如Ivy和Maven之类的依赖解决工具,这些工具清楚地区分了这两种类型的依赖。我只是不明白它的必要性 有谁能

我理解运行时和编译时之间的区别以及如何区分两者,但我不认为有必要区分编译时和运行时依赖关系

让我窒息的是:一个程序怎么能在运行时不依赖于它在编译过程中依赖的东西呢?如果我的Java应用程序使用log4j,那么它需要log4j.jar文件来编译(我的代码与log4j集成并从log4j内部调用成员方法)以及运行时(我的代码完全无法控制log4j.jar内部的代码运行后会发生什么)

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


有谁能给出一个简单的“King’s English”类型的解释,最好是用一个像我这样糟糕的sap都能理解的实际例子?

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

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

可能会有一些附带的情况,项目需要某种依赖关系才能编译,但实际上并不需要相应的代码,但这种情况很少见

另一方面,包含编译时不需要的运行时依赖项是非常常见的。例如,如果您正在编写一个JavaEE6应用程序,您可以根据JavaEE6API进行编译,但在运行时,可以使用任何JavaEE容器;正是这个容器提供了实现


使用反射可以避免编译时依赖关系。例如,JDBC驱动程序可以加载一个,而实际加载的类可以通过一个配置文件进行配置。

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


许多图书馆需要其他图书馆。这些库在编译时不需要,但在运行时需要。i、 当代码实际运行时。

通常您是对的,如果运行时依赖项和编译时依赖项相同,则可能是理想情况

当这个规则不正确时,我将给你两个例子

如果类A依赖于类B,而类B依赖于类C,而类C依赖于类D,其中A是您的类,B、C和D是来自不同第三方库的类,则编译时只需要B和C,运行时也需要D。 程序通常使用动态类加载。在这种情况下,不需要由编译时使用的库动态加载类。此外,库通常选择在运行时使用哪个实现。例如,SLF4J或Commons日志记录可以在运行时更改目标日志实现。编译时只需要SSL4J本身

相反的例子是,编译时比运行时需要更多的依赖项。 假设您正在开发必须在不同环境或操作系统下工作的应用程序。编译时需要所有特定于平台的库,运行时只需要当前环境所需的库


我希望我的解释能有所帮助。

在编译时,您可以启用依赖项所期望的契约/api。 (例如:这里您刚刚与宽带互联网提供商签订了一份合同) 在运行时,实际上您正在使用依赖项。
(例如:这里您实际使用的是宽带互联网)

通常,静态依赖关系图是动态依赖关系图的一个子图,请参见例如


也就是说,存在一些例外,主要是添加编译器支持的依赖项,这些依赖项在运行时变得不可见。例如,通过生成代码或通过附加检查。

刚刚遇到一个问题,可以回答您的问题
ServletAPI.jar
是我的web项目中的一个暂时依赖项,在编译时和运行时都需要。但是,
ServletAPI.jar
也包含在我的Tomcat库中

这里的解决方案是使maven中的
servlet api.jar
仅在编译时可用,并且不打包在war文件中,这样它就不会与Tomcat库中包含的
servlet api.jar
冲突


我希望这能解释编译时和运行时依赖关系。

每个Maven依赖关系都有一个作用域,用于定义依赖关系所在的类路径

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

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

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

运行时范围表示您的项目在单元测试执行和运行时执行类路径上具有依赖关系, 但与编译范围不同,它不可用
<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>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>...</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>...</version>
</dependency>