我理解运行时和编译时之间的区别以及如何区分这两者,但我没有看到需要区分编译时和运行时依赖。
我正在窒息的是:程序如何在编译期间不依赖运行时依赖的东西?如果我的Java应用程序使用log4j,那么它需要log4j.jar文件才能编译(我的代码与log4j内部的成员方法集成并调用成员方法)以及运行时(我的代码完全无法控制log4j中的代码会发生什么) .jar跑了)。
我正在阅读依赖解析工具,如Ivy和Maven,这些工具清楚地区分了这两种类型的依赖关系。我只是不明白它的必要性。
任何人都可以给出一个简单的,“国王的英语”式的解释,最好有一个实际的例子,即使像我这样的可怜的人也能理解?
运行时通常需要编译时依赖项。在maven中,compile
作用域依赖项将在运行时添加到类路径中(例如,在战争中它们将被复制到WEB-INF / lib)。
但是,并非严格要求;例如,我们可以针对某个API进行编译,使其成为编译时依赖项,但是在运行时包含一个也包含API的实现。
可能存在边缘情况,其中项目需要一定的依赖性来编译,但实际上并不需要相应的代码,但这些很少见。
另一方面,包括编译时不需要的运行时依赖性是非常常见的。例如,如果您正在编写Java EE 6应用程序,则可以针对Java EE 6 API进行编译,但在运行时,可以使用任何Java EE容器;这个容器提供了实现。
使用反射可以避免编译时依赖性。例如,可以使用Class.forName
加载JDBC驱动程序,并且可以通过配置文件配置加载的实际类。
每个Maven依赖项都有一个范围,用于定义依赖项可用的类路径。
为项目创建JAR时,依赖项不会与生成的工件捆绑在一起;它们仅用于编译。 (但是,您仍然可以在构建的jar中使maven包含依赖项,请参阅:Including dependencies in a jar with Maven)
使用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容器或应用程序服务器的类路径中,并且不会复制到已部署的应用程序中。这也意味着您需要此依赖项来编译项目。
您需要在运行时可能需要的编译时依赖项。但是,许多库运行时没有所有可能的依赖项。即一个可以使用四个不同XML库但只需要一个工作的库。
许多库依次需要其他库。编译时不需要这些库,但在运行时需要这些库。即当代码实际运行时。
通常,您是正确的,如果运行时和编译时依赖性相同,则它是理想的情况。
当这条规则不正确时,我会举两个例子。
如果A类依赖于依赖于类C的B类,它依赖于D类,其中A是您的类,B,C和D是来自不同第三方库的类,您在编译时只需要B和C,并且还需要D at运行。程序通常使用动态类加载。在这种情况下,您不需要在编译时使用的库动态加载的类。此外,库通常会选择在运行时使用哪个实现。例如,SLF4J或Commons Logging可以在运行时更改目标日志实现。编译时只需要SSL4J。
相反,在编译时需要比运行时更多的依赖项。认为您正在开发必须在不同环境或操作系统上工作的应用程序。在编译时需要所有特定于平台的库,并且在运行时只需要当前环境所需的库。
我希望我的解释有所帮助。
通常,静态依赖关系图是动态关系图的子图,参见例如。 this blog entry from the author of NDepend。
也就是说,有一些例外,主要是添加编译器支持的依赖项,它们在运行时变得不可见。例如,通过Lombok进行代码生成或通过(pluggable type-)Checker Framework进行额外检查。
刚遇到一个回答你问题的问题。 servlet-api.jar
是我的Web项目中的瞬态依赖项,在编译时和运行时都需要。但servlet-api.jar
也包含在我的Tomcat库中。
这里的解决方案是让maven中的servlet-api.jar
仅在编译时可用,而不是打包在我的war文件中,这样它就不会与我的Tomcat库中包含的servlet-api.jar
冲突。
我希望这解释了编译时间和运行时依赖性。
我理解运行时和编译时之间的区别以及如何区分这两者,但我没有看到需要区分编译时和运行时依赖。
一般的编译时和运行时概念以及Maven特定的compile
和runtime
范围依赖关系是两个非常不同的东西。你不能直接比较它们,因为它们没有相同的框架:一般的编译和运行时概念是广泛的,而maven compile
和runtime
范围概念是根据时间特定的依赖关系可用性/可见性:编译或执行。
不要忘记Maven首先是一个javac
/ java
包装器,并且在Java中你有一个编译时类路径,你用javac -cp ...
和你用java -cp ...
指定的运行时类路径指定。
将Maven compile
范围视为在Java编译和运行时类路径(javac
和java
)中添加依赖关系的方法并不错,而Maven runtime
范围可以被视为仅在Java中添加依赖关系的方法运行时classppath(javac
)。
我正在窒息的是:程序如何在编译期间不依赖运行时依赖的东西?
你所描述的与runtime
和compile
范围没有任何关系。
对于在编译时依赖于依赖项而不是在运行时依赖的provided
作用域,它看起来更像。
您可以使用它,因为您需要编译依赖项,但您不希望将它包含在打包的组件(JAR,WAR或任何其他组件)中,因为依赖项已由环境提供:它可以包含在服务器中或任何其中启动指定为Java应用程序的类路径的路径。
如果我的Java应用程序使用log4j,那么它需要log4j.jar文件才能编译(我的代码与log4j内部的成员方法集成并调用成员方法)以及运行时(我的代码完全无法控制log4j中的代码会发生什么) .jar跑了)。
在这种情况下是的。但是假设您需要编写一个可移植代码,该代码依赖于slf4j作为log4j前面的外观,以便以后能够切换到另一个日志记录实现(log4J 2,logback或任何其他)。
在这种情况下,您需要将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一起使用。
在编译时,您可以从依赖项中启用期望的contract / api。 (例如:在这里,您只需与宽带互联网提供商签订合同)在运行时实际上您正在使用依赖项。 (例如:你实际上在这里使用宽带互联网)
要回答“程序如何在运行时不依赖于编译期间依赖于某些东西?”的问题,让我们看一下注释处理器的示例。
假设您已编写自己的注释处理器,并假设它对com.google.auto.service:auto-service
具有编译时依赖性,以便它可以使用@AutoService
。这种依赖性只需要编译注释处理器,但在运行时不需要:所有其他项目(取决于注释处理器用于处理注释)不需要在运行时依赖于com.google.auto.service:auto-service
(也不需要在编译时或任何其他时间) )。
这不常见,但它发生了。