我了解运行时和编译时之间的区别以及如何区分两者,但我只是不认为需要区分编译时和运行时依赖项。
一般的编译时和运行时概念以及 Maven 特定compile
和runtime
范围依赖是两个非常不同的东西。您不能直接比较它们,因为它们没有相同的框架:一般的编译和运行时概念是广泛的,而 mavencompile
和runtime
作用域的概念具体是关于根据时间的依赖关系可用性/可见性:编译或执行。
不要忘记 Maven 首先是一个javac
/java
包装器,而在 Java 中,您有一个用 指定的编译时类路径javac -cp ...
和一个用 指定的运行时类路径java -cp ...
。
将 Mavencompile
范围视为在 Java 编译和运行时 classppath 中添加依赖项的一种方式并没有错(javac
和java
),而 Mavenruntime
作用域可以看作是一种仅在 Java 运行时 classppath ( javac
) 中添加依赖项的方法。
我哽咽的是:程序如何在运行时不依赖它在编译期间依赖的东西?
runtime
您所描述的与范围没有任何关系compile
。对于您为依赖项指定
的范围而言,它看起来更像是在编译时而不是在运行时依赖于该范围。
您使用它是因为您需要编译依赖项,但您不想将其包含在打包的组件(JAR、WAR 或任何其他组件)中,因为该依赖项已经由环境提供:它可以包含在服务器或任何启动指定为 Java 应用程序的类路径的路径。 provided
如果我的 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 一起工作。