JavaRush /Blog Jawa /Random-JV /Wisata singkat menyang injeksi dependensi utawa "Apa mane...
Viacheslav
tingkat

Wisata singkat menyang injeksi dependensi utawa "Apa maneh CDI?"

Diterbitake ing grup
Yayasan sing saiki dibangun kerangka kerja sing paling populer yaiku injeksi dependensi. Aku suggest dipikir apa specification CDI ngandika bab iki, apa Kapabilitas dhasar kita duwe lan carane kita bisa nggunakake.
A dolan singkat menyang injeksi katergantungan utawa

Pambuka

Aku pengin nyedhiyakake review singkat iki kanggo CDI. Apa iki? CDI singkatan saka Contexts and Dependency Injection. Iki minangka spesifikasi Java EE sing nggambarake Injeksi Ketergantungan lan konteks. Kanggo informasi, sampeyan bisa ndeleng ing website http://cdi-spec.org . Wiwit CDI minangka spesifikasi (gambaran babagan cara kerjane, sakumpulan antarmuka), kita uga butuh implementasine kanggo nggunakake. Salah sawijining implementasine yaiku Weld - http://weld.cdi-spec.org/ Kanggo ngatur dependensi lan nggawe proyek, kita bakal nggunakake Maven - https://maven.apache.org Dadi, kita wis nginstal Maven, saiki kita bakal ngerti ing laku , supaya dadi ora ngerti abstrak. Kanggo nindakake iki, kita bakal nggawe proyek nggunakake Maven. Ayo mbukak baris printah (ing Windows, sampeyan bisa nggunakake Win + R kanggo mbukak jendhela "Run" lan nglakokaké cmd) lan takon Maven kanggo nindakake kabeh kanggo kita. Kanggo iki, Maven duwe konsep sing diarani archetype: Maven Archetype .
A dolan singkat menyang injeksi katergantungan utawa
Sawisé iku, ing pitakonan " Pilih nomer utawa aplikasi Filter " lan " Pilih org.apache.maven.archetypes: maven-archetype-quickstart version " mung pencet Ketik. Sabanjure, ketik pengenal proyek, sing diarani GAV (pirsani Naming Convention Guide ).
A dolan singkat menyang injeksi katergantungan utawa
Sawise nggawe proyek sukses, kita bakal weruh tulisan "BUILD SUCCESS". Saiki kita bisa mbukak proyek kita ing IDE favorit kita.

Nambahake CDI menyang Proyek

Ing introduksi, kita weruh yen CDI nduweni situs web sing menarik - http://www.cdi-spec.org/ . Ana bagean download, sing ngemot tabel sing ngemot data sing dibutuhake:
A dolan singkat menyang injeksi katergantungan utawa
Kene kita bisa ndeleng carane Maven njlèntrèhaké kasunyatan sing kita nggunakake API CDI ing project. API minangka antarmuka pemrograman aplikasi, yaiku sawetara antarmuka pemrograman. Kita nggarap antarmuka tanpa kuwatir babagan apa lan cara kerjane ing mburi antarmuka iki. API minangka arsip jar sing bakal kita gunakake ing proyek kita, yaiku, proyek kita wiwit gumantung ing jar iki. Mulane, API CDI kanggo proyek kita minangka ketergantungan. Ing Maven, sawijining proyek diterangake ing file POM.xml ( POM - Model Obyek Proyek ). Dependensi diterangake ing blok dependensi, sing kudu ditambahake entri anyar:
<dependency>
	<groupId>javax.enterprise</groupId>
	<artifactId>cdi-api</artifactId>
	<version>2.0</version>
