Bevezetés a Java CDI-be (Contexts and Dependency Injection)

1. Áttekintés

A CDI (Contexts and Dependency Injection) egy standard függőség-injektáló keretrendszer, amely a Java EE 6 és újabb verzióiban található.

Lehetővé teszi számunkra, hogy tartományspecifikus életciklus-kontextusokon keresztül kezeljük az állapotfájlok összetevőinek életciklusát, és alkatrészeket (szolgáltatásokat) típusbiztonsággal fecskendezzünk az ügyfélobjektumokba.

Ebben az oktatóanyagban alaposan megvizsgáljuk a CDI legfontosabb szolgáltatásait, és különböző megközelítéseket alkalmazunk a függőségek injektálására az ügyfélosztályokban.

2. DYDI (csináld magad függőségi injekció)

Dióhéjban lehetséges a DI megvalósítása anélkül, hogy bármilyen keretet igénybe vennénk.

Ezt a megközelítést közismert nevén DYDI (Do-it-Yourself függőségi injekció).

A DYDI használatával az alkalmazáskódot elkülönítjük az objektum létrehozásától, azáltal, hogy a szükséges függőségeket sima régi gyárakon / építőkön keresztül juttatjuk el az ügyfélosztályokhoz.

Így nézhet ki egy alapvető DYDI megvalósítás:

nyilvános felület TextService {String doSomethingWithText (String text); String doSomethingElseWithText (Karakterlánc szöveg); }
public class SpecializedTextService megvalósítja a TextService {...}
public class TextClass {private TextService textService; // konstruktor}
public class TextClassFactory {public TextClass getTextClass () {return new TextClass (new SpecializedTextService ();}}

Természetesen a DYDI alkalmas néhány viszonylag egyszerű használati esetre.

Ha a mintaalkalmazásunk mérete és komplexitása növekedne, összekapcsolt objektumok nagyobb hálózatának megvalósításával, rengeteg objektumgrafikon-gyárral szennyeznénk.

Ehhez sok kazánlap kódra lenne szükség csak az objektumgrafikonok létrehozásához. Ez nem teljesen méretezhető megoldás.

Csinálhatunk jobban DI-t? Természetesen megtehetjük. Pontosan itt jelenik meg a képen a CDI.

3. Egyszerű példa

A CDI a DI-t egyszerűsített folyamatgá változtatja, amelynek fő célja az, hogy csak néhány egyszerű feljegyzéssel díszítse a szolgáltatási osztályokat, és meghatározza a kliensosztályok megfelelő injekciós pontjait.

Annak bemutatásához, hogy a CDI hogyan hajtja végre a DI-t a legalapvetőbb szinten, tegyük fel, hogy egy egyszerű képfájl-szerkesztő alkalmazást szeretnénk kifejleszteni. Képes megnyitni, szerkeszteni, írni, képfájlt menteni és így tovább.

3.1. A „Beans.xml” File

Először a „Beans.xml” fájl a „Src / main / resources / META-INF /” mappába. Még akkor is, ha ez a fájl egyáltalán nem tartalmaz konkrét DI irányelveket, a CDI működéséhez és működéséhez szükséges:

3.2. A szolgáltatási osztályok

Ezután hozzuk létre azokat a szolgáltatási osztályokat, amelyek a fent említett fájlokat GIF, JPG és PNG fájlokon hajtják végre:

nyilvános felület ImageFileEditor {String openFile (String fájlnév); Karakterlánc editFile (String fájlnév); String writeFile (String fájlnév); String saveFile (String fájlnév); }
public class GifFileEditor implementálja az ImageFileEditor {@Orride public String openFile (String fileName) {return "Opening GIF file" + fileName; } @Orride public String editFile (String fileName) {return "GIF fájl szerkesztése" + fileName; } @Orride public String writeFile (String fileName) {return "GIF fájl írása" + fájlnév; } @Orride public String saveFile (String fileName) {return "GIF fájl mentése" + fileName; }}
public class A JpgFileEditor megvalósítja az ImageFileEditor {// JPG-specifikus megvalósításokat az openFile () / editFile () / writeFile () / saveFile () ...}
public class A PngFileEditor megvalósítja az ImageFileEditor {// PNG-specifikus megvalósításokat az openFile () / editFile () / writeFile () / saveFile () ...}

3.3. Az ügyfélosztály

Végül valósítsunk meg egy kliens osztályt, amely egy ImageFileEditor megvalósítás a konstruktorban, és definiáljuk az injekciós pontot a @ Injekció kommentár:

public class ImageFileProcessor {private ImageFileEditor imageFileEditor; @ Inject public ImageFileProcessor (ImageFileEditor imageFileEditor) {this.imageFileEditor = imageFileEditor; }}

Egyszerűen fogalmazva: @ Injekció az annotáció a CDI tényleges munkalova. Ez lehetővé teszi számunkra, hogy meghatározzuk az injekciós pontokat az ügyfélosztályokban.

Ebben az esetben, @ Injekció utasítja a CDI-t egy injekció beadására ImageFileEditor megvalósítás a konstruktorban.

Továbbá, a szolgáltatás használatával is be lehet injekciózni egy szolgáltatást @ Injekció annotáció a mezőkben (terepi injektálás) és a seterek (szetter injektálás). Ezeket a lehetőségeket később megvizsgáljuk.

3.4. A ImageFileProcessor Objektum grafikon hegesztéssel

Természetesen meg kell győződnünk arról, hogy a CDI beadja-e a jogot ImageFileEditor végrehajtás a ImageFileProcessor osztály kivitelezője.

Ehhez először meg kell szereznünk egy példányt az osztályból.

Mivel nem támaszkodunk egyetlen Java EE alkalmazáskiszolgálóra sem a CDI használatához, ezt a Weld-mel, a Java SE CDI referencia-megvalósításával fogjuk megtenni:

public static void main (String [] args) {Hegesztési varrat = új Hegesztés (); WeldContainer tartály = varrat.initialize (); ImageFileProcessor imageFileProcessor = container.select (ImageFileProcessor.class) .get (); System.out.println (imageFileProcessor.openFile ("file1.png")); tároló.leállítás (); } 

Itt készítünk egy WeldContainer objektumot, majd kap egy ImageFileProcessor objektumot, és végül annak hívja fájl megnyitása() módszer.

Ahogy az várható volt, ha futtatjuk az alkalmazást, a CDI hangosan panaszkodik azáltal, hogy a-t dob DeploymentException:

Kielégítetlen függőségek az ImageFileEditor típushoz @Default minősítőkkel az injekció beadási pontjában ...

Azért kapjuk ezt a kivételt, mert a CDI nem tudja mit ImageFileEditor végrehajtás beadni a ImageFileProcessor konstruktőr.

A CDI terminológiájában, ez kétértelmű injekciós kivételként ismert.

3.5. A @ Default és @Alternatív Megjegyzések

E kétértelműség megoldása egyszerű. A CDI alapértelmezés szerint feljegyzi az interfész összes megvalósítását a @ Default annotáció.

Tehát kifejezetten meg kell mondanunk neki, hogy melyik implementációt kell beadni az ügyfélosztályba:

@Alternative public class GifFileEditor implementálja az ImageFileEditor {...}
@Alternative public class JpgFileEditor megvalósítja az ImageFileEditor {...} 
public class PngFileEditor megvalósítja az ImageFileEditor {...}

Ebben az esetben jegyzeteltük GifFileEditor és JpgFileEditor a ... val @Alternatív annotációval, tehát a CDI ezt már tudja PngFileEditor (alapértelmezés szerint a @ Default annotáció) az a megvalósítás, amelyet be akarunk injektálni.

Ha újra futtatjuk az alkalmazást, akkor ez a várakozásoknak megfelelően fog végrehajtódni:

PNG fájl megnyitása1.png 

Továbbá annotálás PngFileEditor a ... val @ Default annotálás és a többi megvalósítás alternatívaként való megtartása ugyanazt a fenti eredményt eredményezi.

Ez dióhéjban azt mutatja, hogyan tudjuk nagyon egyszerűen felcserélni a megvalósítások futásidejű injektálását a @Alternatív annotációk a szolgáltatási osztályokban.

4. Terepi injekció

A CDI támogatja a mezőn és a szetteren kívüli injekciót is.

Így végezheti el a terepi injekciót (a szolgáltatások minősítésének szabályai @ Default és @Alternatív a kommentárok változatlanok maradnak):

@ Inject private final ImageFileEditor imageFileEditor;

5. Szetter injekció

Hasonlóképpen, a következőképpen kell elvégezni a szetter injekciót:

@ Inject public void setImageFileEditor (ImageFileEditor imageFileEditor) {...}

6. A @Nevezett Megjegyzés

Eddig megtanultuk, hogyan kell meghatározni az injekciós pontokat az ügyfélosztályokban, és a szolgáltatásokat injektálni a @ Injekció, @ Default , és @Alternatív annotációk, amelyek a legtöbb használati esetet lefedik.

Mindazonáltal a CDI lehetővé teszi számunkra a szolgáltatás injekciójának végrehajtását is a @Nevezett annotáció.

Ez a módszer a szolgáltatások injektálásának szemantikusabb módját nyújtja, értelmes nevet kötve a megvalósításhoz:

@Named ("GifFileEditor") nyilvános osztály GifFileEditor megvalósítja az ImageFileEditor {...} @Names ("JpgFileEditor") nyilvános osztályt }

Most át kell alakítanunk az injekciós pontot a ImageFileProcessor osztály egy megnevezett megvalósításhoz:

@ Inject public ImageFileProcessor (@Named ("PngFileEditor") ImageFileEditor imageFileEditor) {...}

Szintén lehetséges terepi és szetter injekciót megnevezett megvalósításokkal, ami nagyon hasonlít a @ Default és @Alternatív kommentárok:

@ Inject private final @Named ("PngFileEditor") ImageFileEditor imageFileEditor; @ Inject public void setImageFileEditor (@Named ("PngFileEditor") ImageFileEditor imageFileEditor) {...}

7. Az @Termékek Megjegyzés

Előfordul, hogy egy szolgáltatás megköveteli valamilyen konfiguráció teljes inicializálását, mielőtt injekciót kapna a további függőségek kezelésére.

A CDI ezeken a helyzeteken nyújt támogatást a @Termékek annotáció.

@Termékek lehetővé teszi számunkra a gyári osztályok megvalósítását, amelyek felelőssége a teljesen inicializált szolgáltatások létrehozása.

Megérteni, hogy a @Termékek az annotáció működik, alakítsuk át a ImageFileProcessor osztály, így további kell TimeLogger szolgáltatás a kivitelezőben.

A szolgáltatást arra a naplózásra használják, amikor egy bizonyos képfájl-műveletet végrehajtanak:

@ Inject public ImageFileProcessor (ImageFileEditor imageFileEditor, TimeLogger timeLogger) {...} public String openFile (String fileName) {return imageFileEditor.openFile (fileName) + "at:" + timeLogger.getTime (); } // további képfájl-módszerek 

Ebben az esetben a TimeLogger osztály két további szolgáltatást vesz igénybe, SimpleDateFormat és Naptár:

public class TimeLogger {private SimpleDateFormat dateFormat; privát Naptár-naptár; // konstruktorok public String getTime () {return dateFormat.format (calendar.getTime ()); }}

Hogyan mondhatjuk meg a CDI-nek, hová kell néznie a teljes inicializáláshoz TimeLogger tárgy?

Csak létrehozunk egy TimeLogger gyári osztály és jegyezze fel gyári módszerét a @Termékek kommentár:

public class TimeLoggerFactory {@Products public TimeLogger getTimeLogger () {return new TimeLogger (new SimpleDateFormat ("HH: mm"), Calendar.getInstance ()); }}

Amikor kapunk egy ImageFileProcessor Például a CDI beolvassa a TimeLoggerFactory osztály, majd hívja a getTimeLogger () metódus (mivel a @Termékek annotáció), és végül beadja a Időnaplózó szolgáltatás.

Ha a refrakcionált minta alkalmazást a Hegeszteni, a következőket adja ki:

A PNG fájl megnyitása :1.png: 17:46

8. Egyéni minősítők

A CDI támogatja az egyéni minősítők használatát a függőségek minősítésére és a kétértelmű injekciós pontok megoldására.

Az egyéni selejtezők nagyon hatékony funkciók. Nemcsak szemantikus nevet kötnek egy szolgáltatáshoz, hanem injekciós metaadatokat is. Metaadatok, például a RetentionPolicy és a jogi kommentár célok (ElementType).

Nézzük meg, hogyan használhatjuk az egyéni minősítőket alkalmazásunkban:

@Qualifier @Retention (RetentionPolicy.RUNTIME) @Target ({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER}) public @interface GifFileEditorQualifier {} 
@Qualifier @Retention (RetentionPolicy.RUNTIME) @Target ({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER}) public @interface JpgFileEditorQualifier {} 
@Qualifier @Retention (RetentionPolicy.RUNTIME) @Target ({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER}) public @interface PngFileEditorQualifier {} 

Most kössük az egyéni minősítőket a ImageFileEditor megvalósítások:

@GifFileEditorQualifier nyilvános osztály A GifFileEditor megvalósítja az ImageFileEditor {...} 
@JpgFileEditorQualifier nyilvános osztály A JpgFileEditor megvalósítja az ImageFileEditor {...}
@PngFileEditorQualifier nyilvános osztály A PngFileEditor megvalósítja az ImageFileEditor {...} 

Végül alakítsuk át az injekciós pontot a ImageFileProcessor osztály:

@ Inject public ImageFileProcessor (@PngFileEditorQualifier ImageFileEditor imageFileEditor, TimeLogger timeLogger) {...} 

Ha még egyszer futtatjuk az alkalmazásunkat, akkor a fent bemutatott kimenetet kell generálnia.

Az egyéni minősítők tiszta szemantikai megközelítést kínálnak a nevek és a kommentárok metaadatainak a megvalósításokhoz történő kötéséhez.

Továbbá, az egyedi minősítők lehetővé teszik számunkra, hogy korlátozóbb, típusbiztonsági befecskendezési pontokat határozzunk meg (felülmúlva a @Default és @Alternative kommentárok funkcionalitását).

Ha a típushierarchiában csak egy altípus minősül, akkor a CDI csak az altípust fogja beadni, az alaptípust nem.

9. Következtetés

Megkérdőjelezhetetlenül, A CDI a függőségi injekciót nem veszi észre, az extra annotációk költsége nagyon kevés erőfeszítést jelent a szervezett függőség-injektálás érdekében.

Van, amikor a DYDI-nek még mindig helye van a CDI-vel szemben. Mint például meglehetősen egyszerű alkalmazások fejlesztésekor, amelyek csak egyszerű objektumgrafikonokat tartalmaznak.

Mint mindig, a cikkben bemutatott összes kódminta elérhető a GitHubon.


$config[zx-auto] not found$config[zx-overlay] not found