XML配置的Java配置类似物不起作用 [英] A Java config analog of XML configuration not working
问题描述
TL/DR:问题归结为创建自定义Spring范围,将具有prototype
范围的Bean注入到具有proxyMode = ScopedProxyMode.TARGET_CLASS
的单例中,但仍在配置的Java配置版本中获得单例(尽管它在XML上工作正常).
TL/DR: The problem boils down to creating a custom Spring scope, injecting a prototype
-like scoped bean into a singleton with proxyMode = ScopedProxyMode.TARGET_CLASS
but still getting a singleton in the Java config version of the configuration (whereas it works fine with XML).
更新:问题已解决,请参见答案.
UPDATE: Problem solved, see answer.
我正在使用jBehave为我们的Spring应用程序编写BDD测试方案.我们最近认为在执行测试方案时需要独立性(这意味着必须在每个方案之前重置测试上下文),并发现
I'm using jBehave to write BDD test scenarios for our Spring application. We recently thought that we need independence in executing test scenarios (meaning that test context has to be reset before each scenario) and found this article on the web that addresses exactly the issue we're dealing with.
本文建议创建一个自定义Spring Scenario
范围,将其分配给表示测试上下文的类,并注入AOP代理而不是上下文文件.
The article advises creating a custom Spring Scenario
scope, assigning it to the class that represents test context and injecting an AOP proxy instead of the context file.
我已经按照文章对所有内容进行了编码,并且效果很好,但是问题是我们需要Java配置而不是XML的方式,当我将所有更改都转换为Java config时,它就停止了工作-这意味着在每个测试方案之后,StoryContext
中的Map
都没有重置,并且包含先前方案中的值.
I've coded everything in accordance with the article and it worked great, but the thing is we need it in terms of Java config, not XML, and when I converted all the changes to Java config, it stopped working - meaning the Map
in StoryContext
was not reset after each test scenario and contained values from the previous scenario.
我的更改如下:
- 用
@Component
批注标记ScenarioScope
类:
- marked the
ScenarioScope
class with the@Component
annotation:
@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
类进行批注: - annotated the
StoryContext
class with the@Component
and@Scope
annotations:
@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配置,如下所示:
To my knowledge, the code above is analogous to the XML configuration, which was as follows:
<?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,但是大多数类似的问题都是通过在@Scope
注释中添加proxyMode = ScopedProxyMode.TARGET_CLASS
来解决的.
Can anyone please point me to why the Java config is not working as expected? I've spent some time researching stackoverflow but the majority of similar questions is solved by adding proxyMode = ScopedProxyMode.TARGET_CLASS
to the @Scope
annotation, which I did.
更新:因此,我尝试通过注释/分解文件中的相应行来逐步从XML迁移到Java配置,并发现问题出在代码的这一部分:
UPDATE: So I tried to gradually move from XML to Java config by commenting / decommenting corresponding lines in the files and figured out that the problem is in this part of the code:
<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
并按照在这里,但是它也不起作用.
the StoryContext
bean becomes a singleton. I tried doing it another way through registering a custom BeanFactoryPostProcessor
and using the registerScope()
method as described here, but it didn't work either.
推荐答案
我已经设法解决了这个问题,而解决方案却微不足道:SpringConfiguration
类中的ScenarioScope
实例必须由Spring管理.容器,而不是通过new()
运算符创建:
I've managed to solve the problem, and the solution was trivial: the ScenarioScope
instance in the SpringConfiguration
class has to be managed by the Spring container rather than be created via the new()
operator:
@Configuration
public class SpringConfiguration {
@Bean
public static CustomScopeConfigurer scopeConfigurer(ScenarioScope scenarioScope) {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", scenarioScope);
return configurer;
}
}
这篇关于XML配置的Java配置类似物不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!