Мне потребовалось некоторое время, чтобы четко понять, что вы хотите сделать, и последствия за его пределами. Поиск корневого контекста приложения из сервлета был бы легкой частью, context.getParentBeanFactory()
, или непосредственно context.getParent()
дает его немедленно в любом классе ApplicationContextAware
или путем прямого внедрения ApplicationContext
.
Сложность заключается в том, что во время инициализации контекста приложения сервлета корневой контекст приложения уже был обновлен. Если я посмотрю, что происходит в Tomcat:
- во время развертывания контекст корневого приложения полностью инициализируется и обновляется
- затем при первом запросе
DispatcherServlet
дочерний контекст инициализируется корневым контекстом в качестве родительского.
Это означает, что при инициализации контекста сервлета уже слишком поздно внедрять bean-компоненты в корневой контекст: все одноэлементные bean-компоненты уже созданы.
Там может быть обходной путь, все со своими недостатками:
- register a new Configuration class in parent context and do a new refresh().IMHO, this would be the least bad solution as normally WebApplicationContextes support multiple refresh. The potentials problems are :
- all other beans must be initialized once without the new beans : the configuration must be tolerant to non existing beans
- другие компоненты (фильтры, безопасность, DAO и т. д.) уже могут быть запущены, и их необходимо тщательно протестировать на предмет горячего обновления контекста.
- create an intermediate ApplicationContext with current root as parent containing beans that should not go into servlet application context, and then create the servlet application context with this intermediate context as parent.
- no problem for the root application context that is not even aware of the whole operation
- но ни один bean-компонент корневого контекста не может быть введен с любым новым bean-компонентом
- зарегистрировать все новые bean-компоненты непосредственно в корневом контексте. Хорошо, все в порядке для корневой инициализации, и компоненты контекста сервлета будут иметь доступ ко всем компонентам. Но если один новый bean-компонент необходимо внедрить с bean-компонентом из контекста сервлета, вам придется сделать это вручную при инициализации контекста сервлета, с тщательными тестами (или молитвами), что его нельзя использовать до этого... и у вас будет несколько загрязнение корневого контекста bean-компонентами, относящимися только к сервлету
- use only only root context and an empty servlet context.
- ok, each bean has access to any other one
- но это нарушает разделение между корневым контекстом и контекстом сервлета и добавляет некоторое загрязнение корневому контексту.
Мой вывод состоит в том, что наличие одной части конфигурации для двух разных контекстов приложения немного противоречит философии Spring, и я бы посоветовал вам использовать два отдельных класса конфигурации, один для корневого контекста, а другой для контекста сервлета. Но если хотите, я могу уточнить обновление корневого контекста из контекста сервлета (1-е решение).
Если вы хотите внедрить bean-компоненты в корневой контекст из функции, которая была бы объявлена в контексте сервлета с единственной точкой конфигурации, вы можете использовать что-то вроде:
@Configuration
public class FeatureConfig implements ApplicationContextAware {
static boolean needInit = true;
@Override
// Register the configuration class into parent context and refreshes all
public void setApplicationContext(ApplicationContext ac) throws BeansException {
AnnotationConfigWebApplicationContext parent =
(AnnotationConfigWebApplicationContext) ((AnnotationConfigWebApplicationContext) ac).getParent();
if (needInit) { // ensure only one refresh
needInit = false;
parent.register(RootConfig.class);
parent.refresh();
((AnnotationConfigWebApplicationContext) ac).refresh();
}
}
@Configuration
@Conditional(NoParentContext.class)
// Can only be registered in root context
public static class RootConfig {
// configuration to be injected in root context ...
}
// special condition that the context is root
public static class NoParentContext implements Condition {
@Override
public boolean matches(ConditionContext cc, AnnotatedTypeMetadata atm) {
logger.debug(" {} parent {}", cc.getBeanFactory(), cc.getBeanFactory().getParentBeanFactory());
return (cc.getBeanFactory().getParentBeanFactory() == null);
}
}
// other beans or configuration that normally goes in servlet context
}
С таким классом @Configuration
достаточно иметь аннотацию @import(FeatureConfig.class)
в классе конфигурации для контекста приложения класса DispatcherServlet
.
Но я не смог найти способ разрешить настройку до обычной инициализации контекста приложения сервлета. В результате любой bean-компонент из специальной конфигурации может быть внедрен только в корневой контекст с @Autowired(required=false)
, потому что корневой контекст будет обновляться дважды, первый раз без специального класса конфигурации, второй раз с ним.
person
Serge Ballesta
schedule
26.08.2014
ImportAware
, которая импортируется с помощью аннотации@EnableFeature
, которая настраивает некоторые компоненты. Однако один компонент (фабрика шаблонов) лучше подходит для корневого контекста. Но я не уверен, как добиться этого чистым способом. - person Bart   schedule 25.08.2014getParentBeanFactory
— ваш лучший выбор. - person Andrei Stefan   schedule 25.08.2014@Conditional
и то, как он используется. - person Bart   schedule 25.08.2014@Bean
для его регистрации в веб-приложении. контекст, но с контекстом корневого приложения?! - person Andrei Stefan   schedule 25.08.2014@Configuration
связан с контекстом приложения, и bean-компоненты внутри этого класса будут зарегистрированы в этом контексте приложения (так работает Spring). В вашем случае вы хотите войти в этот жизненный цикл и заставить Spring зарегистрировать bean-компонент в классе@Configuration
с другим контекстом приложения, чем тот, который изначально связан с классом конфигурации. - person Andrei Stefan   schedule 25.08.2014BeanFactoryPostProcessor
, зарегистрированный в контексте веб-приложения, и вручную зарегистрировать те компоненты, которые вам нужны, в корневом контексте. К сожалению, я не уверен, что это чистый способ, так как вам нужно будет вручную зарегистрировать определения компонентов внутри методаpostProcessBeanFactory
, используя что-то вродеgetParentBeanFactory().registerSingleton()
. Но в этом случае есть несколько вопросительных знаков, связанных с тем, насколько сложен ваш bean-компонент (есть ли у него зависимости?, является ли он сам зависимостью?, если да, то где находятся эти зависимости — root или web?). - person Andrei Stefan   schedule 25.08.2014BeanFactoryPostProcessor
может быть правильным способом изменить определения компонентов. Сегодня я обдумаю эту идею и отчитаюсь. Если у вас есть другие предложения, пожалуйста, поделитесь ими со мной :) - person Bart   schedule 25.08.2014