Rozumiem różnicę między środowiskiem uruchomieniowym i czasem kompilacji oraz jak je rozróżnić, ale po prostu nie widzę potrzeby rozróżniania między zależnościami czasu kompilacji i czasu wykonania.
Ogólne koncepcje czasu kompilacji i środowiska uruchomieniowego compile
oraz runtime
zależności specyficzne i zakresowe Mavena to dwie bardzo różne rzeczy. Nie można ich bezpośrednio porównywać, ponieważ nie mają one tej samej ramki: ogólne koncepcje kompilacji i środowiska uruchomieniowego są szerokie, podczas gdy koncepcje maven compile
i runtime
zakresu dotyczą konkretnie dostępności / widoczności zależności w zależności od czasu: kompilacja lub wykonanie.
Nie zapominaj, że Maven to przede wszystkim javac
/ java
wrapper, a w Javie masz ścieżkę klasy czasu kompilacji, którą określasz, javac -cp ...
i ścieżkę klas środowiska wykonawczego, którą określasz java -cp ...
.
Nie byłoby błędem traktowanie compile
zakresu Maven jako sposobu na dodanie zależności zarówno w kompilacji Java, jak iw ścieżce klasy środowiska wykonawczego (javac
and java
), podczas gdy runtime
zakres Maven może być postrzegany jako sposób na dodanie zależności tylko w środowisku wykonawczym Java classppath ( javac
).
Dławię się tym: w jaki sposób program może nie zależeć w czasie wykonywania od czegoś, od czego zależał podczas kompilacji?
To, co opisujesz, nie ma żadnego związku runtime
ani compile
zakresu.
Wygląda na to, że bardziej odpowiada provided
zakresowi, który określisz, aby zależność zależała od tego w czasie kompilacji, ale nie w czasie wykonywania.
Używasz go, ponieważ potrzebujesz zależności do kompilacji, ale nie chcesz włączać go do pakietu (JAR, WAR lub innych), ponieważ zależność jest już dostarczona przez środowisko: może być zawarta na serwerze lub w dowolnym ścieżka do ścieżki klasy określonej podczas uruchamiania aplikacji Java.
Jeśli moja aplikacja Java używa log4j, to potrzebuje pliku log4j.jar w celu kompilacji (mój kod integruje się z metodami składowymi i wywołuje je z wnętrza log4j), a także środowiska uruchomieniowego (mój kod nie ma absolutnie żadnej kontroli nad tym, co się stanie, gdy kod wewnątrz log4j .jar jest uruchomiony).
W tym przypadku tak. Ale przypuśćmy, że musisz napisać przenośny kod, który opiera się na slf4j jako fasadzie przed log4j, aby móc później przełączyć się na inną implementację rejestrowania (log4J 2, logback lub dowolną inną).
W tym przypadku w twoim pomie musisz określić slf4j jako compile
zależność (jest to ustawienie domyślne), ale określisz zależność log4j jako runtime
zależność:
<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>
W ten sposób nie można było odwoływać się do klas log4j w skompilowanym kodzie, ale nadal będzie można odwoływać się do klas slf4j.
Jeśli określisz te dwie zależności z compile
czasem, nic nie powstrzyma cię przed odwoływaniem się do klas log4j w skompilowanym kodzie i możesz stworzyć niepożądane sprzężenie z implementacją logowania:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>...</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>...</version>
</dependency>
Typowym zastosowaniem runtime
zakresu jest deklaracja zależności JDBC. Aby napisać kod przenośny, nie chcesz, aby kod klienta mógł odwoływać się do klas określonej zależności DBMS (na przykład: zależność PostgreSQL JDBC), ale chcesz, aby to samo zawierało go w aplikacji, ponieważ w czasie wykonywania klasy są potrzebne do API JDBC współpracuje z tym systemem DBMS.