125

I understand the difference between runtime and compile-time and how to differentiate between the two, but I just don't see the need to make a distinction between compile-time and runtime dependencies.

What I'm choking on is this: how can a program not depend on something at runtime that it depended on during compilation? If my Java app uses log4j, then it needs the log4j.jar file in order to compile (my code integrating with and invoking member methods from inside log4j) as well as runtime (my code has absolutely no control over what happens once code inside log4j.jar is ran).

I'm reading up on dependency resolution tools such as Ivy and Maven, and these tools clearly make the distinction between these two types of dependencies. I just don't understand the need for it.

Can anyone give a simple, "King's English"-type explanation, preferably with an actual example that even a poor sap like me could understand?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
IAmYourFaja
  • 55,468
  • 181
  • 466
  • 756

10 Answers10

73

A compile-time dependency is generally required at runtime. In maven, a compile scoped dependency will be added to the classpath on runtime (e.g. in wars they will be copied to WEB-INF/lib).

It is not, however, strictly required; for instance, we may compile against a certain API, making it a compile-time dependency, but then at runtime include an implementation that also includes the API.

There may be fringe cases where the project requires a certain dependency to compile but then the corresponding code is not actually needed, but these will be rare.

On the other hand, including runtime dependencies that are not needed at compile-time is very common. For instance, if you're writing a Java EE 6 application, you compile against the Java EE 6 API, but at runtime, any Java EE container can be used; it's this container that provides the implementation.

Compile-time dependencies can be avoided by using reflection. For instance, a JDBC driver can be loaded with a Class.forName and the actual class loaded be configurable through a configuration file.

Emre Yazici
  • 10,136
  • 6
  • 48
  • 55
Artefacto
  • 96,375
  • 17
  • 202
  • 225
  • 20
    About the Java EE API--isn't that what the "provided" dependency scope is for? – Kevin Aug 15 '11 at 20:51
  • 21
    an example where a dependency is needed to compile but not needed at runtime is lombok (www.projectlombok.org). The jar is used to transform java code at compile time but is not needed at all at runtime. Specifying scope "provided" causes the jar to not be included in the war/jar. – Kevin Aug 15 '11 at 20:53
  • 2
    @Kevin Yes, good point, the `provided` scope adds a compile time dependency without adding a runtime dependency on the expectation that the dependency will be provided at runtime by other means (e.g. a shared library in the container). `runtime` on the other hand adds a runtime dependency without making it a compile-time dependency. – Artefacto Aug 15 '11 at 20:56
  • So is it safe to say that there is *usually* a 1:1 correlation between a "module configuration" (using Ivy terms) and major directory under your project root? For instance, all my JUnit tests that depend on the JUnit JAR will be under the test/ root, etc. I just don't see how the same classes, packaged under the same source root, could be "configured" to depend on different JARs at any given time. If you need log4j, then you need log4j; there's no way to tell the same code to invoke log4j calls under 1 config, but to ignore log4j calls under some "non-logging" config, right? – IAmYourFaja Aug 15 '11 at 20:56
39

Each Maven dependency has a scope that defines which classpath that dependency is available on.

When you create a JAR for a project, dependencies are not bundled with the generated artifact; they are used only for compilation. (However, you can still make maven include the dependencies in the built jar, see: Including dependencies in a jar with Maven)

When you use Maven to create a WAR or an EAR file, you can configure Maven to bundle dependencies with the generated artifact, and you can also configure it to exclude certain dependencies from the WAR file using the provided scope.

The most common scope — compile — indicates that the dependency is available to your project on the compile classpath, the unit test compile and execution classpaths, and the eventual runtime classpath when you execute your application. In a Java EE web application, this means the dependency is copied into your deployed application. In a JAR file however, dependencies will not be included when the compile scope is used.

runtime scope indicates that the dependency is available to your project on the unit test execution and runtime execution classpaths, but unlike the compile scope it is not available when you compile your application or its unit tests. A Runtime Dependency is copied into your deployed application, but it is not available during compilation. This is good for making sure you do not mistakenly depend on a specific library. Imagine you have a specific logging implementation being used, but you only want to import a logging facade in your source code. You would include the concrete log library with a runtime scope, so you do not mistakenly rely on it.

