מדות מול אביב - הזרקת תלות

1. הקדמה

Google Guice ו אביב הן שתי מסגרות חזקות המשמשות להזרקת תלות. שתי המסגרות מכסות את כל הרעיונות של הזרקת תלות, אך לכל אחת מהן הדרך שלה ליישם אותן.

במדריך זה נדון במה שמסגרות Guice ו- Spring נבדלות בתצורה ובהטמעה.

2. תלות Maven

נתחיל בהוספה של התלות Guice ו- Spring Maven לתוכנו pom.xml קוֹבֶץ:

 org.springframework spring-context 5.1.4.RELEASE com.google.inject guice 4.2.2 

אנחנו תמיד יכולים לגשת לחדשות האחרונות אביב-הקשר אוֹ רִיחָה תלות ממייבן סנטרל.

3. תצורת הזרקת תלות

הזרקת תלות היא טכניקת תכנות בה אנו משתמשים כדי להפוך את השיעורים שלנו ללא תלות בתלות שלהם.

בחלק זה נתייחס למספר מאפייני ליבה הנבדלים בין אביב לגויס בדרכים להגדרת הזרקת תלות.

3.1. חיווט באביב

אביב מכריז על תצורות הזרקת התלות בשיעור תצורה מיוחד. המחלקה הזו חייבת להיות מסומנת על ידי @תְצוּרָה ביאור. מיכל האביב משתמש בכיתה זו כמקור להגדרות שעועית.

קוראים לשיעורים המנוהלים על ידי אביבשעועית קפיץ.

האביב משתמש ב @Autowired ביאור לחיבור התלות באופן אוטומטי. @Autowired הוא חלק מהערות הליבה המובנות של אביב. אנחנו יכולים להשתמש @Autowired על משתני חבר, שיטות קביעה ובונים.

גם האביב תומך @לְהַזרִיק. @לְהַזרִיק הוא חלק מ- Java CDI (Contexts and Dependency Injection) המגדיר תקן להזרקת תלות.

בואו נגיד שאנחנו רוצים לחבר תלות אוטומטית למשתנה חבר. אנחנו יכולים פשוט להוסיף לו הערות @Autowired:

@ UserService בכיתה ציבורית רכיב {@ AccountService accountService פרטי; }
@Component ציבור בכיתה AccountServiceImpl מיישם את AccountService {}

שנית, בואו ניצור מחלקת תצורה שתשמש כמקור לשעועית בזמן טעינת הקשר היישום שלנו:

@Configuration @ComponentScan ("com.baeldung.di.spring") כיתה ציבורית SpringMainConfig {}

שים לב שגם הערנו שירות משתמש ו AccountServiceImpl עם @רְכִיב לרשום אותם כשעועית. זה ה @ComponentScan ביאור שיגיד לאביב לאן לחפש לרכיבים המבוארים.

למרות שהערנו AccountServiceImpl,האביב יכול למפות אותו ל שירות חשבון מאז שהוא מיישם שירות חשבון.

לאחר מכן, עלינו להגדיר הקשר יישומי כדי לגשת לשעועית. נציין כי נתייחס להקשר זה בכל מבחני יחידת האביב שלנו:

ApplicationContext context = AnnotationConfigApplicationContext חדש (SpringMainConfig.class);

כעת בזמן הריצה נוכל לאחזר את ה- אccountService מופע משלנו שירות משתמש אפונה:

UserService userService = context.getBean (UserService.class); assertNotNull (userService.getAccountService ());

3.2. קשירת גויסות

גויס מנהל את התלות שלו בשיעור מיוחד שנקרא מודול. מודול Guice צריך להרחיב את ה- תקציר מודול מעמד ולדרוס את שלו הגדר () שיטה.

Guice משתמש בכריכה כשווה ערך לחיווט באביב. פשוט שים, הכריכות מאפשרות לנו להגדיר כיצד תוספיקו להיות מוזרקים לכיתה. כריכות המדריך מוכרזות במודולים שלנו הגדר () שיטה.

במקום @Autowired, גויס משתמש ב- @לְהַזרִיק ביאור להזרקת התלות.

בואו ניצור דוגמה מקבילה של Guice:

מחלקה ציבורית GuiceUserService {@ הזרקת חשבון AccountService פרטי; }

שנית, ניצור את מחלקת המודולים המהווה מקור להגדרות המחייבות שלנו:

הכיתה הציבורית GuiceModule מרחיבה את AbstractModule {@Override מוגן ריק להגדיר () {bind (AccountService.class) .to (AccountServiceImpl.class); }}

בדרך כלל, אנו מצפים מ- Guice להקים כל אובייקט תלות מהבונים המוגדרים כברירת מחדל שלהם אם אין מחייב המוגדר במפורש הגדר () שיטה. אך מכיוון שלא ניתן להתקין ממשקים באופן ישיר, עלינו להגדיר כריכות לספר לגויס איזה ממשק ישויך לאיזה יישום.

ואז, עלינו להגדיר מַזרֵק באמצעות GuiceModule כדי לקבל מקרים של השיעורים שלנו. רק נציין כי כל מבחני ה- Guice ישתמשו בכך מַזרֵק:

מזרק מזרק = Guice.createInjector (GuiceModule חדש ());

לבסוף, בזמן הריצה אנו מאחזרים א GuiceUserService מופע עם לא null חשבון שירות תלות:

GuiceUserService guiceUserService = injector.getInstance (GuiceUserService.class); assertNotNull (guiceUserService.getAccountService ());

3.3. הערת האביב של @Bean

האביב מספק גם ביאור ברמת השיטה @אפונה לרשום שעועית כחלופה להערות ברמת הכיתה שלה כמו @רְכִיב. ערך ההחזר של a @אפונה שיטת הערה רשומה כשעועית במיכל.

בואו נגיד שיש לנו מופע של BookServiceImpl שאנחנו רוצים להנגיש להזרקה. נוכל להשתמש @אפונה לרשום את המופע שלנו:

@Bean BookService public bookServiceGenerator () {החזר BookServiceImpl חדש (); }

ועכשיו אנחנו יכולים להשיג שירות ספרים אפונה:

BookService bookService = context.getBean (BookService.class); assertNotNull (bookService);

3.4. Guice's @ מספק הערה

כמקבילה לזה של אביב @אפונה ביאור, לגויס יש ביאור מובנה @Provides לעשות את אותה העבודה. כמו @אפונה, @Provides מוחל רק על השיטות.

עכשיו בואו ניישם את דוגמת שעועית האביב הקודמת עם Guice. כל שעלינו לעשות הוא להוסיף את הקוד הבא למחלקת המודולים שלנו:

@Provides BookService publicServiceGenerator () {להחזיר BookServiceImpl חדש (); }

ועכשיו נוכל לאחזר מופע של שירות ספרים:

BookService bookService = injector.getInstance (BookService.class); assertNotNull (bookService);

3.5. סריקת רכיבי Classpath באביב

האביב מספק א @ComponentScan ביאור מזהה ומותאם באופן אוטומטי רכיבי הערה באופן אוטומטי על ידי סריקת חבילות שהוגדרו מראש.

ה @ComponentScan ההערה מספרת לאביב אילו חבילות ייסרקו עבור רכיבים המאושרים. משתמשים בו עם @תְצוּרָה ביאור.

3.6. סריקת רכיבי Classpath במלווה

שלא כמו האביב, לגויס אין תכונה כזו של סריקת רכיבים. אבל לא קשה לדמות את זה. יש כמה תוספים כמו מושל שיכול להכניס את התכונה הזו לגויס.

3.7. הכרת חפצים באביב

האביב מזהה חפצים בשמם. האביב מחזיק את האובייקטים במבנה שהוא בערך כמו מַפָּה. פירוש הדבר שלא יכולים להיות לנו שני אובייקטים בעלי אותו שם.

התנגשות שעועית עקב קיומה של שעועית מרובה באותו שם היא בעיה נפוצה אחת מפתחי אביב פגעו. לדוגמה, בואו ניקח בחשבון את הצהרות השעועית הבאות:

@Configuration @Import ({SpringBeansConfig.class}) @ComponentScan ("com.baeldung.di.spring") מחלקה ציבורית SpringMainConfig {@Bean BookService publicServiceGenerator () {להחזיר BookServiceImpl חדש (); }}
@Configuration מחלקה ציבורית SpringBeansConfig {@Bean Public AudioBookService bookServiceGenerator () {להחזיר AudioBookServiceImpl חדש (); }}

כזכור, כבר הייתה לנו הגדרת שעועית ל שירות ספרים ב SpringMainConfig מעמד.

כדי ליצור כאן התנגשות שעועית, עלינו להכריז על שיטות השעועית באותו שם. אך אסור שיהיו לנו שתי שיטות שונות עם אותו שם בכיתה אחת. מסיבה זו הכרזנו על שירות AudioBookService שעועית בכיתת תצורה אחרת.

עכשיו, בואו נתייחס לשעועית הזו במבחן יחידה:

BookService bookService = context.getBean (BookService.class); assertNotNull (bookService); AudioBookService audioBookService = context.getBean (AudioBookService.class); assertNotNull (audioBookService);

מבחן היחידה ייכשל עם:

org.springframework.beans.factory.NoSuchBeanDefinitionException: אין שעועית מזכה מסוג 'AudioBookService' זמינה

ראשית, אביב רשם את שירות AudioBookService שעועית עם “BookServiceGenerator” שם במפת השעועית שלה. ואז, זה היה צריך לבטל את זה לפי הגדרת שעועית עבור שירות ספרים בשל "אסור להכניס שמות כפולים" טבעו של מפת גיבוב מבנה נתונים.

לבסוף, אנו יכולים להתגבר על נושא זה על ידי ייחודיות לשמות שיטות שעועית או הגדרת ה- שֵׁם תכונה לשם ייחודי לכל אחד @אפונה.

3.8. זיהוי אובייקטים ב- Guice

שלא כמו האביב, לגויס בעצם יש מַפָּה מִבְנֶה. המשמעות היא שלא נוכל לקבל מספר כריכות לאותו סוג מבלי להשתמש במטא נתונים נוספים.

Guice מספק הערות מחייבות כדי לאפשר הגדרת כריכות מרובות לאותו סוג. בואו נראה מה קורה אם יש לנו שתי כריכות שונות לאותו סוג ב- Guice.

אדם בכיתה ציבורית {}

עכשיו, בואו נכריז על שני מחייבים שונים עבור ה- אדם מעמד:

לאגד (Person.class). toConstructor (Person.class.getConstructor ()); לאגד (Person.class) .toProvider (ספק חדש () {אדם ציבורי לקבל () {אדם p = אדם חדש (); להחזיר p;}});

וכאן נוכל להשיג מופע של אדם מעמד:

אדם אדם = מזרק. GetInstance (Person.class); assertNotNull (אדם);

פעולה זו תיכשל עם:

com.google.inject.CreationException: כריכה לאדם כבר הוגדרה ב- GuiceModule.configure ()

אנו יכולים להתגבר על נושא זה פשוט על ידי השלכת אחד הכריכות עבור ה- אדם מעמד.

3.9. תלות אופציונלית באביב

תלות אופציונלית הן תלות שאינן נדרשות בעת חיווט אוטומטי או הזרקת שעועית.

עבור שדה שעליו הועלה הערה @Autowired, אם שעועית עם סוג נתונים תואם לא תמצא בהקשר, אביב יזרוק NoSuchBeanDefinitionException.

למרות זאת, לפעמים אולי נרצה לדלג על חיווט אוטומטי לתלות מסוימת ולהשאיר אותם כ- ריקבלי לזרוק חריג:

בואו נסתכל על הדוגמה הבאה:

@Component בכיתה ציבורית BookServiceImpl מיישם את BookService {@Autowired פרטי AuthorService authorService; }
מחלקה ציבורית AuthorServiceImpl מיישמת את AuthorService {}

כפי שניתן לראות מהקוד לעיל, AuthorServiceImpl המחלקה לא הוסרה כמרכיב. ונניח כי אין בשיטת הצהרת שעועית בקבצי התצורה שלנו.

עכשיו, בואו נעבור את הבדיקה הבאה כדי לראות מה קורה:

BookService bookService = context.getBean (BookService.class); assertNotNull (bookService);

באופן לא מפתיע, זה ייכשל עם:

org.springframework.beans.factory.NoSuchBeanDefinitionException: אין שעועית כשירה מסוג 'AuthorService' זמינה

אנחנו יכולים לעשות authorService תלות אופציונלית באמצעות Java 8 אופציונאלי הקלד כדי להימנע מחריג זה.

מחלקה ציבורית BookServiceImpl מיישמת את BookService {@Autowired private אופציונלי authorService; }

עכשיו, שלנו authorService התלות דומה יותר למיכל שעשוי להכיל שעועית של AuthorService סוּג. למרות שאין שעועית ל AuthorService בהקשר היישום שלנו, שלנו authorService השדה עדיין לא יהיהריק מיכל ריק. לפיכך, לאביב לא תהיה שום סיבה לזרוק NoSuchBeanDefinitionException.

כחלופה ל אופציונאלי, אנחנו יכולים להשתמש @Autowiredשל נדרש המאפיין, שמוגדר ל- נָכוֹן כברירת מחדל, כדי להפוך תלות לבחירה. אנחנו יכולים להגדיר את נדרש מייחס ל שֶׁקֶר כדי להפוך תלות לאופציונלית לחיווט אוטומטי.

לפיכך, אביב ידלג על הזרקת התלות אם שעועית מסוג הנתונים שלה אינה זמינה בהקשר. התלות תישאר מוגדרת ריק:

@Component בכיתה ציבורית BookServiceImpl מיישם את BookService {@Autowired (חובה = שקר) AuthorService פרטי authorService; }

לפעמים סימון תלות אופציונלי יכול להיות שימושי מכיוון שלא כל התלות תמיד נדרשות.

עם זאת, עלינו לזכור כי נצטרך לנקוט משנה זהירות ו ריק-בדיקות במהלך הפיתוח כדי להימנע מכך NullPointerException בשל ריק תלות.

3.10. תלות אופציונלית ב- Guice

בדיוק כמו אביב, רעיון יכול גם להשתמש ב- Java 8 אופציונאלי הקלד כדי להפוך תלות לבחירה.

בואו נגיד שאנחנו רוצים ליצור כיתה ועם a פו תלות:

FooProcessor בכיתה ציבורית {@ הזרקת Foo foo פרטית; }

עכשיו, בואו נגדיר כריכה ל- פו מעמד:

bind (Foo.class) .toProvider (ספק חדש () {public Foo get () {return null;}});

עכשיו בואו ננסה להשיג מופע של FooProcessor במבחן יחידה:

FooProcessor fooProcessor = injector.getInstance (FooProcessor.class); assertNotNull (fooProcessor);

מבחן היחידה שלנו ייכשל עם:

com.google.inject.ProvisionException: null מוחזר על ידי כריכה ב- GuiceModule.configure (..) אך הפרמטר הראשון של FooProcessor. [...] אינו @ Nullable

על מנת לדלג על חריג זה, אנו יכולים לבצע את foo תלות אופציונלית עם עדכון פשוט:

FooProcessor בכיתה ציבורית {@ הזרקה פרטית אופציונלי foo; }

@לְהַזרִיק אין נדרש תכונה לסימון התלות אופציונלית. גישה אלטרנטיבית ל עשה תלות אופציונלית ב- Guice היא להשתמש ב- @מאפשרת ערכי null ביאור.

Guice סובל הזרקה ריק ערכים במקרה של שימוש @מאפשרת ערכי null כפי שבא לידי ביטוי בהודעת החריג שלמעלה. בואו להחיל את @מאפשרת ערכי null ביאור:

FooProcessor בכיתה ציבורית {@Inject @ Nullable Foo foo foo; }

4. יישומים של סוגי הזרקת תלות

בחלק זה, נבחן את סוגי הזרקת התלות ונשווה את היישומים המסופקים על ידי אביב וגויס על ידי מעבר על מספר דוגמאות.

4.1. הזרקת קונסטרוקטור באביב

בהזרקת תלות מבוססת קונסטרוקטור, אנו מעבירים את התלות הנדרשת לכיתה בזמן האינסטיזציה.

