JavaRush /Blog Java /Random-MS /Lawatan singkat ke suntikan pergantungan atau "Apakah lag...

Lawatan singkat ke suntikan pergantungan atau "Apakah lagi CDI?"

Diterbitkan dalam kumpulan
Asas di mana rangka kerja yang paling popular kini dibina ialah suntikan pergantungan. Saya cadangkan melihat apa yang dinyatakan oleh spesifikasi CDI tentang ini, apakah keupayaan asas yang kita ada dan bagaimana kita boleh menggunakannya.
Lawatan singkat ke suntikan pergantungan atau

pengenalan

Saya ingin menumpukan ulasan ringkas ini kepada perkara seperti CDI. Apakah ini? CDI adalah singkatan dari Contexts and Dependency Injection. Ini ialah spesifikasi Java EE yang menerangkan Suntikan Ketergantungan dan konteks. Untuk maklumat, anda boleh melihat laman web http://cdi-spec.org . Memandangkan CDI ialah spesifikasi (penerangan tentang cara ia harus berfungsi, satu set antara muka), kami juga memerlukan pelaksanaan untuk menggunakannya. Salah satu pelaksanaan tersebut ialah Weld - http://weld.cdi-spec.org/ Untuk menguruskan kebergantungan dan mencipta projek, kami akan menggunakan Maven - https://maven.apache.org Jadi, kami telah memasang Maven, sekarang kami akan memahaminya dalam amalan, supaya tidak memahami abstrak. Untuk melakukan ini, kami akan membuat projek menggunakan Maven. Mari buka baris arahan (dalam Windows, anda boleh menggunakan Win+R untuk membuka tetingkap "Run" dan laksanakan cmd) dan minta Maven melakukan segala-galanya untuk kami. Untuk ini, Maven mempunyai konsep yang dipanggil archetype: Maven Archetype .
Lawatan singkat ke suntikan pergantungan atau
Selepas itu, pada soalan " Pilih nombor atau gunakan penapis " dan " Pilih org.apache.maven.archetypes:maven-archetype-quickstart version " cuma tekan Enter. Seterusnya, masukkan pengecam projek, yang dipanggil GAV (lihat Panduan Konvensyen Penamaan ).
Lawatan singkat ke suntikan pergantungan atau
Selepas berjaya mencipta projek, kita akan melihat tulisan "BINA KEJAYAAN". Kini kami boleh membuka projek kami dalam IDE kegemaran kami.

Menambah CDI pada Projek

Dalam pengenalan, kami melihat bahawa CDI mempunyai laman web yang menarik - http://www.cdi-spec.org/ . Terdapat bahagian muat turun, yang mengandungi jadual yang mengandungi data yang kami perlukan:
Lawatan singkat ke suntikan pergantungan atau
Di sini kita dapat melihat bagaimana Maven menerangkan fakta bahawa kita menggunakan API CDI dalam projek itu. API ialah antara muka pengaturcaraan aplikasi, iaitu beberapa antara muka pengaturcaraan. Kami bekerja dengan antara muka tanpa perlu risau tentang perkara dan cara ia berfungsi di sebalik antara muka ini. API ialah arkib balang yang akan kami mula gunakan dalam projek kami, iaitu projek kami mula bergantung pada balang ini. Oleh itu, API CDI untuk projek kami adalah kebergantungan. Dalam Maven, projek diterangkan dalam fail POM.xml ( POM - Model Objek Projek ). Ketergantungan diterangkan dalam blok kebergantungan, yang mana kita perlu menambah entri baharu:
<dependency>
	<groupId>javax.enterprise</groupId>
	<artifactId>cdi-api</artifactId>
	<version>2.0</version>
