我了解运行时和编译时之间的区别以及如何
区分两者,但我只是不认为有必要
区分编译时依赖和运行时依赖。
一般的编译时和运行时概念和 Maven 特定的 compile 和 runtime 范围依赖是两个非常不同的东西。你不能直接比较它们,因为它们没有相同的框架:一般的编译和运行时概念很广泛,而 maven compile 和 runtime 范围概念是关于根据时间的依赖关系可用性/可见性:编译或执行。
不要忘记 Maven 首先是一个 javac/java 包装器,在 Java 中,您有一个用 javac -cp ... 指定的编译时类路径和一个用 java -cp ... 指定的运行时类路径。
将 Maven compile 范围视为在 Java 编译和运行时 classppath(javac 和 java)中添加依赖项的一种方式并没有错,而 Maven runtime 范围可以被视为一种方式仅在 Java 运行时 classppath (javac) 中添加依赖项。
让我哽咽的是:程序怎么能不依赖于某些东西
在编译期间它依赖的运行时?
您所描述的与runtime 和compile 范围没有任何关系。
您为依赖项指定的 provided 范围看起来更像是在编译时而不是在运行时依赖于它。
您在需要编译依赖项时使用它,但您不想将其包含在打包的组件(JAR、WAR 或任何其他组件)中,因为环境已经 提供 依赖项:它可以被包含在服务器或类路径的任何路径中指定为 Java 应用程序启动。
如果我的 Java 应用程序使用 log4j,那么它需要 log4j.jar 文件才能编译(我的代码
从 log4j 内部集成和调用成员方法)为
以及运行时(我的代码完全无法控制发生的事情
一旦运行 log4j.jar 中的代码)。
在这种情况下是的。但是假设您需要在 log4j 前面编写一个依赖 slf4j 作为外观的可移植代码,以便以后能够切换到另一个日志记录实现(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>
这样,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 一起工作。