Управление сложными жизненными циклами в Guice
Я столкнулся с ситуацией, когда у меня есть граф объектов данных, и я хотел бы создать один сервис для каждого узла на этом графе. Проблема в том, что этот сервис (и его зависимости) зависят от узла, для которого они работают. Что-то вроде этого:
class Visitor {
void enter(Node node){
Service service = guice.create(node)
service.doComplexDomainLogic(/*some runtime information about the graph and the path to get here, other things*/)
}
}
Обратите внимание, что мое намерение состоит в том, чтобы создать новый экземпляр Service
и новый экземпляр любых зависимостей для Service
для каждого узла на графике.
Так что теперь у меня есть пара вариантов:
guice
поддерживается вспомогательной инъекционной фабрикой. Это то, что мы делаем в настоящее время, и требует, чтобы зависимость от узла определялась во всем, от чего зависит сервис, если они тоже зависят отnode
, Другими словами, если я использую фабрику вспомогательных инъекций здесь, я должен использовать ее для каждого класса, которыйservice
зависит от того, что неприятно.- Я могу использовать то, что мы называем вложенными загрузчиками, то есть новый модуль с новым инжектором, эффективно новую среду, и в настройках для этой среды я могу написать
bind(Node.class).toInstance(node)
, Этот код будет выполнен при первом посещении, и его результат будет кэширован. - Я могу использовать пользовательские рамки Guice (я думаю).
Мы сделали второе в паре мест просто потому, что я и моя команда не знали о пользовательских сферах. Теперь у нас есть еще одно использование настраиваемой области, и я должен признаться, что ее реализация была намного сложнее, чем хотелось бы. Следование этому руководству помогает, но ясно, что мне придется углубиться в безопасную землю, и мой последний визит был не таким приятным.
Какую услугу хитрости я должен использовать, чтобы получить это поведение?
1 ответ
Вы можете ввести Injector
и создайте детский инжектор для частей, которые вы хотите. Дочерний инжектор позволит вам изменить граф объектов и получить доступ ко всем зависимостям родительского инжектора, но вы можете получить доступ к своему узлу настолько глубоко, насколько захотите.
class Visitor {
@Inject Injector injector;
void enter(final Node node) {
Service service = injector.createChildInjector(new AbstractModule() {
@Override public void configure() {
bind(Node.class).toInstance(node);
// Anything that does require a Node should be bound here, because
// you can't define it in the parent due to the unsatisfied binding.
bind(SomeInterface.class).to(SomeClassThatRequiresNode.class);
}
}).getInstance(Service.class);
service.doComplexDomainLogic(/* ... */)
}
}
Хотя с областями можно сделать нечто подобное, имейте в виду, что области предназначены только для определения того, когда создавать новый объект, а не когда возвращать тот же объект. Это означает, что вы можете создать @NodeScoped
область и убедитесь, что одни и те же объекты будут возвращены при обработке данного узла, но вам все равно нужно будет связать некоторый вид @NodeScoped NodeHolder
удерживать ваши узлы во время погружения. Вместо того, чтобы хранить и заполнять этот отдельный держатель, дочерний инжектор позволит вам напрямую запросить узел, что может сделать ваш код легче для понимания и тестирования.