</dependency>
Seperti yang anda mungkin perasan, kami tidak menentukan skop dengan nilai yang disediakan. Mengapa terdapat perbezaan sedemikian? Skop ini bermakna seseorang akan memberikan kita pergantungan. Apabila aplikasi berjalan pada pelayan Java EE, ini bermakna pelayan akan menyediakan aplikasi dengan semua teknologi JEE yang diperlukan. Demi kesederhanaan ulasan ini, kami akan bekerja dalam persekitaran Java SE, oleh itu tiada siapa yang akan memberikan kami pergantungan ini. Anda boleh membaca lebih lanjut mengenai Skop Ketergantungan di sini: " Skop Ketergantungan ". Okay, kami kini mempunyai keupayaan untuk bekerja dengan antara muka. Tetapi kita juga memerlukan pelaksanaan. Seperti yang kita ingat, kita akan menggunakan Weld. Adalah menarik bahawa kebergantungan yang berbeza diberikan di mana-mana. Tetapi kami akan mengikuti dokumentasi. Oleh itu, mari baca " 18.4.5. Menetapkan Laluan Kelas " dan lakukan seperti yang dinyatakan:
<dependency>
	<groupId>org.jboss.weld.se</groupId>
	<artifactId>weld-se-core</artifactId>
	<version>3.0.5.Final</version>
</dependency>
Adalah penting bahawa versi baris ketiga Weld menyokong CDI 2.0. Oleh itu, kita boleh bergantung pada API versi ini. Kini kami bersedia untuk menulis kod.
Lawatan singkat ke suntikan pergantungan atau

Memulakan bekas CDI

CDI adalah mekanisme. Seseorang mesti mengawal mekanisme ini. Seperti yang telah kita baca di atas, pengurus sedemikian adalah bekas. Oleh itu, kita perlu menciptanya; ia sendiri tidak akan muncul dalam persekitaran SE. Mari tambah yang berikut pada kaedah utama kami:
public static void main(String[] args) {
	SeContainerInitializer initializer = SeContainerInitializer.newInstance();
	initializer.addPackages(App.class.getPackage());
	SeContainer container = initializer.initialize();
}
Kami mencipta bekas CDI secara manual kerana... Kami bekerja dalam persekitaran SE. Dalam projek pertempuran biasa, kod berjalan pada pelayan, yang menyediakan pelbagai teknologi kepada kod. Sehubungan itu, jika pelayan menyediakan CDI, ini bermakna pelayan sudah mempunyai bekas CDI dan kami tidak perlu menambah apa-apa. Tetapi untuk tujuan tutorial ini, kami akan mengambil persekitaran SE. Di samping itu, bekas itu ada di sini, dengan jelas dan difahami. Mengapa kita memerlukan bekas? Bekas di dalamnya mengandungi kacang (kacang CDI).
Lawatan singkat ke suntikan pergantungan atau

Kacang CDI

Jadi, kacang. Apakah tong CDI? Ini ialah kelas Java yang mengikut beberapa peraturan. Peraturan ini diterangkan dalam spesifikasi, dalam bab " 2.2. Apakah jenis kelas kacang? ". Mari tambahkan kacang CDI pada pakej yang sama dengan kelas App:
public class Logger {
    public void print(String message) {
        System.out.println(message);
    }
}
Sekarang kita boleh memanggil kacang ini dari mainkaedah kami:
Logger logger = container.select(Logger.class).get();
logger.print("Hello, World!");
Seperti yang anda lihat, kami tidak mencipta kacang menggunakan kata kunci baharu. Kami meminta bekas CDI: "Bekas CDI. Saya sangat memerlukan contoh kelas Logger, tolong berikan kepada saya." Kaedah ini dipanggil " Pencarian ketergantungan ", iaitu, mencari kebergantungan. Sekarang mari buat kelas baharu:
public class DateSource {
    public String getDate() {
        return new Date().toString();
    }
}
Kelas primitif yang mengembalikan perwakilan teks tarikh. Sekarang mari tambah output tarikh pada mesej:
public class Logger {
    @Inject
    private DateSource dateSource;

    public void print(String message) {
        System.out.println(dateSource.getDate() + " : " + message);
    }
}
Anotasi @Inject yang menarik telah muncul. Seperti yang dinyatakan dalam bab " 4.1. Titik suntikan " dokumentasi kimpalan cdi, menggunakan anotasi ini kami mentakrifkan Titik Suntikan. Dalam bahasa Rusia, ini boleh dibaca sebagai "titik pelaksanaan". Ia digunakan oleh bekas CDI untuk menyuntik kebergantungan semasa membuat instantiat kacang. Seperti yang anda lihat, kami tidak memberikan sebarang nilai kepada medan dateSource. Sebab untuk ini adalah fakta bahawa bekas CDI membenarkan di dalam kacang CDI (hanya kacang yang dibuatnya sendiri, iaitu ia berjaya) menggunakan " Suntikan Ketergantungan ". Ini adalah satu lagi cara Penyongsangan Kawalan , pendekatan di mana kebergantungan dikawal oleh orang lain dan bukannya kami secara eksplisit mencipta objek. Suntikan kebergantungan boleh dilakukan melalui kaedah, pembina, atau medan. Untuk butiran lanjut, lihat bab spesifikasi CDI " 5.5. Suntikan Ketergantungan ". Prosedur untuk menentukan apa yang perlu dilaksanakan dipanggil typesafe resolution, yang perlu kita bincangkan.
Lawatan singkat ke suntikan pergantungan atau