</dependency>
Minangka sampeyan bisa uga wis ngeweruhi, kita ora nemtokake orane katrangan karo nilai kasedhiya. Apa sebabe ana bedane? Ruang lingkup iki tegese ana wong sing bakal menehi ketergantungan. Nalika aplikasi mbukak ing server Java EE, iku tegese server bakal nyedhiyani aplikasi karo kabeh teknologi JEE perlu. Kanggo gamblang review iki, kita bakal bisa ing lingkungan Java SE, mulane ora ana sing bakal nyedhiyani kita karo dependensi iki. Sampeyan bisa maca liyane babagan Dependency Scope kene: " Dependency Scope ". Oke, saiki kita duwe kemampuan kanggo nggarap antarmuka. Nanging kita uga butuh implementasine. Nalika kita elinga, kita bakal nggunakake Weld. Iku menarik sing beda dependensi diwenehi nang endi wae. Nanging kita bakal tindakake dokumentasi. Mulane, ayo maca " 18.4.5. Nyetel Classpath " lan tindakake kaya sing diucapake:
<dependency>
	<groupId>org.jboss.weld.se</groupId>
	<artifactId>weld-se-core</artifactId>
	<version>3.0.5.Final</version>
</dependency>
Penting yen versi baris katelu saka Weld ndhukung CDI 2.0. Mulane, kita bisa ngandelake API versi iki. Saiki kita siap nulis kode.
A dolan singkat menyang injeksi katergantungan utawa

Initializing wadhah CDI

CDI minangka mekanisme. Wong kudu ngontrol mekanisme iki. Kaya sing wis diwaca ing ndhuwur, manajer kasebut minangka wadhah. Mulane, kita kudu nggawe; iku dhewe ora bakal katon ing lingkungan SE. Ayo ditambahake ing ngisor iki menyang metode utama:
public static void main(String[] args) {
	SeContainerInitializer initializer = SeContainerInitializer.newInstance();
	initializer.addPackages(App.class.getPackage());
	SeContainer container = initializer.initialize();
}
Kita nggawe wadhah CDI kanthi manual amarga ... Kita kerja ing lingkungan SE. Ing proyek pertempuran khas, kode kasebut mlaku ing server, sing nyedhiyakake macem-macem teknologi kanggo kode kasebut. Mulane, yen server nyedhiyakake CDI, iki tegese server wis duwe wadhah CDI lan kita ora perlu nambah apa-apa. Nanging kanggo tujuan tutorial iki, kita bakal njupuk lingkungan SE. Kajaba iku, wadhah kasebut ana ing kene, kanthi jelas lan dingerteni. Napa kita butuh wadhah? Wadah ing njero ngemot kacang buncis (buncis CDI).
A dolan singkat menyang injeksi katergantungan utawa

Kacang CDI

Dadi, kacang buncis. Apa CDI bin? Iki kelas Jawa sing nderek sawetara aturan. Aturan kasebut diterangake ing spesifikasi, ing bab " 2.2. Apa jenis kelas kacang? ". Ayo nambah kacang CDI menyang paket sing padha karo kelas App:
public class Logger {
    public void print(String message) {
        System.out.println(message);
    }
}
Saiki kita bisa nelpon kacang iki saka maincara kita:
Logger logger = container.select(Logger.class).get();
logger.print("Hello, World!");
Kaya sing sampeyan ngerteni, kita ora nggawe kacang buncis nggunakake tembung kunci anyar. We takon wadhah CDI: "wadhah CDI. Aku pancene butuh conto saka kelas Logger, please menehi kula." Cara iki diarani " Lookup dependensi ", yaiku, nggoleki dependensi. Saiki ayo nggawe kelas anyar:
public class DateSource {
    public String getDate() {
        return new Date().toString();
    }
}
Kelas primitif sing ngasilake perwakilan teks tanggal. Ayo saiki nambah output tanggal menyang pesen:
public class Logger {
    @Inject
    private DateSource dateSource;