Finally, provided scope indicates that the container in which your application executes provides the dependency on your behalf. In a Java EE application, this means the dependency is already on the Servlet container’s or application server’s classpath and is not copied into your deployed application. It also means that you need this dependency for compiling your project.

Koray Tugay
  • 22,894
  • 45
  • 188
  • 319
  • @Koray Tugay Answer is more precise :) i have quick question say i have a dependeny jar w/ the scope of run time . Does the maven will be looking for the jar at compile time ? – thar45 Oct 11 '18 at 10:46
  • @gks No, it won 't require it in compile time. – Koray Tugay Sep 23 '19 at 23:51
9

You need at compile time dependencies which you might need at runtime. However many libraries run without all its possible dependencies. i.e. libraries which can use four different XML libraries, but only needs one to work.

Many libraries, need other libraries in turn. These libraries are not needed at compile time but are needed at runtime. i.e. when the code is actually run.

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
6

I understand the difference between runtime and compile-time and how to differentiate between the two, but I just don't see the need to make a distinction between compile-time and runtime dependencies.

The general compile-time and runtime concepts and the Maven specific compile and runtime scope dependencies are two very different things. You cannot directly compare them as these don't have the same frame : the general compile and runtime concepts are broad while the maven compile and runtime scope concepts is about specifically the dependencies availability/visibility according to the time : compilation or execution.
Don't forget that Maven is above all a javac/java wrapper and that in Java you have a compile time classpath that you specify with javac -cp ... and a runtime classpath that you specify with java -cp ... .
It would be not wrong to consider the Maven compile scope as a way to add a dependency both in the Java compile and runtime classppath (javac and java) while the Maven runtime scope can be seen as a way to add a dependency only in the Java runtime classppath (javac).

What I'm choking on is this: how can a program not depend on something at runtime that it depended on during compilation?

What you describe doesn't have any relationship with runtime and compile scope.
It looks like more to the provided scope that you specify for a dependency to depend to that at compile time but not at runtime.
You use it as you need the dependency to compile but you don't want to include it in the packaged component (JAR, WAR or any others) because the dependency is already provided by the environment : it can be included in the server or any path of the classpath specified as the Java application is started.

If my Java app uses log4j, then it needs the log4j.jar file in order to compile (my code integrating with and invoking member methods from inside log4j) as well as runtime (my code has absolutely no control over what happens once code inside log4j.jar is ran).

In this case yes. But suppose that you need to write a portable code that relies on slf4j as facade in front of log4j to be able to switch to another logging implementation later (log4J 2, logback or any other).
In this case in you pom you need to specify slf4j as a compile dependency (it is the default) but you will specify the log4j dependency as a runtime dependency :

<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>

In this way, the log4j classes could not be referenced in the compiled code but you will still be able to refer slf4j classes.
If you specified the two dependencies with the compile time, nothing will prevent you from referencing log4j classes in the compiled code and you could so create an undesirable coupling with the logging implementation :

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>...</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>...</version>
</dependency>

A common usage of runtime scope is the JDBC dependency declaration. To write portable code, you don't want that the client code may refer classes of the specific DBMS dependency (for example : PostgreSQL JDBC dependency) but you want all the same include it in your application as at runtime the classes are needed to make the JDBC API works with this DBMS.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
5

The runtime scope is there to prevent programmers from adding direct dependencies to implementation libraries in the code instead of using abstractions or façades.

In other words, it enforces to use interfaces.

Concrete examples:

1) Your team is using SLF4J over Log4j. You want your programmers to use the SLF4J API, not the Log4j one. Log4j is to be used by SLF4J internally only. Solution:

  • Define SLF4J as a regular compile-time dependency
  • Define log4j-core and log4j-api as runtime dependecies.