בואו נגיד שאנחנו רוצים שיהיה לנו רכיב קפיצי ואנחנו רוצים להוסיף תלות באמצעות הבנאי שלו. אנחנו יכולים להעלות הערה על הבנאי הזה עם @Autowired:

@Component class class SpringPersonService {PersonDao personDao פרטי; @PowerPersonService ציבורי אוטומטי (PersonDao personDao) {this.personDao = personDao; }}

החל באביב 4, @Autowired תלות אינה נדרשת עבור הזרקת סוג זה אם בכיתה יש בנאי אחד בלבד.

בואו נחזור א SpringPersonService שעועית במבחן:

SpringPersonService personService = context.getBean (SpringPersonService.class); assertNotNull (personService);

4.2. הזרקת קונסטרוקטור בגויסה

אנו יכולים לסדר מחדש את הדוגמה הקודמת ל ליישם הזרקת קונסטרוקטור בגויס. שים לב כי Guice משתמש @לְהַזרִיק במקום @Autowired.

מעמד ציבורי GuicePersonService {PersonDao personDao פרטי; @ הזרקת GuicePersonService ציבורית (PersonDao personDao) {this.personDao = personDao; }}

כך אנו יכולים לקבל מופע של GuicePersonService כיתה מה מַזרֵק במבחן:

GuicePersonService personService = injector.getInstance (GuicePersonService.class); assertNotNull (personService);

4.3. הזרקת סתר או שיטה באביב

בהזרקת תלות מבוססת מגדיר, המכולה תקרא לשיטות קביעת מחלקה לאחר קריאת הקונסטרוקטור לאינסטינציה של הרכיב.

בואו נגיד שאנחנו רוצים שאביב יקנה אוטומטית תלות בשיטת מגדיר. אנו יכולים להעלות הערה על שיטת הקובעת הזו עם @Autowired:

@Component class class SpringPersonService {PersonDao personDao פרטי; @ בטל ציבורי מבוטל setPersonDao (PersonDao personDao) {this.personDao = personDao; }}

בכל פעם שאנחנו צריכים מופע של SpringPersonService בכיתה, האביב יהיה חוט אוטומטי personDao שדה על ידי הפעלת ה- setPersonDao () שיטה.

אנחנו יכולים להשיג SpringPersonService שעועית וגישה שלה personDao שדה במבחן כמפורט להלן:

SpringPersonService personService = context.getBean (SpringPersonService.class); assertNotNull (personService); assertNotNull (personService.getPersonDao ());

4.4. הזרקת סתר או שיטה בגויסה

פשוט נשנה קצת את הדוגמה שלנו כדי להשיג הזרקת סטר בגויס.

מעמד ציבורי GuicePersonService {אישי PersonDao personDao; @ הזריק בטל פומבי setPersonDao (PersonDao personDao) {this.personDao = personDao; }}

בכל פעם שאנחנו מקבלים מופע של GuicePersonService בכיתה מהמזרק, יהיה לנו את personDao שדה הועבר לשיטת הקובע לעיל.

כך נוכל ליצור מופע של GuicePersonService בכיתה וגישה שלה personDao שדהבמבחן:

GuicePersonService personService = injector.getInstance (GuicePersonService.class); assertNotNull (personService); assertNotNull (personService.getPersonDao ());

4.5. הזרקת שדה באביב

כבר ראינו כיצד ליישם הזרקת שדה גם לאביב וגם לגויס בכל הדוגמאות שלנו. אז זה לא מושג חדש עבורנו. אבל בואו רק נפרט את זה שוב לשלמות.

במקרה של הזרקת תלות מבוססת שדה, אנו מזריקים את התלות על ידי סימון אותם עם @Autowired אוֹ @לְהַזרִיק.

4.6. הזרקת שדה בגויסה

כפי שהזכרנו בסעיף לעיל, כבר כיסינו את ה- הזרקת שדה לשימוש ב- Guice @לְהַזרִיק.

5. מסקנה

במדריך זה חקרנו את מספר ההבדלים המרכזיים בין מסגרות גויס לאביב בדרכים שלהם ליישום הזרקת תלות. כמו תמיד, דגימות קוד Guice ו- Spring הסתיימו ב- GitHub.


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