Resolusi nama atau resolusi Typesafe

Biasanya, antara muka digunakan sebagai jenis objek yang akan dilaksanakan, dan bekas CDI sendiri menentukan pelaksanaan yang hendak dipilih. Ini berguna untuk banyak sebab, yang akan kita bincangkan. Jadi kami mempunyai antara muka logger:
public interface Logger {
    void print(String message);
}
Dia mengatakan bahawa jika kita mempunyai beberapa pembalak, kita boleh menghantar mesej kepadanya dan ia akan menyelesaikan tugasnya - log. Bagaimana dan di mana tidak akan menjadi kepentingan dalam kes ini. Sekarang mari kita buat pelaksanaan untuk pembalak:
public class SystemOutLogger implements Logger {
    @Inject
    private DateSource dateSource;

    public void print(String message) {
        System.out.println(message);
    }
}
Seperti yang anda lihat, ini ialah pembalak yang menulis ke System.out. Hebat. Sekarang, kaedah utama kami akan berfungsi seperti dahulu. Logger logger = container.select(Logger.class).get(); Talian ini masih akan diterima oleh pembalak. Dan keindahannya ialah kita hanya perlu mengetahui antara muka, dan bekas CDI sudah memikirkan tentang pelaksanaan untuk kita. Katakan kita mempunyai pelaksanaan kedua yang sepatutnya menghantar log ke suatu tempat ke storan jauh:
public class NetworkLogger implements Logger {
    @Override
    public void print(String message) {
        System.out.println("Send log message to remote log system");
    }
}
Jika kita sekarang menjalankan kod kita tanpa perubahan, kita akan mendapat ralat, kerana Bekas CDI melihat dua pelaksanaan antara muka dan tidak boleh memilih antaranya: org.jboss.weld.exceptions.AmbiguousResolutionException: WELD-001335: Ambiguous dependencies for type Logger Apa yang perlu dilakukan? Terdapat beberapa variasi yang tersedia. Yang paling mudah ialah anotasi @Vetoed untuk kacang CDI supaya bekas CDI tidak menganggap kelas ini sebagai kacang CDI. Tetapi ada pendekatan yang lebih menarik. Biji CDI boleh ditandakan sebagai "alternatif" menggunakan anotasi @Alternativeyang diterangkan dalam bab " 4.7. Alternatif " dalam dokumentasi CDI Weld. Apakah maksudnya? Ini bermakna melainkan jika kami menyatakan secara jelas untuk menggunakannya, ia tidak akan dipilih. Ini adalah versi alternatif kacang. Mari tandakan kacang NetworkLogger sebagai @Alternatif dan kita dapat melihat bahawa kod itu dilaksanakan semula dan digunakan oleh SystemOutLogger. Untuk mendayakan alternatif, kita mesti mempunyai fail beans.xml . Soalan mungkin timbul: " beans.xml, di manakah saya meletakkan anda? " Oleh itu, mari letakkan fail dengan betul:
Lawatan singkat ke suntikan pergantungan atau
Sebaik sahaja kami mempunyai fail ini, artifak dengan kod kami akan dipanggil " Arkib kacang eksplisit ". Kini kami mempunyai 2 konfigurasi berasingan: perisian dan xml. Masalahnya ialah mereka akan memuatkan data yang sama. Sebagai contoh, definisi kacang DataSource akan dimuatkan 2 kali dan program kami akan ranap apabila dilaksanakan, kerana Bekas CDI akan menganggapnya sebagai 2 biji berasingan (walaupun sebenarnya mereka adalah kelas yang sama, yang bekas CDI belajar dua kali). Untuk mengelakkan ini terdapat 2 pilihan:
  • alih keluar baris initializer.addPackages(App.class.getPackage())dan tambahkan petunjuk alternatif kepada fail xml:
<beans
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
    <alternatives>
        <class>ru.javarush.NetworkLogger</class>
    </alternatives>
</beans>
  • tambahkan atribut bean-discovery-modedengan nilai " tiada " pada elemen akar kacang dan nyatakan alternatif secara pemrograman:
initializer.addPackages(App.class.getPackage());
initializer.selectAlternatives(NetworkLogger.class);
Oleh itu, menggunakan alternatif CDI, bekas boleh menentukan kacang yang hendak dipilih. Menariknya, jika bekas CDI mengetahui beberapa alternatif untuk antara muka yang sama, maka kita boleh memberitahunya dengan menunjukkan keutamaan menggunakan anotasi @Priority(Sejak CDI 1.1).
Lawatan singkat ke suntikan pergantungan atau

Kelayakan

Secara berasingan, adalah wajar membincangkan perkara seperti kelayakan. Kelayakan ditunjukkan dengan anotasi di atas kacang dan memperhalusi carian untuk kacang. Dan sekarang butiran lanjut. Menariknya, mana-mana kacang CDI dalam apa jua keadaan mempunyai sekurang-kurangnya satu kelayakan - @Any. Jika kami tidak menyatakan SEBARANG kelayakan di atas kacang, tetapi bekas CDI itu sendiri menambah @Anykelayakan lain kepada kelayakan - @Default. Jika kami menentukan apa-apa (contohnya, nyatakan @Any), maka kelayakan @Default tidak akan ditambahkan secara automatik. Tetapi keindahan kelayakan ialah anda boleh membuat kelayakan anda sendiri. Kelayakan hampir tidak berbeza daripada anotasi, kerana pada dasarnya, ini hanyalah anotasi yang ditulis dengan cara yang istimewa. Sebagai contoh, anda boleh memasukkan Enum untuk jenis protokol:
public enum ProtocolType {
    HTTP, HTTPS
}
Seterusnya kita boleh membuat kelayakan yang akan mengambil kira jenis ini:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Protocol {
    ProtocolType value();
    @Nonbinding String comment() default "";
}
Perlu diingat bahawa medan yang ditandakan sebagai @Nonbindingtidak menjejaskan penentuan kelayakan. Sekarang anda perlu menentukan kelayakan. Ia ditunjukkan di atas jenis kacang (supaya CDI tahu cara mentakrifkannya) dan di atas Titik Suntikan (dengan anotasi @Inject, supaya anda memahami kacang mana yang hendak dicari untuk suntikan di tempat ini). Sebagai contoh, kita boleh menambah beberapa kelas dengan kelayakan. Untuk kesederhanaan, untuk artikel ini kami akan melakukannya di dalam NetworkLogger:
public interface Sender {
	void send(byte[] data);
}

@Protocol(ProtocolType.HTTP)
public static class HTTPSender implements Sender{
	public void send(byte[] data) {
		System.out.println("sended via HTTP");
	}
}

@Protocol(ProtocolType.HTTPS)
public static class HTTPSSender implements Sender{
	public void send(byte[] data) {
		System.out.println("sended via HTTPS");
	}
}
Dan kemudian apabila kami melakukan Inject, kami akan menentukan kelayakan yang akan mempengaruhi kelas mana yang akan digunakan:
@Inject
@Protocol(ProtocolType.HTTPS)
private Sender sender;
Hebat, bukan?) Nampak cantik, tetapi tidak jelas mengapa. Sekarang bayangkan perkara berikut:
Protocol protocol = new Protocol() {
	@Override
	public Class<? extends Annotation> annotationType() {
		return Protocol.class;
	}
	@Override
	public ProtocolType value() {
		String value = "HTTP";
		return ProtocolType.valueOf(value);
	}
};
container.select(NetworkLogger.Sender.class, protocol).get().send(null);
Dengan cara ini kita boleh mengatasi mendapatkan nilai supaya ia boleh dikira secara dinamik. Sebagai contoh, ia boleh diambil dari beberapa tetapan. Kemudian kita boleh menukar pelaksanaan walaupun dengan cepat, tanpa menyusun semula atau memulakan semula program/pelayan. Ia menjadi lebih menarik, bukan? )
Lawatan singkat ke suntikan pergantungan atau

