【发布时间】:2019-10-15 14:41:40
【问题描述】:
TL/DR:问题归结为创建自定义 Spring 范围,将 prototype 类似范围的 bean 注入带有 proxyMode = ScopedProxyMode.TARGET_CLASS 的单例中,但仍然在配置的 Java 配置版本中获得单例(而它适用于 XML)。
更新:问题已解决,请参阅答案。
我正在使用 jBehave 为我们的 Spring 应用程序编写 BDD 测试场景。我们最近认为我们在执行测试场景时需要独立性(这意味着必须在每个场景之前重置测试上下文)并在网络上找到了 this 文章,该文章正好解决了我们正在处理的问题。
文章建议创建自定义 Spring Scenario 范围,将其分配给表示测试上下文的类并注入 AOP 代理而不是上下文文件。
我已经按照文章编写了所有代码并且效果很好,但问题是我们在 Java 配置方面需要它,而不是 XML,当我将所有更改转换为 Java 配置时,它停止工作 - 意思是StoryContext 中的 Map 在每个测试场景之后都没有重置,并且包含前一个场景的值。
我的更改如下:
- 用
@Component注释标记ScenarioScope类:
@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;
}
}
- 使用
@Component和@Scope注释对StoryContext类进行注释:
@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 注释来解决,我这样做了。
更新:所以我尝试通过注释/取消文件中的相应行来逐步从 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 bean 变成了单例。我尝试通过注册自定义BeanFactoryPostProcessor 并使用registerScope() 方法(如here 所述)以另一种方式进行操作,但它也不起作用。
【问题讨论】:
标签: java spring spring-aop jbehave dynamic-proxy