Bir program, derleme sırasında bir kitaplığa bağlı olabilir, ancak çalışma zamanında değil mi?


110

Çalışma zamanı ve derleme zamanı arasındaki farkı ve ikisi arasında nasıl ayrım yapılacağını anlıyorum, ancak derleme zamanı ve çalışma zamanı bağımlılıkları arasında bir ayrım yapma gereğini görmüyorum .

Boğulduğum şey şudur: Bir program , derleme sırasında bağlı olduğu çalışma zamanında bir şeye nasıl bağlı kalmaz ? Java uygulamam log4j kullanıyorsa, o zaman derlemek için (kodum log4j içinden üye yöntemleriyle bütünleşen ve bunları çağıran kodum) ve çalışma zamanının (kodumun log4j içinde kod olduğunda ne olacağı üzerinde kesinlikle hiçbir kontrolü yoktur) .jar çalıştırılır).

Ivy ve Maven gibi bağımlılık çözümleme araçlarını okuyorum ve bu araçlar bu iki bağımlılık türü arasındaki farkı açıkça ortaya koyuyor. Sadece buna olan ihtiyacı anlamıyorum.

Herkes benim gibi zavallı bir sapın bile anlayabileceği gerçek bir örnekle, basit, "King's English" tipi bir açıklama verebilir mi?


2
Yansımayı kullanabilir ve derleme zamanında mevcut olmayan sınıfları kullanabilirsiniz. "Eklenti" yi düşünün.
Başına Alexandersson

Yanıtlar:


64

Çalışma zamanında genellikle derleme zamanı bağımlılığı gereklidir. Maven'de, compileçalışma zamanında sınıf yoluna kapsamlı bir bağımlılık eklenecektir (örneğin, savaşlarda WEB-INF / lib'ye kopyalanacaklar).

Ancak, kesinlikle gerekli değildir; örneğin, belirli bir API'ye karşı derleme yapabiliriz, bu da onu bir derleme zamanı bağımlılığı yapar, ancak daha sonra çalışma zamanında API'yi de içeren bir uygulama dahil edebiliriz.

Projenin derlemek için belirli bir bağımlılık gerektirdiği ancak daha sonra karşılık gelen koda gerçekten ihtiyaç duyulmayan sınır durumlar olabilir, ancak bunlar nadir olacaktır.

Öte yandan, derleme zamanında ihtiyaç duyulmayan çalışma zamanı bağımlılıklarının dahil edilmesi çok yaygındır. Örneğin, bir Java EE 6 uygulaması yazıyorsanız, Java EE 6 API'sine göre derlersiniz, ancak çalışma zamanında herhangi bir Java EE konteyneri kullanılabilir; uygulamayı sağlayan bu kaptır.

Derleme zamanı bağımlılıkları yansıma kullanılarak önlenebilir. Örneğin, bir JDBC sürücüsü bir ile yüklenebilir Class.forNameve yüklenen gerçek sınıf bir yapılandırma dosyası aracılığıyla yapılandırılabilir.


17
Java EE API hakkında - "sağlanan" bağımlılık kapsamı bunun için değil mi?
Kevin

15
Derlemek için bir bağımlılığın gerekli olduğu ancak çalışma zamanında gerekli olmadığı bir örnek lombok'tur (www.projectlombok.org). Jar, derleme zamanında java kodunu dönüştürmek için kullanılır, ancak çalışma zamanında hiç gerekli değildir. Kapsamı "sağlanan" olarak belirtmek kavanozun savaşa / kavanoz'a dahil edilmemesine neden olur.
Kevin

2
@Kevin Evet, iyi bir nokta, providedkapsam, bağımlılığın başka yollarla çalışma zamanında sağlanacağı beklentisine bir çalışma zamanı bağımlılığı eklemeden bir derleme zamanı bağımlılığı ekler (örneğin, kapsayıcıda paylaşılan bir kitaplık). runtimediğer yandan, derleme zamanı bağımlılığı yapmadan çalışma zamanı bağımlılığı ekler.
Artefacto

Öyleyse, bir "modül yapılandırması" (Ivy terimlerini kullanan) ile proje kökünüz altındaki ana dizin arasında genellikle 1: 1 korelasyon olduğunu söylemek güvenli midir? Örneğin, JUnit JAR'a bağlı tüm JUnit testlerim test / kök altında olacak, vb. Aynı kaynak kök altında paketlenen aynı sınıfların farklı Herhangi bir zamanda JAR'lar. Log4j'ye ihtiyacınız varsa, log4j'ye ihtiyacınız var; 1 yapılandırma altında log4j çağrılarını çağırmak için aynı kodu söylemenin bir yolu yoktur, ancak bazı "günlük tutmayan" yapılandırmalar altında log4j çağrılarını yok saymanın yolu yoktur, değil mi?
IAmYourFaja

30

Her Maven bağımlılığının, bağımlılığın hangi sınıf yolunda kullanılabileceğini tanımlayan bir kapsamı vardır.

Bir proje için JAR oluşturduğunuzda, bağımlılıklar oluşturulan yapı ile paketlenmez; sadece derleme için kullanılırlar. (Bununla birlikte, maven'in yerleşik kavanozdaki bağımlılıkları dahil etmesini sağlayabilirsiniz, bkz: Maven ile bir kavanozda bağımlılıkları dahil etme )

Bir WAR veya EAR dosyası oluşturmak için Maven'i kullandığınızda, Maven'i bağımlılıkları oluşturulan yapı ile bir araya getirecek şekilde yapılandırabilir ve ayrıca sağlanan kapsamı kullanarak WAR dosyasından belirli bağımlılıkları dışlayacak şekilde yapılandırabilirsiniz.

En yaygın kapsam - Derleme Kapsamı - uygulamanızı çalıştırdığınızda derleme sınıf yolunda, birim testi derleme ve yürütme sınıf yollarında ve nihai çalışma zamanı sınıf yolunda bağımlılığın projenizde mevcut olduğunu gösterir. Bir Java EE web uygulamasında bu, bağımlılığın konuşlandırılmış uygulamanıza kopyalandığı anlamına gelir. Ancak bir .jar dosyasında, bağımlılıklar derleme kapsamına dahil edilmeyecektir.

Çalışma Zamanı Kapsamı , projenizde birim testi yürütme ve çalışma zamanı yürütme sınıf yollarına bağımlılığın mevcut olduğunu gösterir, ancak derleme kapsamından farklı olarak , uygulamanızı veya birim testlerini derlediğinizde kullanılamaz . Bir Çalışma Zamanı Bağımlılığı, konuşlandırılan uygulamanıza kopyalanır, ancak derleme sırasında kullanılamaz! Bu, yanlışlıkla belirli bir kitaplığa bağlı olmadığınızdan emin olmak için iyidir.

Son olarak, Sağlanan Kapsam , uygulamanızın içinde yürütüldüğü kapsayıcının sizin adınıza bağımlılığı sağladığını belirtir. Bir Java EE uygulamasında bu, bağımlılığın zaten Servlet kapsayıcısının veya uygulama sunucusunun sınıf yolunda olduğu ve konuşlandırılmış uygulamanıza kopyalanmadığı anlamına gelir . Ayrıca, projenizi derlemek için bu bağımlılığa ihtiyacınız olduğu anlamına gelir.


@Koray Tugay Cevap daha kesin :) Hızlı bir sorum var diyelim ki çalışma süresi kapsamı ile bağımlı bir kavanozum var. Maven derleme zamanında kavanozu arayacak mı?
GKS

@gks Hayır, derleme zamanında gerektirmez.
Koray Tugay

9

Çalışma zamanında ihtiyaç duyabileceğiniz derleme zamanı bağımlılıklarına ihtiyacınız vardır. Ancak birçok kitaplık, tüm olası bağımlılıkları olmadan çalışır. yani, dört farklı XML kitaplığı kullanabilen, ancak çalışmak için yalnızca birine ihtiyaç duyan bir kitaplık.

Birçok kütüphane, sırayla diğer kütüphanelere ihtiyaç duyar. Bu kitaplıklar derleme sırasında gerekli değildir, ancak çalışma zamanında gereklidir. yani kod gerçekten çalıştırıldığında.


Derleme sırasında ihtiyaç duyulmayacak, ancak çalışma zamanında ihtiyaç duyulacak bu tür kitaplıkların örneklerini bize verebilir misiniz?
Cristiano

1
@Cristiano tüm JDBC kitaplıkları böyledir. Ayrıca standart bir API uygulayan kitaplıklar.
Peter Lawrey

4

Genelde haklısınız ve muhtemelen çalışma zamanı ve derleme zamanı bağımlılıkları aynıysa bu ideal durumdur.

Bu kural yanlış olduğunda size 2 örnek vereceğim.

A sınıfı, D sınıfına bağlı olan B sınıfına bağlıysa, burada A sizin sınıfınızdır ve B, C ve D farklı üçüncü taraf kitaplıklarından gelen sınıflardır ve derleme zamanında yalnızca B ve C'ye ihtiyacınız vardır ve ayrıca Çalışma süresi. Genellikle programlar dinamik sınıf yüklemesi kullanır. Bu durumda, derleme zamanında kullandığınız kitaplık tarafından dinamik olarak yüklenen sınıflara ihtiyacınız yoktur. Ayrıca, çoğu zaman kütüphane çalışma zamanında hangi uygulamanın kullanılacağını seçer. Örneğin SLF4J veya Commons Logging, çalışma zamanında hedef günlük uygulamasını değiştirebilir. Derleme sırasında yalnızca SSL4J'nin kendisine ihtiyacınız vardır.

