Java 配置模拟 XML 配置不起作用
A Java config analog of XML configuration not working
TL/DR:问题归结为创建自定义 Spring 作用域,将类似 prototype
的作用域 bean 注入具有 proxyMode = ScopedProxyMode.TARGET_CLASS
的单例中,但仍然得到一个配置的 Java 配置版本中的单例(而它在 XML 中工作正常)。
更新:问题已解决,查看答案。
我正在使用 jBehave 为我们的 Spring 应用程序编写 BDD 测试场景。我们最近认为我们需要独立执行测试场景(这意味着必须在每个场景之前重置测试上下文)并在网络上找到 this 文章准确地解决了我们正在处理的问题。
文章建议创建自定义 Spring Scenario
范围,将其分配给表示测试上下文的 class 并注入 AOP 代理而不是上下文文件。
我已经根据文章对所有内容进行了编码并且效果很好,但问题是我们在 Java 配置方面需要它,而不是 XML,当我转换所有更改 Java 配置,它停止工作 - 这意味着 StoryContext
中的 Map
在每个测试场景后都没有重置,并且包含前一个场景的值。
我的修改如下:
- 用
@Component
注释标记了 ScenarioScope
class:
@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 配置 class 以添加新范围:
@Configuration
public class SpringConfiguration {
@Bean
public static CustomScopeConfigurer scopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", new ScenarioScope());
return configurer;
}
}
- 用
@Component
和 @Scope
注释对 StoryContext
class 进行了注释:
@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 配置没有按预期工作?我花了一些时间研究 Whosebug,但大多数类似的问题都是通过将 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
并使用 here 中描述的 registerScope()
方法来实现它,但它也没有用。
我已经设法解决了问题,解决方案很简单:SpringConfiguration
class 中的 ScenarioScope
实例必须由 Spring 管理容器而不是通过 new()
运算符创建:
@Configuration
public class SpringConfiguration {
@Bean
public static CustomScopeConfigurer scopeConfigurer(ScenarioScope scenarioScope) {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", scenarioScope);
return configurer;
}
}
TL/DR:问题归结为创建自定义 Spring 作用域,将类似 prototype
的作用域 bean 注入具有 proxyMode = ScopedProxyMode.TARGET_CLASS
的单例中,但仍然得到一个配置的 Java 配置版本中的单例(而它在 XML 中工作正常)。
更新:问题已解决,查看答案。
我正在使用 jBehave 为我们的 Spring 应用程序编写 BDD 测试场景。我们最近认为我们需要独立执行测试场景(这意味着必须在每个场景之前重置测试上下文)并在网络上找到 this 文章准确地解决了我们正在处理的问题。
文章建议创建自定义 Spring Scenario
范围,将其分配给表示测试上下文的 class 并注入 AOP 代理而不是上下文文件。
我已经根据文章对所有内容进行了编码并且效果很好,但问题是我们在 Java 配置方面需要它,而不是 XML,当我转换所有更改 Java 配置,它停止工作 - 这意味着 StoryContext
中的 Map
在每个测试场景后都没有重置,并且包含前一个场景的值。
我的修改如下:
- 用
@Component
注释标记了ScenarioScope
class:
@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 配置 class 以添加新范围:
@Configuration
public class SpringConfiguration {
@Bean
public static CustomScopeConfigurer scopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", new ScenarioScope());
return configurer;
}
}
- 用
@Component
和@Scope
注释对StoryContext
class 进行了注释:
@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 配置没有按预期工作?我花了一些时间研究 Whosebug,但大多数类似的问题都是通过将 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
并使用 here 中描述的 registerScope()
方法来实现它,但它也没有用。
我已经设法解决了问题,解决方案很简单:SpringConfiguration
class 中的 ScenarioScope
实例必须由 Spring 管理容器而不是通过 new()
运算符创建:
@Configuration
public class SpringConfiguration {
@Bean
public static CustomScopeConfigurer scopeConfigurer(ScenarioScope scenarioScope) {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", scenarioScope);
return configurer;
}
}