    public void print(String message) {
        System.out.println(dateSource.getDate() + " : " + message);
    }
}
Anotasi @Inject sing menarik wis muncul. Kaya sing kasebut ing bab " 4.1. Titik injeksi "dokumentasi las cdi, nggunakake anotasi iki kita nemtokake Titik Injeksi. Ing basa Rusia, iki bisa diwaca minangka "titik implementasi". Iki digunakake dening wadhah CDI kanggo nyuntikake dependensi nalika instantiating kacang. Kaya sing sampeyan ngerteni, kita ora menehi nilai menyang lapangan dateSource. Alesan kanggo iki yaiku kasunyatan manawa wadhah CDI ngidini ing jero kacang CDI (mung kacang buncis sing digawe dhewe, yaiku sing bisa digunakake) nggunakake " Injeksi Ketergantungan ". Iki minangka cara liya saka Inversion of Control , pendekatan ing ngendi ketergantungan dikontrol dening wong liya tinimbang kita kanthi jelas nggawe obyek kasebut. Injeksi dependensi bisa ditindakake liwat metode, konstruktor, utawa lapangan. Kanggo rincian liyane, waca bab spesifikasi CDI " 5.5. Injeksi dependensi ". Prosedur kanggo nemtokake apa sing kudu dileksanakake diarani resolusi typesafe, yaiku sing kudu dirembug.
A dolan singkat menyang injeksi katergantungan utawa

Résolusi jeneng utawa Résolusi Typesafe

Biasane, antarmuka digunakake minangka jinis obyek sing bakal dileksanakake, lan wadhah CDI dhewe nemtokake implementasine sing kudu dipilih. Iki migunani kanggo akeh alasan, sing bakal kita bahas. Dadi kita duwe antarmuka logger:
public interface Logger {
    void print(String message);
}
Ngandika yen kita duwe sawetara logger, kita bisa ngirim pesen lan bakal ngrampungake tugas - log. Carane lan ing ngendi ora bakal dadi kapentingan ing kasus iki. Ayo saiki nggawe implementasine kanggo logger:
public class SystemOutLogger implements Logger {
    @Inject
    private DateSource dateSource;

    public void print(String message) {
        System.out.println(message);
    }
}
Nalika sampeyan bisa ndeleng, iki logger sing nulis kanggo System.out. Apik banget. Saiki, cara utama kita bakal bisa digunakake kaya sadurunge. Logger logger = container.select(Logger.class).get(); Baris iki isih bakal ditampa dening logger. Lan kaendahan iku kita mung kudu ngerti antarmuka, lan wadhah CDI wis mikir babagan implementasine kanggo kita. Ayo kita duwe implementasi kapindho sing ngirim log menyang papan sing adoh:
public class NetworkLogger implements Logger {
    @Override
    public void print(String message) {
        System.out.println("Send log message to remote log system");
    }
}
Yen saiki kita mbukak kode tanpa owah-owahan, kita bakal entuk kesalahan, amarga Wadhah CDI ndeleng rong implementasi antarmuka lan ora bisa milih ing antarane: org.jboss.weld.exceptions.AmbiguousResolutionException: WELD-001335: Ambiguous dependencies for type Logger Apa sing kudu ditindakake? Ana sawetara variasi sing kasedhiya. Sing paling gampang yaiku anotasi @Vetoed kanggo kacang CDI supaya wadhah CDI ora nganggep kelas iki minangka kacang CDI. Nanging ana pendekatan sing luwih menarik. A kacang buncis CDI bisa ditandhani minangka "alternatif" nggunakake anotasi @Alternativediterangake ing " 4.7. Alternatif " bab dokumentasi Weld CDI. Iki artine apa? Iki tegese kajaba kita tegas ngandika nggunakake, iku ora bakal dipilih. Iki minangka versi alternatif saka kacang buncis. Ayo menehi tandha kacang NetworkLogger minangka @Alternative lan kita bisa ndeleng manawa kode kasebut dieksekusi maneh lan digunakake dening SystemOutLogger. Kanggo ngaktifake alternatif, kita kudu duwe file beans.xml . Pitakonan bisa uga muncul: " beans.xml, ing ngendi aku nyelehake sampeyan? " Mulane, ayo nyelehake file kanthi bener:
A dolan singkat menyang injeksi katergantungan utawa
Sanalika kita duwe file iki, artefak karo kode kita bakal disebut " Arsip kacang eksplisit ". Saiki kita duwe 2 konfigurasi sing kapisah: piranti lunak lan xml. Masalahe yaiku bakal mbukak data sing padha. Contone, definisi kacang DataSource bakal dimuat kaping 2 lan program kita bakal nabrak nalika dieksekusi, amarga Wadah CDI bakal nganggep minangka 2 kacang sing kapisah (sanajan nyatane padha kelas, sing wadhah CDI sinau babagan kaping pindho). Kanggo nyegah iki ana 2 opsi:
  • mbusak baris initializer.addPackages(App.class.getPackage())lan tambahake indikasi alternatif kanggo file 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>
  • tambahake atribut bean-discovery-modekanthi nilai " ora ana " menyang unsur root kacang lan nemtokake alternatif kanthi program:
initializer.addPackages(App.class.getPackage());
initializer.selectAlternatives(NetworkLogger.class);
Mangkono, nggunakake alternatif CDI, wadhah bisa nemtokake kacang sing dipilih. Apike, yen wadhah CDI ngerti sawetara alternatif kanggo antarmuka sing padha, mula bisa dicritakake kanthi nuduhake prioritas nggunakake anotasi @Priority(Wiwit CDI 1.1).
A dolan singkat menyang injeksi katergantungan utawa

Kualifikasi

Kapisah, iku worth ngrembug bab kuwi minangka kualifikasi. Kualifikasi dituduhake kanthi anotasi ing ndhuwur kacang lan nyaring panelusuran kanggo kacang. Lan saiki rincian liyane. Apike, sembarang kacang CDI ing kasus apa wae nduweni paling ora siji kualifikasi - @Any. Yen kita ora nemtokake kualifikasi ANY ndhuwur kacang, nanging wadhah CDI dhewe nambah @Anykualifikasi liyane kanggo kualifikasi - @Default. Yen kita nemtokake apa wae (contone, kanthi tegas nemtokake @Any), banjur kualifikasi @Default ora bakal ditambahake kanthi otomatis. Nanging kaendahane kualifikasi yaiku sampeyan bisa nggawe kualifikasi dhewe. Kualifikasi meh ora beda karo anotasi, amarga intine, iki mung anotasi sing ditulis kanthi cara khusus. Contone, sampeyan bisa ngetik Enum kanggo jinis protokol:
public enum ProtocolType {
    HTTP, HTTPS
}
Sabanjure kita bisa nggawe kualifikasi sing bakal nimbang jinis iki:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Protocol {
    ProtocolType value();
    @Nonbinding String comment() default "";
}
Wigati dicathet yen lapangan sing ditandhani minangka @Nonbindingora mengaruhi penentuan kualifikasi. Saiki sampeyan kudu nemtokake kualifikasi. Dituduhake ing ndhuwur jinis kacang (supaya CDI ngerti carane nemtokake) lan ing ndhuwur Titik Injeksi (kanthi anotasi @Inject, supaya sampeyan ngerti kacang endi sing kudu digoleki kanggo injeksi ing papan iki). Contone, kita bisa nambah sawetara kelas karo kualifikasi. Kanggo gamblang, kanggo artikel iki kita bakal nindakake ing 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");
	}
}
Banjur nalika nindakake Inject, kita bakal nemtokake kualifikasi sing bakal mengaruhi kelas sing bakal digunakake:
@Inject
@Protocol(ProtocolType.HTTPS)
private Sender sender;
Hebat, ta?) Katon ayu, nanging ora jelas apa sebabe. Saiki bayangake ing ngisor iki:
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);
Kanthi cara iki, kita bisa ngalahake entuk nilai supaya bisa diitung kanthi dinamis. Contone, bisa dijupuk saka sawetara setelan. Banjur kita bisa ngganti implementasine sanajan ing fly, tanpa recompiling utawa miwiti maneh program / server. Iku dadi luwih menarik, ta? )
A dolan singkat menyang injeksi katergantungan utawa

Produsèn

Fitur liyane sing migunani saka CDI yaiku produser. Iki minangka cara khusus (ditandani karo anotasi khusus) sing diarani nalika sawetara kacang njaluk injeksi dependensi. Rincian liyane diterangake ing dokumentasi, ing bagean " 2.2.3. Cara produser ". Conto paling prasaja:
@Produces
public Integer getRandomNumber() {
	return new Random().nextInt(100);
}
Saiki, nalika nyuntikake menyang lapangan jinis Integer, metode iki bakal diarani lan entuk nilai. Ing kene kita kudu langsung ngerti yen nalika ndeleng tembung kunci anyar, kita kudu langsung ngerti yen iki dudu kacang CDI. Yaiku, conto kelas Random ora bakal dadi kacang CDI mung amarga asale saka sing ngontrol wadhah CDI (ing kasus iki, produser).
A dolan singkat menyang injeksi katergantungan utawa

Interceptors

Interceptors minangka interceptor sing "ngganggu" ing karya. Ing CDI iki rampung cukup cetha. Ayo ndeleng carane kita bisa nindakake logging nggunakake juru (utawa interceptors). Kaping pisanan, kita kudu njlèntrèhaké ikatan menyang interceptor. Kaya akeh perkara, iki ditindakake kanthi nggunakake anotasi:
@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface ConsoleLog {
}
Wangsulan: Bab ingkang utama punika punika naleni kanggo interceptor ( @InterceptorBinding), kang bakal dipun warisaken dening ngluwihi ( @InterceptorBinding). Saiki ayo nulis interceptor dhewe:
@Interceptor
@ConsoleLog
public class LogInterceptor {
    @AroundInvoke
    public Object log(InvocationContext ic) throws Exception {
        System.out.println("Invocation method: " + ic.getMethod().getName());
        return ic.proceed();
    }
}
Sampeyan bisa maca liyane babagan carane interceptors ditulis ing conto saka specification: " 1.3.6. Conto Interceptor ". Inggih, kita mung kudu nguripake inerceptor. Kanggo nindakake iki, nemtokake anotasi ikatan ing ndhuwur metode sing ditindakake:
@ConsoleLog
public void print(String message) {
Lan saiki rincian liyane sing penting banget. Interceptor dipateni kanthi standar lan kudu diaktifake kanthi cara sing padha karo alternatif. Contone, ing file beans.xml :
<interceptors>
	<class>ru.javarush.LogInterceptor</class>
</interceptors>
Nalika sampeyan bisa ndeleng, iku cukup prasaja.
A dolan singkat menyang injeksi katergantungan utawa

Acara & Pengamat

CDI uga nyedhiyakake model acara lan pengamat. Ing kene kabeh ora ketok kaya interceptor. Dadi, Acara ing kasus iki bisa dadi kelas apa wae, ora ana sing khusus kanggo deskripsi kasebut. Tuladhane:
public class LogEvent {
    Date date = new Date();
    public String getDate() {
        return date.toString();
    }
}
Saiki wong kudu ngenteni acara kasebut:
public class LogEventListener {
    public void logEvent(@Observes LogEvent event){
        System.out.println("Message Date: " + event.getDate());
    }
}
Sing utama ing kene yaiku nemtokake anotasi @Observes, sing nuduhake yen iki ora mung cara, nanging cara sing kudu diarani minangka asil observasi acara saka jinis LogEvent. Saiki, kita butuh wong sing bakal nonton:
public class LogObserver {
    @Inject
    private Event<LogEvent> event;
    public void observe(LogEvent logEvent) {
        event.fire(logEvent);
    }
}
Kita duwe cara siji sing bakal ngandhani wadhah manawa ana acara Acara kanggo jinis acara LogEvent. Saiki sing isih ana yaiku nggunakake pengamat. Contone, ing NetworkLogger kita bisa nambah injeksi pengamat kita:
@Inject
private LogObserver observer;
Lan ing cara cetak, kita bisa menehi kabar marang pengamat manawa ana acara anyar:
public void print(String message) {
	observer.observe(new LogEvent());
Iku penting kanggo ngerti sing acara bisa diproses ing siji thread utawa ing sawetara. Kanggo pangolahan asinkron, gunakake metode .fireAsync(tinimbang .fire) lan anotasi @ObservesAsync(tinimbang @Observes). Contone, yen kabeh acara dieksekusi ing macem-macem utas, banjur yen 1 utas mbuwang Exception, liyane bakal bisa nindakake pakaryan kanggo acara liyane. Sampeyan bisa maca liyane babagan acara ing CDI, minangka biasanipun, ing specification, ing bab " 10. Events ".
A dolan singkat menyang injeksi katergantungan utawa

Dekorator

Kaya sing kita deleng ing ndhuwur, macem-macem pola desain diklumpukake ing sangisore swiwi CDI. Lan iki siji liyane - dekorator. Iki minangka perkara sing menarik banget. Ayo ndeleng kelas iki:
@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);
    }
}
Kanthi nyatakake dekorator, kita ujar manawa ana implementasine Logger digunakake, "add-on" iki bakal digunakake, sing ngerti implementasine nyata, sing disimpen ing lapangan delegasi (amarga ditandhani karo anotasi @Delegate). Dekorator mung bisa digandhengake karo kacang CDI, sing ora dadi interceptor utawa dekorator. Conto uga bisa dideleng ing spesifikasi: " 1.3.7. Conto dekorator ". Dekorator, kaya interceptor, kudu diuripake. Contone, ing beans.xml :
<decorators>
	<class>ru.javarush.LoggerDecorator</class>
