Spring IoC контейнер: зависимости, инъекция метода поиска
Инъекция метода поиска (Lookup method injection) - это способность контейнера переопределять методы в управляемых контейнером bean-компонентах и возвращать результат поиска для другого именованного bean-компонента в контейнере. Поиск обычно включает в себя прототип bean-компонента, как в сценарии, описанном в предыдущем посте. Spring Framework реализует внедрение этого метода, используя генерацию байт-кода из библиотеки CGLIB для динамической генерации подкласса, который переопределяет метод.
- Чтобы этот динамический подкласс работал, класс, который является подклассом контейнера bean-компонента Spring, не может быть окончательным (final), и переопределяемый метод также не может быть конечным (final).
- Модульное тестирование (юнит-тестирование) класса, который имеет абстрактный метод, требует, чтобы вы сами создали подкласс класса и предоставили реализацию-заглушку абстрактного метода.
- Конкретные методы также необходимы для сканирования компонентов, для которого требуются конкретные классы.
- Еще одним ключевым ограничением является то, что методы поиска не работают с фабричными методами и, в частности, не работают с методами @Bean в классах конфигурации, поскольку в этом случае контейнер не отвечает за создание экземпляра и, следовательно, не может создавать сгенерированные во время выполнения подкласс на лету.
В случае класса CommandManager из кода предыдущего поста контейнер Spring динамически переопределяет реализацию метода createCommand(). Класс CommandManager не имеет никаких зависимостей Spring, как показывает переработанный пример:
Java
package fiona.apple;
// нет больше Spring импортов!
public abstract class CommandManager {
public Object process(Object commandState) {
// захватить новый экземпляр соответствующего интерфейса Command
Command command = createCommand();
// устанавливаем состояние (ожидаемо, нового) экземпляра Command
command.setState(commandState);
return command.execute();
}
// хорошо ... но где реализация этого метода?
protected abstract Command createCommand();
}
Kotlin
package fiona.apple
// нет больше Spring импортов!
abstract class CommandManager {
fun process(commandState: Any): Any {
// захватить новый экземпляр соответствующего интерфейса Command
val command = createCommand()
// устанавливаем состояние (ожидаемо, нового) экземпляра Command
command.state = commandState
return command.execute()
}
// хорошо ... но где реализация этого метода?
protected abstract fun createCommand(): Command
}
В клиентском классе, который содержит метод для внедрения (в данном случае CommandManager), метод для внедрения требует сигнатуры следующей формы:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
Если метод абстрактный, динамически генерируемый подкласс реализует метод. В противном случае динамически генерируемый подкласс переопределяет конкретный метод, определенный в исходном классе. Рассмотрим следующий пример:
<!-- бин с состоянием, развернутый как прототип (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- вставлять зависимости здесь как требуется -->
</bean>
<!-- commandProcessor использует statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
Компонент, идентифицированный как commandManager, вызывает собственный метод createCommand() всякий раз, когда ему требуется новый экземпляр компонента myCommand. Вы должны быть осторожны при развертывании bean-компонента myCommand в качестве прототипа, если это действительно то, что нужно. Если это одиночный объект (синглтон), каждый раз возвращается один и тот же экземпляр компонента myCommand.
В качестве альтернативы, в модели компонентов на основе аннотаций вы можете объявить метод поиска через аннотацию @Lookup, как показано в следующем примере:
Java
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
Kotlin
abstract class CommandManager {
fun process(commandState: Any): Any {
val command = createCommand()
command.state = commandState
return command.execute()
}
@Lookup("myCommand")
protected abstract fun createCommand(): Command
}
Или, более идиоматично, вы можете рассчитывать на то, что целевой бин будет разрешен в соответствии с объявленным типом возврата метода поиска:
Java
public abstract class CommandManager {
public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract MyCommand createCommand();
}
Kotlin
abstract class CommandManager {
fun process(commandState: Any): Any {
val command = createCommand()
command.state = commandState
return command.execute()
}
@Lookup
protected abstract fun createCommand(): Command
}
Обратите внимание, что вы обычно должны объявлять такие аннотированные методы поиска с конкретной реализацией заглушки, чтобы они были совместимы с правилами сканирования компонентов Spring, в которых абстрактные классы игнорируются по умолчанию. Это ограничение не распространяется на явно зарегистрированные или явно импортированные классы компонентов.
Другим способом доступа к целевым bean-объектам различной области является точка внедрения ObjectFactory/Provider.
Читайте также:
- Spring IoC контейнер: зависимости, лениво инициализированные бины
- Spring IoC контейнер: зависимости, инъекция метода
- Spring IoC контейнер: зависимости, автопривязка взаимодействующих компонентов
Комментарии
Отправить комментарий