TL / DR:问题归结为创建自定义Spring范围,将具有prototype
的作用域的bean注入到带有proxyMode = ScopedProxyMode.TARGET_CLASS
的单例中,但是仍然在配置的Java配置版本中获得了一个单例(尽管它可以正常工作使用XML也可以)。
UPDATE:问题已解决,请参见答案。
我正在使用jBehave为我们的Spring应用程序编写BDD测试方案。最近,我们认为在执行测试方案时需要独立性(这意味着必须在每个方案之前重置测试上下文),并在网上找到this文章,确切地解决了我们正在处理的问题。
本文建议创建一个自定义Spring Scenario
范围,将其分配给表示测试上下文的类,并注入AOP代理而不是上下文文件。
我已经按照文章进行了编码,并且效果很好,但是我们需要的是Java配置而不是XML,当我将所有更改都转换为Java配置时,它就停止了工作-这意味着在每个测试方案之后,Map
中的StoryContext
未重置,并且包含先前方案中的值。
我的更改如下:
ScenarioScope
注释标记@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";
}
}
@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配置转移到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
bean成为单例。我尝试通过注册自定义BeanFactoryPostProcessor
并按照registerScope()
所述使用here方法来做另一种方式,但它也没有用。
TL / DR:问题归结为创建自定义Spring范围,使用proxyMode = ScopedProxyMode.TARGET_CLASS将类似原型的范围内的bean注入到一个单例中,但仍然在...
我设法解决了这个问题,解决方案很简单:ScenarioScope
类中的SpringConfiguration
实例必须由Spring容器管理,而不是通过new()
运算符创建:
@Configuration
public class SpringConfiguration {
@Bean
public static CustomScopeConfigurer scopeConfigurer(ScenarioScope scenarioScope) {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", scenarioScope);
return configurer;
}
}