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.