Kamusta! Habang ang pangangasiwa ng JavaRush ay nagtatrabaho sa mga bagong antas, gusto kong magsimula ng isang serye ng mga artikulo sa pagsasanay sa Spring Framework. Oo, alam ko na marami nang materyal sa paksang ito sa Internet, ngunit, bilang mga palabas sa pagsasanay, lahat sila ay nasa antas ng Hello World. Gusto kong pag-usapan hindi ang tungkol sa kung paano tama ang paglalagay ng mga anotasyon, ngunit tungkol sa kung paano gumagana ang lahat ng ito "sa ilalim ng hood." Ang artikulo ay inilaan para sa mga nakagawa na sa balangkas na ito sa isang paraan o iba pa at pamilyar sa mga pangunahing konsepto.
Sinisimulan ang konteksto.
Kaya magsimula tayo sa mga pangunahing kaalaman. Sa palagay ko, isa sa mga pinakamahalagang punto ay upang maunawaan kung paano naka-set up ang konteksto at sinisimulan ang mga bean. Tulad ng alam mo, bago magsimulang gumana ang Spring , kailangan itong i-configure. Sa antediluvian times, ito ay ginawa gamit ang mga xml file (sa ilang mga proyekto, pangunahin sa mga luma, patuloy nilang ginagawa ito hanggang ngayon). Narito ang isang maliit na halimbawa ng naturang configuration file:<?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>
Sa pangkalahatan, ito ay sapat na upang lumikha ng isang pares ng mga controllers at maglunsad ng isang startup (na hindi mag-alis). Ngunit paano gagawin ng pagsasaayos na ito ang Spring? At narito kung saan nagiging kawili-wili ang mga bagay. Upang ang aming pagsasaayos ay maunawaan ng Spring, mayroong isang XmlBeanDefinitionReader
. BeanDefinition
Ito ay isang panloob na bahagi ng Spring na nag-scan (nag-parse) ng xml at lumilikha ng 's batay sa kung ano ang isinulat namin doon . BeanDefinition
ay isang bagay na nag-iimbak ng impormasyon tungkol sa bean. Kabilang dito ang: mula sa aling klase ito (ang bean) dapat likhain; saklaw; kung naka-install ang tamad na pagsisimula; Kailangan bang magsimula bago ang bean na ito ng isa pa at iba pang mga katangian na inilarawan sa xml. Ang lahat ng natanggap BeanDefinition
ay idinaragdag sa HashMap
, kung saan ang identifier ay ang pangalan ng bean (itinakda mo o itinalaga ng Spring) at BeanDefinition
ang mismong bagay. Matapos BeanDefinition
malikha ang lahat, isang bagong bayani ang lilitaw sa entablado - BeanFactory
. Ang bagay na ito ay umuulit sa HashMap’e
s BeanDefinition
, lumilikha ng mga beans batay sa mga ito at inilalagay ang mga ito sa isang lalagyan ng IoC. Mayroong isang nuance dito, sa katunayan, kapag nagsimula ang application, ang lalagyan ng IoC ay maglalaman ng mga beans na mayroong saklaw ng Singleton (itinakda bilang default), habang ang iba ay nilikha kapag kinakailangan ang mga ito (prototype, kahilingan, session). At ngayon isang maliit na digression, kilalanin natin ang isa pang karakter.
Kilalanin ang BeanPostProcessor. (BPP)
Ang katotohanan ay ang isang bean ay hindi kinakailangang isang klase ng lohika ng negosyo para sa iyong aplikasyon. Ang isang bean ay tinatawag ding isang infrastructure bean. Sa madaling salita, ang isang infrastructure bean ay isang klase na nagko-configure ng iyong business logic beans (oo, masyadong maraming beans). Sasabihin ko sa iyo ang higit pa tungkol dito sa ibaba, ngunit upang gawing mas malinaw kung ano ang eksaktong kino-configure ng BPP, magbibigay ako ng isang halimbawa. Pamilyar ba ang lahat sa buod@Autowired
? Kaya, AutowiredAnnotationBeanPostProcessor
responsable ka sa pagtiyak na ang lahat ng iyong mga klase ay naka-embed sa isa't isa.
Bumalik tayo sa BeanFactory
Зная теперь о BPP, нужно уточнить, что итерируясь поHashMap
’e с BeanDefinition
’ами сперва создаются и кладутся отдельно (не в IoC контейнер) все BeanPostProcessor
’ы. После этого создаются обычные бины нашей бизнес-логики, складываются в IoC-контейнер и начинается их настройка с помощью отдельно отложенных BPP. А происходит это вот How, каждый BPP имеет 2 метода:
postProcessorBeforeInitialization(Object bean, String beanName);
postProcessorAfterInitialization(Object bean, String beanName);
Происходит итерация по нашим бинам дважды. В первый раз вызывается метод postProcessorBeforeInitialization
, а во второй раз вызывается метод postProcessorAfterInitialization
. Наверняка возник вопрос, зачем нужны два метода, объясняю. Дело в том, что для обработки некоторых аннотаций (таких How @Transactional
, например) наш бин заменяется proxy классом. Whatбы понять зачем это делается, нужно знать How работает @Transactional
, а работает это вот How. В метод, помеченный данной аннотацией необходимо налету добавить еще пару строк codeа. Как это сделать? Верно, с помощью создания класса proxy, внутри которого и будет добавлен необходимый code в нужный метод. А теперь представим такую ситуацию, у нас есть класс:
class A {
@Autowired
private SomeClass someClass;
@Transactional
public void method() {
// модификатор доступа обязательно public
}
}
В этом классе 2 аннотации @Autowired
и @Transactional
. Обе аннотации обрабатываются разными BPP. Если первым отработает AutowiredBPP
, то все будет в порядке, но если нет, то мы столкнемся с вот Howой проблемой. Дело в том, что когда создается класс proxy, то вся мета-информация теряется. Другими словами, информации об аннотации @Autowired
в proxy классе не будет, а значит и AutowiredBPP
не отработает, а значит наше поле someClass
будет иметь meaning null
, что, скорее всего, приведет к NPE. Также стоит знать, что между вызовами методов postProcessorBeforeInitialization
и postProcessorAfterInitialization
происходит вызов init
-метода, если он есть. Это по-большому счету второй конструктор, но отличие в том, что в этот момент все наши зависимости уже внедрены в класс и мы можем к ним обратиться из init
-метода. Итак, еще раз алгоритм инициализации контекста:
XmlBeanDefinitionReader
сканирует наш xml-конфигурационный файл.- Создает
BeanDefinition
’ы и кладет их вHashMap
. - Приходит
BeanFactory
и из этойHashMap
отдельно складывает всеBeanPostProcessor
’ы. - Создает из
BeanDefinition
’ов бины и кладет их в IoC-контейнер. - Тут приходят BPP и настраивают эти бины с помощью 2х методов.
- Готово.
GO TO FULL VERSION