Pengeluar

Satu lagi ciri berguna CDI ialah pengeluar. Ini adalah kaedah khas (ia ditandakan dengan anotasi khas) yang dipanggil apabila sesetengah kacang telah meminta suntikan kebergantungan. Butiran lanjut diterangkan dalam dokumentasi, dalam bahagian " 2.2.3. Kaedah pengeluar ". Contoh paling mudah:
@Produces
public Integer getRandomNumber() {
	return new Random().nextInt(100);
}
Sekarang, apabila Menyuntik ke dalam medan jenis Integer, kaedah ini akan dipanggil dan nilai akan diperoleh daripadanya. Di sini kita harus segera memahami bahawa apabila kita melihat kata kunci baru, kita mesti segera memahami bahawa ini BUKAN kacang CDI. Iaitu, contoh kelas Rawak tidak akan menjadi kacang CDI hanya kerana ia berasal daripada sesuatu yang mengawal bekas CDI (dalam kes ini, pengeluar).
Lawatan singkat ke suntikan pergantungan atau

Pemintas

Pemintas ialah pemintas yang "mengganggu" kerja. Dalam CDI ini dilakukan dengan jelas. Mari lihat bagaimana kita boleh melakukan pengelogan menggunakan jurubahasa (atau pemintas). Pertama, kita perlu menerangkan pengikatan kepada pemintas. Seperti banyak perkara, ini dilakukan menggunakan anotasi:
@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface ConsoleLog {
}
Perkara utama di sini ialah ini adalah pengikatan untuk pemintas ( @InterceptorBinding), yang akan diwarisi oleh extends ( @InterceptorBinding). Sekarang mari kita tulis pemintas itu sendiri:
@Interceptor
@ConsoleLog
public class LogInterceptor {
    @AroundInvoke
    public Object log(InvocationContext ic) throws Exception {
        System.out.println("Invocation method: " + ic.getMethod().getName());
        return ic.proceed();
    }
}
Anda boleh membaca lebih lanjut tentang cara pemintas ditulis dalam contoh daripada spesifikasi: " 1.3.6. Contoh pemintas ". Nah, apa yang perlu kita lakukan ialah menghidupkan inerceptor. Untuk melakukan ini, nyatakan anotasi yang mengikat di atas kaedah yang sedang dilaksanakan:
@ConsoleLog
public void print(String message) {
Dan kini satu lagi butiran yang sangat penting. Pemintas dilumpuhkan secara lalai dan mesti didayakan dengan cara yang sama seperti alternatif. Contohnya, dalam fail beans.xml :
<interceptors>
	<class>ru.javarush.LogInterceptor</class>
</interceptors>
Seperti yang anda lihat, ia agak mudah.
Lawatan singkat ke suntikan pergantungan atau

Peristiwa & Pemerhati

CDI juga menyediakan model peristiwa dan pemerhati. Di sini semuanya tidak begitu jelas seperti pemintas. Jadi, Peristiwa dalam kes ini boleh menjadi mana-mana kelas; tiada yang istimewa diperlukan untuk penerangan. Sebagai contoh:
public class LogEvent {
    Date date = new Date();
    public String getDate() {
        return date.toString();
    }
}
Sekarang seseorang harus menunggu acara itu:
public class LogEventListener {
    public void logEvent(@Observes LogEvent event){
        System.out.println("Message Date: " + event.getDate());
    }
}
Perkara utama di sini adalah untuk menentukan anotasi @Observes, yang menunjukkan bahawa ini bukan hanya kaedah, tetapi kaedah yang harus dipanggil sebagai hasil pemerhatian peristiwa jenis LogEvent. Nah, sekarang kita memerlukan seseorang yang akan menonton:
public class LogObserver {
    @Inject
    private Event<LogEvent> event;
    public void observe(LogEvent logEvent) {
        event.fire(logEvent);
    }
}
Kami mempunyai satu kaedah yang akan memberitahu bekas bahawa peristiwa Acara telah berlaku untuk jenis acara LogEvent. Sekarang yang tinggal hanyalah menggunakan pemerhati. Sebagai contoh, dalam NetworkLogger kami boleh menambah suntikan pemerhati kami:
@Inject
private LogObserver observer;
Dan dalam kaedah cetakan kami boleh memberitahu pemerhati bahawa kami mempunyai acara baharu:
public void print(String message) {
	observer.observe(new LogEvent());
Adalah penting untuk mengetahui bahawa acara boleh diproses dalam satu utas atau dalam beberapa. Untuk pemprosesan tak segerak, gunakan kaedah .fireAsync(bukannya .fire) dan anotasi @ObservesAsync(bukannya @Observes). Contohnya, jika semua acara dilaksanakan dalam urutan yang berbeza, maka jika 1 utas melemparkan Pengecualian, maka yang lain akan dapat melakukan kerja mereka untuk acara lain. Anda boleh membaca lebih lanjut mengenai acara dalam CDI, seperti biasa, dalam spesifikasi, dalam bab " 10. Acara ".
Lawatan singkat ke suntikan pergantungan atau

Penghias

Seperti yang kita lihat di atas, pelbagai corak reka bentuk dikumpulkan di bawah sayap CDI. Dan inilah seorang lagi - seorang penghias. Ini adalah perkara yang sangat menarik. Mari kita lihat kelas ini:
@Decorator
public abstract class LoggerDecorator implements Logger {
    public final static String ANSI_GREEN = "\u001B[32m";
    public static final String ANSI_RESET = "\u001B[0m";

    @Inject
    @Delegate
    private Logger delegate;

    @Override
    public void print(String message) {
        delegate.print(ANSI_GREEN + message + ANSI_RESET);
    }
}
Dengan mengisytiharkannya sebagai penghias, kami mengatakan bahawa apabila mana-mana pelaksanaan Logger digunakan, "tambahan" ini akan digunakan, yang mengetahui pelaksanaan sebenar, yang disimpan dalam medan perwakilan (kerana ia ditandakan dengan anotasi @Delegate). Penghias hanya boleh dikaitkan dengan kacang CDI, yang bukan pemintas mahupun penghias. Contoh juga boleh dilihat dalam spesifikasi: " 1.3.7. Contoh penghias ". Penghias, seperti pemintas, mesti dihidupkan. Contohnya, dalam beans.xml :
<decorators>
	<class>ru.javarush.LoggerDecorator</class>
</decorators>
Untuk butiran lanjut, lihat rujukan kimpalan: " Bab 10. Penghias ".

Kitaran hidup

Kacang mempunyai kitaran hidup mereka sendiri. Ia kelihatan seperti ini:
Lawatan singkat ke suntikan pergantungan atau
Seperti yang anda boleh lihat daripada gambar, kami mempunyai panggilan balik kitaran hayat yang dipanggil. Ini adalah anotasi yang akan memberitahu bekas CDI untuk memanggil kaedah tertentu pada peringkat tertentu dalam kitaran hayat kacang. Sebagai contoh:
@PostConstruct
public void init() {
	System.out.println("Inited");
}
Kaedah ini akan dipanggil apabila biji CDI dibuat seketika oleh bekas. Perkara yang sama akan berlaku dengan @PreDestroy apabila kacang dimusnahkan apabila ia tidak lagi diperlukan. Bukan tanpa alasan bahawa akronim CDI mengandungi huruf C - Konteks. Kacang dalam CDI adalah kontekstual, bermakna kitaran hayatnya bergantung pada konteks di mana ia wujud dalam bekas CDI. Untuk memahami perkara ini dengan lebih baik, anda harus membaca bahagian spesifikasi " 7. Kitaran hayat kejadian kontekstual ". Perlu juga diketahui bahawa bekas itu sendiri mempunyai kitaran hayat, yang boleh anda baca dalam " Peristiwa kitaran hayat bekas ".
Lawatan singkat ke suntikan pergantungan atau

Jumlah

Di atas kami melihat di hujung gunung ais yang dipanggil CDI. CDI adalah sebahagian daripada spesifikasi JEE dan digunakan dalam persekitaran JavaEE. Mereka yang menggunakan Spring tidak menggunakan CDI, tetapi DI, iaitu, ini adalah spesifikasi yang sedikit berbeza. Tetapi mengetahui dan memahami perkara di atas, anda boleh mengubah fikiran anda dengan mudah. Memandangkan Spring menyokong anotasi daripada dunia CDI (Inject yang sama). Bahan tambahan: #Viacheslav
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION