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.