TL / DR: проблема сводится к созданию настраиваемой области Spring, внедряющей bean-объект, подобный prototype
, в синглтон с proxyMode = ScopedProxyMode.TARGET_CLASS
, но все еще получающий синглтон в версии конфигурации Java для конфигурации (тогда какотлично работает с XML).
ОБНОВЛЕНИЕ: Проблема решена, см. ответ.
Я использую jBehave для написания тестовых сценариев BDD для нашего приложения Spring. Недавно мы подумали, что нам нужна независимость при выполнении тестовых сценариев (то есть контекст тестирования должен быть сброшен перед каждым сценарием), и нашли в Интернете эту статью, которая точно решает проблему, с которой мы имеем дело.
В статье рекомендуется создать настраиваемую область Spring Scenario
, назначить ее классу, представляющему тестовый контекст, и внедрить прокси-сервер AOP вместо файла контекста.
Я кодировал все в соответствии со статьей, и это прекрасно работало, но дело в том, что нам это нужно с точки зрения конфигурации Java, а не XML, и когда я преобразовал все изменения в конфигурацию Java, он остановилсяработающий - то есть Map
в StoryContext
не сбрасывался после каждого тестового сценария и содержал значения из предыдущего сценария.
Мои изменения были следующими:
- помечены *Класс 1024 * с аннотацией
@Component
:
@Component
public class ScenarioScope implements Scope {
private final ConcurrentMap<String, Object> cache = new ConcurrentHashMap<>();
@BeforeScenario
public void startScenario() {
cache.clear();
}
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
return cache.putIfAbsent(name, objectFactory.getObject());
}
@Override
public Object remove(String name) {
return cache.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return "scenario scope";
}
}
- создал класс конфигурации Spring для добавления новой области:
@Configuration
public class SpringConfiguration {
@Bean
public static CustomScopeConfigurer scopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", new ScenarioScope());
return configurer;
}
}
- аннотировал класс
StoryContext
аннотациями @Component
и @Scope
:
@Component
@Scope(value = "scenario", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class StoryContext {
private Map<String, Object> storyContext = new HashMap<>();
public void put(String key, Object value) {
storyContext.put(key,value);
}
public <T> T get(String key, Class<T> tClass) {
return (T) storyContext.get(key);
}
@PostConstruct
public void clearContext() {
storyContext.clear();
}
}
Насколько мне известно, приведенный выше код аналогичен конфигурации XML, которая была следующей:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=" http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<context:component-scan base-package="foo"/>
<bean id="scenarioScope" class="foo.ScenarioScope"/>
<bean class="foo.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="scenario" value-ref="scenarioScope"/>
</map>
</property>
</bean>
<bean id="storyContext" class="foo.StoryContext" scope="scenario">
<aop:scoped-proxy/>
</bean>
</beans>
Может кто-нибудь указать мне, почему конфигурация Java не работает должным образом? Я потратил некоторое время на изучение stackoverflow, но большинство подобных вопросов решается добавлением proxyMode = ScopedProxyMode.TARGET_CLASS
к аннотации @Scope
, что я и сделал.
UPDATE: Поэтому я попыталсяпостепенно переходить от XML к конфигурации Java, комментируя / декомпозируя соответствующие строки в файлах, и выясняю, что проблема в этой части кода:
<bean class="foo.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="scenario" value-ref="scenarioScope"/>
</map>
</property>
</bean>
Когда я заменяю ее на
@Configuration
public class SpringConfiguration {
@Bean
public static CustomScopeConfigurer scopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", new ScenarioScope());
return configurer;
}
}
боб StoryContext
становится одноэлементным. Я попытался сделать это другим способом: зарегистрировать пользовательский BeanFactoryPostProcessor
и использовать метод registerScope()
, как описано здесь , но он тоже не сработал.