Derleme zamanında çalışma zamanındakinden daha fazla bağımlılığa ihtiyacınız olduğunda karşıt örnek. Farklı ortamlarda veya işletim sistemlerinde çalışması gereken bir uygulama geliştirdiğinizi düşünün. Derleme zamanında tüm platforma özgü kitaplıklara ve yalnızca çalışma zamanında geçerli ortam için gereken kitaplıklara ihtiyacınız vardır.

Umarım açıklamalarım yardımcı olur.


Örneğinizde derleme sırasında neden C'ye ihtiyaç duyulduğunu açıklayabilir misiniz? Derleme zamanında C'nin gerekip gerekmediği (B'den) A'nın hangi yöntemlere ve alanlara (B'den) başvurduğuna bağlı olduğu izlenimini edindim ( stackoverflow.com/a/7257518/6095334 adresinden).
Hervian


2

Sorunuzu yanıtlayan bir sorunla karşılaştım. servlet-api.jarweb projemde geçici bir bağımlılıktır ve hem derleme zamanında hem de çalışma zamanında gereklidir. Ama servlet-api.jaraynı zamanda Tomcat kütüphanemde de var.

Buradaki çözüm servlet-api.jar, maven'i yalnızca derleme sırasında kullanılabilir hale getirmek ve servlet-api.jarTomcat kitaplığımda bulunanlarla çakışmaması için savaş dosyamda paketlenmemektir .

Umarım bu, Derleme zamanını ve Çalışma Zamanı bağımlılığını açıklar.


3
O arasındaki farkı açıklar neden senin örneğin verilen soru için aslında yanlıştır compileve providedkapsamları ve aralarında compileve runtime. Compile scopehem derleme sırasında gereklidir hem de uygulamanızda paketlenmiştir. Provided scopeyalnızca derleme sırasında gereklidir, ancak uygulamanızda paketlenmemesi nedeniyle başka bir amaçla sağlanmıştır, örneğin zaten Tomcat sunucusunda bulunmaktadır.
MJar

1
Bence bu oldukça iyi bir örnek çünkü soru derleme zamanı ve çalışma zamanı bağımlılıkları ile ilgiliydi compileve runtime kapsamlar hakkında değil . providedKapsam kolları derleme zamanı bağımlılık çalışma zamanı paketine dahil edilmemelidir durum maven yoludur.
Christian Gawron

1

Çalışma zamanı ve derleme zamanı arasındaki farkı ve ikisi arasında nasıl ayrım yapılacağını anlıyorum, ancak derleme zamanı ve çalışma zamanı bağımlılıkları arasında bir ayrım yapma gereğini görmüyorum.

Genel derleme zamanı ve çalışma zamanı kavramları ve Maven'e özgü compileve runtimekapsam bağımlılıkları iki çok farklı şeydir. Bunlar aynı çerçeveye sahip olmadığından bunları doğrudan karşılaştıramazsınız: genel derleme ve çalışma zamanı kavramları genişken, maven compileve runtimekapsam kavramları özellikle zamana göre bağımlılıklar kullanılabilirliği / görünürlüğü ile ilgilidir: derleme veya yürütme.
Maven'in her şeyden önce bir javac/ javasarmalayıcı olduğunu ve Java'da belirttiğiniz bir derleme zamanı sınıf yoluna javac -cp ... ve belirlediğiniz bir çalışma zamanı sınıf yoluna sahip olduğunuzu unutmayın java -cp ....
Maven compilekapsamını hem Java derlemesinde hem de çalışma zamanı sınıfında bir bağımlılık eklemenin bir yolu olarak düşünmek yanlış olmaz (javacve java) Maven runtimekapsamı yalnızca Java çalışma zamanı classppath ( javac) içinde bir bağımlılık eklemenin bir yolu olarak görülebilir .

Boğulduğum şey şudur: Bir program, derleme sırasında bağlı olduğu çalışma zamanında bir şeye nasıl bağlı kalmaz?

Tanımladığınız şeyin kapsamı runtimeve ilişkisi yoktur compile. Bir bağımlılığın, çalışma zamanında değil, derleme zamanında buna bağlı olması için belirttiğiniz kapsama
daha çok benziyor provided.
Derlemek için bağımlılığa ihtiyaç duyduğunuzda kullanırsınız, ancak bunu paketlenmiş bileşene (JAR, WAR veya diğerleri) dahil etmek istemezsiniz çünkü bağımlılık zaten ortam tarafından sağlanır : sunucuya dahil edilebilir veya herhangi bir Java uygulaması başlatılırken belirtilen sınıf yolunun yolu.

Java uygulamam log4j kullanıyorsa, o zaman derlemek için (kodum log4j içinden üye yöntemleriyle bütünleşen ve bunları çağıran kodum) ve çalışma zamanının (kodumun log4j içinde kod olduğunda ne olacağı üzerinde kesinlikle hiçbir kontrolü yoktur) için log4j.jar dosyasına ihtiyaç duyar .jar çalıştırılır).

Bu durumda evet. Ancak daha sonra başka bir günlük uygulamasına (log4J 2, logback veya başka bir) geçebilmek için log4j'nin önünde ön cephe olarak slf4j'ye dayanan taşınabilir bir kod yazmanız gerektiğini varsayalım.
Bu durumda, compilebağımlılık olarak slf4j belirtmeniz gerekir (bu varsayılandır), ancak bağımlılık olarak log4j bağımlılığını belirteceksiniz 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>

Bu şekilde, log4j sınıflarına derlenen kodda başvurulamaz, ancak yine de slf4j sınıflarına başvurabilirsiniz.
İki bağımlılığı compilezamanla belirttiyseniz, hiçbir şey derlenen kodda log4j sınıflarına başvurmanızı engellemez ve böylece günlük uygulamasıyla istenmeyen bir bağlantı oluşturabilirsiniz:

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

runtimeKapsamın yaygın bir kullanımı , JDBC bağımlılık bildirimidir. Taşınabilir kod yazmak için, istemci kodunun belirli DBMS bağımlılık sınıflarına başvurmasını istemezsiniz (örneğin: PostgreSQL JDBC bağımlılığı), ancak sınıfların yapması gereken çalışma zamanında uygulamanıza dahil edilmesini istersiniz. JDBC API bu DBMS ile çalışır.


0

Derleme zamanında, bağımlılıklarınızdan beklenen sözleşmeleri / api'leri etkinleştirirsiniz. (örneğin: burada geniş bant internet sağlayıcısı ile bir sözleşme imzalamanız yeterlidir) Çalışma zamanında aslında bağımlılıkları kullanıyorsunuz. (örneğin: burada aslında geniş bant interneti kullanıyorsunuz)


0

"Bir program, derleme sırasında bağlı olduğu çalışma zamanında bir şeye nasıl bağlı olmaz?" Sorusuna cevap vermek için, bir açıklama işlemcisi örneğine bakalım.

Kendi açıklama işlemci yazdım varsayalım ve bu bir derleme zamanı bağımlılık vardır herhalde com.google.auto.service:auto-service, kullanabileceği şekilde @AutoService. Bu bağımlılık tek ek açıklama işlemci derlemek için gereklidir, ancak zamanında gerekli değildir: Diğer tüm projeler yapmak açıklamaları işlenmesi için ek açıklama işlemci bağlı değil bağımlılığı gerektiren com.google.auto.service:auto-servicezamanında (ne de derleme sırasında ne de başka zamanda) .

Bu çok yaygın değil ama oluyor.


0

runtimeKapsam kodunda uygulama kütüphaneleri doğrudan bağımlılıkları ekleme yerine soyutlamalar veya cepheleri kullanmasını programcıları önlemek için vardır.

Başka bir deyişle, arayüz kullanmayı zorunlu kılar.

Somut örnekler:

1) Ekibiniz Log4j üzerinden SLF4J kullanıyor. Programcılarınızın Log4j API'sini değil, SLF4J API'sini kullanmasını istiyorsunuz. Log4j yalnızca dahili olarak SLF4J tarafından kullanılacaktır. Çözüm:

  • SLF4J'yi düzenli bir derleme zamanı bağımlılığı olarak tanımlayın
  • Log4j-core ve log4j-api'yi çalışma zamanı bağımlılıkları olarak tanımlayın.

2) Uygulamanız JDBC kullanarak MySQL'e erişiyor. Programcılarınızın doğrudan MySQL sürücü uygulamasına değil, standart JDBC soyutlamasına göre kodlamasını istiyorsunuz.

  • mysql-connector-java(MySQL JDBC sürücüsü) bir çalışma zamanı bağımlılığı olarak tanımlayın .

Çalışma zamanı bağımlılıkları derleme sırasında gizlenir (kodunuzun "doğrudan" bağımlılığı varsa derleme zamanı hataları atılır), ancak yürütme sırasında ve konuşlandırılabilir yapılar (WAR dosyaları, SHADED jar dosyaları vb.) Oluşturulurken dahil edilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.