2) Your application is accessing MySQL using JDBC. You want your programmers to code against the standard JDBC abstraction, not directly against the MySQL driver implementation.

  • Define mysql-connector-java (MySQL JDBC driver) as a runtime dependency.

Runtime dependencies are hidden during compilation (throwing compile-time errors if your code has a "direct" dependency on them) but are included during execution time and when creating deployable artifacts (WAR files, SHADED jar files, etc.).

Agustí Sánchez
  • 10,455
  • 2
  • 34
  • 25
4

Generally you are right and probasbly it is the ideal situation if runtime and compile time dependencies are identical.

I will give you 2 example when this rule is incorrect.

If class A depends on class B that depends on class C that depends on class D where A is your class and B, C and D are classes from different third party libraries you need only B and C at compile time and you need also D at runtime. Often programs use dynamic class loading. In this case you do not need classes dynamically loaded by library you are using at compile time. Moreover often the library chooses which implementation to use at runtime. For example SLF4J or Commons Logging can change the target log implementation at runtime. You need only SSL4J itself at compile time.

Opposite example when you need more dependencies at compile time than at runtime. Think that you are developing application that has to work at different environments or operating systems. You need all platform specific libraries at compile time and only libraries needed for current environment at runtime.

I hope my explanations help.

AlexR
  • 114,158
  • 16
  • 130
  • 208
  • Can you elaborate on why C is needed at compile time in your example? I get the impression (from http://stackoverflow.com/a/7257518/6095334) that whether or not C is needed at compile time is depending on what methods and fields (from B) A is referencing. – Hervian Jan 25 '17 at 22:16
3

Usually, the static dependencies graph is a sub-graph of the dynamic one, see e.g. this blog entry from the author of NDepend.

That said, there are some exceptions, mainly dependencies that add compiler-support, which becomes invisible at runtime. For instance for code generation as via Lombok or additional checks as via the (pluggable type-)Checker Framework.

mernst
  • 7,437
  • 30
  • 45
DaveFar
  • 7,078
  • 4
  • 50
  • 90
2

Just ran into an issue that answers your question. servlet-api.jar is a transient dependency in my web project and is needed both at compile time and runtime. But servlet-api.jar is also included in my Tomcat library.

The solution here is to make servlet-api.jar in maven available only at compile time and not packaged in my war file so that it would not clash with the servlet-api.jar contained in my Tomcat library.

I hope this explains Compile time and Runtime dependency.

Marko
  • 20,385
  • 13
  • 48
  • 64
Mayoor
  • 131
  • 1
  • 3
  • 8
  • 4
    Your example is actually incorrect for given question, cause it explains the difference between `compile` and `provided` scopes and not between `compile` and `runtime`. `Compile scope` is both needed at compile time and is packaged in your app. `Provided scope` is only needed at compile time but is not packaged in your app cause it is provided by other meant, for example it is already in Tomcat server. – MJar Jul 17 '13 at 07:40
  • 1
    Well, I think this is a rather _good_ example because the question was regarding compile-time and runtime _dependencies_ and not about `compile` and `runtime` _maven scopes_. The `provided` scope is the way maven handles the case where a compile-time dependency should not be included in the runtime package. – Christian Gawron May 26 '16 at 14:43
1

At compile time you enables contracts/api that you are expected from your dependencies. (eg: here you just sign for a contract with broadband internet provider) At run-time actually you are using the dependencies. (eg: here you actually are using the broadband internet)

Nicolae Dascalu
  • 3,425
  • 2
  • 19
  • 17
0

To answer the question "how can a program not depend on something at runtime that it depended on during compilation?", let's look at the example of an annotation processor.

Suppose you've written your own annotation processor, and suppose it has a compile-time dependency oncom.google.auto.service:auto-service so that it can use @AutoService. This dependency is only required to compile the annotation processor, but it is not required at runtime: all other projects depending on your annotation processor for processing annotations do not require the dependency on com.google.auto.service:auto-service at runtime (nor at compile-time nor at any other time).

This is not very common, but it happens.

user23288
  • 301
  • 2
  • 3