שלום! בזמן שמנהלת JavaRush עובדת על רמות חדשות, אני רוצה להתחיל סדרה של מאמרי הדרכה על מסגרת האביב. כן, אני יודע שכבר יש הרבה חומר בנושא זה באינטרנט, אבל כפי שמראה בפועל, כולם ברמת שלום עולם. אני רוצה לדבר לא על איך למקם נכון הערות, אלא על איך הכל עובד "מתחת למכסה המנוע". המאמר מיועד למי שכבר עבד עם מסגרת זו בצורה כזו או אחרת ומכיר את המושגים הבסיסיים.
אתחול ההקשר.
אז בואו נתחיל עם היסודות. לדעתי, אחת הנקודות החשובות ביותר היא להבין כיצד מוגדר ההקשר והשעועית מאותחלת. כפי שאתה יודע, לפני שהאביב מתחיל לעבוד, יש להגדיר אותו. בתקופות קדומות, זה נעשה באמצעות קבצי xml (בחלק מהפרויקטים, בעיקר ישנים, הם ממשיכים לעשות זאת עד היום). הנה דוגמה קטנה לקובץ תצורה כזה:<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="helloWorld" class="ru.javarush.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
</beans>
בגדול, זה מספיק כדי ליצור כמה בקרים ולהשיק סטארט-אפ (שלא ימריא). אבל איך התצורה הזו תגרום לאביב לעבוד? וכאן הדברים נעשים מעניינים. על מנת שהתצורה שלנו תובן על ידי אביב, יש XmlBeanDefinitionReader
. BeanDefinition
זהו רכיב Spring פנימי שסורק (מנתח) xml ויוצר על סמך מה שכתבנו שם . BeanDefinition
הוא חפץ המאחסן מידע על השעועית. זה כולל: מאיזה מחלקה צריך ליצור אותו (השעועית); תְחוּם; האם מותקן אתחול עצל; האם יש צורך לאתחל לפני שעועית זו עוד ועוד מאפיינים המתוארים ב-xml. כל המתקבלים BeanDefinition
מתווספים ל- HashMap
, שבהם המזהה הוא שם השעועית (שנקבע על ידך או הוקצה על ידי Spring) והאובייקט BeanDefinition
עצמו. לאחר שהכל BeanDefinition
נוצר, גיבור חדש מופיע על הבמה - BeanFactory
. אובייקט זה חוזר על HashMap’e
s BeanDefinition
, יוצר שעועית המבוססת עליהם ומכניס אותם למיכל IoC. יש כאן ניואנס, למעשה, כאשר האפליקציה מתחילה, מיכל IoC יכיל שעועית בעלי היקף Singleton (מוגדר כברירת מחדל), בעוד השאר נוצרים בעת הצורך (אב-טיפוס, בקשה, סשן). ועכשיו סטייה קטנה, בואו נכיר דמות אחרת.
הכירו את BeanPostProcessor. (BPP)
העובדה היא שעועית היא לא בהכרח סוג של היגיון עסקי עבור היישום שלך. שעועית נקראת גם שעועית תשתית. בקיצור, שעועית תשתית היא מחלקה שמגדירה את שעועית ההיגיון העסקי שלך (כן, יותר מדי שעועית). אני אספר לך יותר על זה למטה, אבל כדי להבהיר קצת יותר מה בדיוק BPP מגדיר, אתן דוגמה. האם כולם מכירים את התקציר@Autowired
? לכן, אתה AutowiredAnnotationBeanPostProcessor
אחראי לוודא שכל השיעורים שלך מוטמעים זה בזה.
בוא נחזור ל-BeanFactory
כאשר יודעים כעת על BPP, יש צורך להבהיר כי בעת איטרציה של overHashMap
's, BeanDefinition
כולם נוצרים תחילה וממוקמים בנפרד (לא במיכל IoC) BeanPostProcessor
. לאחר מכן, נוצרים שעועית רגילה של ההיגיון העסקי שלנו, מוכנסות למיכל IoC, והתצורה שלהם מתחילה באמצעות BPPs דחויים בנפרד. וככה זה קורה, לכל BPP יש 2 שיטות:
postProcessorBeforeInitialization(Object bean, String beanName);
postProcessorAfterInitialization(Object bean, String beanName);
עובר דרך הפחים שלנו פעמיים. בפעם הראשונה נקראת השיטה postProcessorBeforeInitialization
, ובפעם השנייה נקראת השיטה postProcessorAfterInitialization
. בוודאי עלתה השאלה מדוע יש צורך בשתי שיטות, הרשו לי להסביר. העובדה היא שכדי לעבד הערות מסוימות (כגון @Transactional
, למשל), השעועית שלנו מוחלפת במחלקת פרוקסי. כדי להבין למה זה נעשה, אתה צריך לדעת איך זה עובד @Transactional
, וכך זה עובד. עליך להוסיף עוד כמה שורות קוד לשיטה המסומנת בהערה זו תוך כדי תנועה. איך לעשות את זה? זה נכון, על ידי יצירת מחלקת proxy, שבתוכה יתווסף הקוד הדרוש לשיטה הנדרשת. עכשיו דמיינו את המצב הזה, יש לנו שיעור:
class A {
@Autowired
private SomeClass someClass;
@Transactional
public void method() {
// модификатор доступа обязательно public
}
}
לכיתה זו יש 2 הערות @Autowired
ו- @Transactional
. שתי ההערות מעובדות על ידי BPPs שונים. אם זה יעבוד קודם AutowiredBPP
, אז הכל יהיה בסדר, אבל אם לא, אז ניתקל בבעיה הזו. העובדה היא שכאשר נוצרת מחלקת ה-proxy, כל המטא מידע הולך לאיבוד. במילים אחרות, לא יהיה מידע על ההערה @Autowired
במחלקת ה-proxy, ולכן AutowiredBPP
הוא לא יעבוד, מה שאומר שהשדה שלנו someClass
יקבל את הערך null
, שככל הנראה יוביל ל-NPE. כדאי גם לדעת שבין קריאות למתודה נקראת postProcessorBeforeInitialization
השיטה , אם יש כזו. זה בעצם הבנאי השני, אבל ההבדל הוא שברגע זה כל התלות שלנו כבר מוטמעות במחלקה ואנחנו יכולים לגשת אליהן מה- method. אז, שוב אלגוריתם אתחול ההקשר: postProcessorAfterInitialization
init
init
XmlBeanDefinitionReader
סורק את קובץ ה-xml שלנו.- יוצר
BeanDefinition
ומכניס אותםHashMap
. - בא
BeanFactory
ומזהHashMap
בנפרד מצטבר כלBeanPostProcessor
ה'ים. - יוצר
BeanDefinition
שעועית מ-'s ומניח אותם במיכל IoC. - כאן מגיעים BPPs ומגדירים את הפולים האלה בשתי שיטות.
- מוּכָן.
GO TO FULL VERSION