Spring IoC контейнер: области применения бинов, бины с областями применения как зависимости
Контейнер Spring IoC управляет не только созданием ваших объектов (компонентов), но и подключением сотрудников (collaborators) (или зависимостей). Если вы хотите внедрить (например) компонент с областью действия HTTP-запроса в другой компонент с более длительным сроком действия, вы можете внедрить прокси AOP (AOP proxy) вместо этого компонента. То есть вам нужно внедрить прокси-объект, который предоставляет тот же открытый интерфейс, что и объект области действия, но который также может извлечь реальный целевой объект из соответствующей области (например, HTTP-запрос) и делегировать вызовы методов в реальный объект.
Вы также можете использовать <aop:scoped-proxy/> между bean-компонентами, которые определены как одноэлементные (singleton), причем ссылка затем проходит через промежуточный прокси, который сериализуем и поэтому может повторно получить целевой одноэлементный компонент при десериализации.
При объявлении <aop:scoped-proxy/> против bean-объекта области видимости прототипа (prototype) каждый вызов метода на общем прокси приводит к созданию нового целевого экземпляра, на который затем перенаправляется вызов.
Кроме того, прокси с областями видимости - не единственный способ получить доступ к бинам из более коротких областей безопасным способом. Вы также можете объявить свою точку внедрения (то есть аргумент конструктора или сеттера или поле с автосвязью (autowired)) как ObjectFactory<MyTargetBean>, что позволяет при вызове getObject() извлекать текущий экземпляр по требованию каждый раз, когда это необходимо - без удержания экземпляра или хранения его отдельно.
В качестве расширенного варианта вы можете объявить ObjectProvider<MyTargetBean>, который предоставляет несколько дополнительных вариантов доступа, включая getIfAvailable и getIfUnique.
Этот вариант JSR-330 называется Provider и используется с объявлением Provider<MyTargetBean> и соответствующим вызовом get() для каждой попытки поиска.
Конфигурация в следующем примере - это всего одна строка, но важно понимать "почему" и "как" за ней:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean-объект в области HTTP-сессии, представленный в качестве прокси -->
<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
<!-- поручает контейнеру проксировать окружающий компонент -->
<aop:scoped-proxy/> <!-- строка, которая определяет прокси -->
</bean>
<!-- бин с singleton областью действия вводится через прокси вышеупомянутому бину -->
<bean id="userService" class="com.something.SimpleUserService">
<!-- ссылка на проксируемый компонент userPreferences -->
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>
Чтобы создать такой прокси, вы вставляете дочерний элемент <aop:scoped-proxy/> в определение bean-объекта с областью применения. Почему для определения bean-объектов на уровнях запроса, сеанса (request, session) и настраиваемой (custom-scope) области требуется элемент <aop:scoped-proxy/>? Рассмотрите следующее определение одноэлементного (singleton) компонента и сопоставьте его с тем, что вам нужно определить для вышеупомянутых областей (обратите внимание, что следующее определение компонента userPreferences в его нынешнем виде неполно):
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
<bean id="userManager" class="com.something.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
В предыдущем примере одноэлементный (singleton) компонент (userManager) внедряется со ссылкой на компонент области HTTP сессии (userPreferences). Важным моментом здесь является то, что bean-компонент userManager является одноэлементным: он создается один раз для каждого контейнера, а его зависимости (в данном случае только один, bean-компонент userPreferences) также вводятся только один раз. Это означает, что bean-компонент userManager работает только с тем же объектом userPreferences (т. е. тем, с которым он был изначально внедрен).
Это не то поведение, которое требуется при внедрении бина с коротким сроком действия в бин с более долгим сроком действия (например, внедрение взаимодействующего бина с областью действия HTTP сессии в качестве зависимости в одноэлементный бин). Скорее, вам нужен один объект userManager, а для времени жизни сеанса HTTP вам нужен объект userPreferences, специфичный для сеанса HTTP. Таким образом, контейнер создает объект, который предоставляет тот же открытый интерфейс, что и класс UserPreferences (в идеале объект, который является экземпляром UserPreferences), который может извлечь реальный объект UserPreferences из механизма определения объема (HTTP request, Session и т. д.). Контейнер внедряет этот прокси-объект в бин userManager, который не знает, что эта ссылка UserPreferences является прокси. В этом примере, когда экземпляр UserManager вызывает метод для объекта UserPreferences с внедрением зависимостей, он фактически вызывает метод на прокси. Затем прокси извлекает реальный объект UserPreferences из (в данном случае) сеанса HTTP и делегирует вызов метода на извлеченный реальный объект UserPreferences.
Таким образом, вам потребуется следующая (правильная и полная) конфигурация при внедрении bean-объектов в рамках запроса и сеанса в взаимодействующие объекты, как показано в следующем примере:
<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.something.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
Читайте также:
- Spring IoC контейнер: области применения бинов
- Spring IoC контейнер: области применения бинов, синглтон
- Spring IoC контейнер: области применения бинов, прототип (prototype)
- Spring IoC контейнер: области применения бинов - Request, Session, Application и WebSocket
Комментарии
Отправить комментарий