</decorators>
Kanggo rincian liyane, ndeleng referensi weld: " Bab 10. Decorators ".

Siklus urip

Kacang buncis duwe siklus urip dhewe. Iku katon kaya iki:
A dolan singkat menyang injeksi katergantungan utawa
Kaya sing sampeyan ngerteni saka gambar kasebut, kita duwe panggilan balik siklus urip. Iki minangka anotasi sing bakal ngandhani wadhah CDI kanggo nelpon cara tartamtu ing tahap tartamtu ing siklus urip kacang. Tuladhane:
@PostConstruct
public void init() {
	System.out.println("Inited");
}
Cara iki bakal diarani nalika buncis CDI instantiated dening wadhah. Sing padha bakal kelakon karo @PreDestroy nalika buncis dirusak nalika ora dibutuhake maneh. Iku ora kanggo apa-apa sing CDI akronim ngemot huruf C - Konteks. Kacang buncis ing CDI iku kontekstual, tegese siklus uripe gumantung saka konteks sing ana ing wadhah CDI. Kanggo luwih ngerti babagan iki, sampeyan kudu maca bagean spesifikasi " 7. Siklus urip saka conto kontekstual ". Sampeyan uga kudu ngerti manawa wadhah kasebut duwe siklus urip, sing bisa diwaca ing " Acara siklus urip Wadhah ".
A dolan singkat menyang injeksi katergantungan utawa

Total

Ndhuwur kita ndeleng pucuk gunung es sing diarani CDI. CDI minangka bagéan saka spesifikasi JEE lan digunakake ing lingkungan JavaEE. Sing nggunakake Spring ora nggunakake CDI, nanging DI, sing, iki specifications rada beda. Nanging ngerti lan ngerti ing ndhuwur, sampeyan bisa kanthi gampang ngganti pikiran. Ngelingi yen Spring ndhukung anotasi saka jagad CDI (sing padha Inject). Bahan tambahan: #Viacheslav
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION