【问题标题】:A Java config analog of XML configuration not workingXML 配置的 Java 配置模拟不起作用
【发布时间】: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


    【解决方案1】:

    我已经设法解决了这个问题,而且解决方案很简单:SpringConfiguration 类中的 ScenarioScope 实例必须由 Spring 容器管理,而不是通过 new() 运算符创建:

    @Configuration
    public class SpringConfiguration {
    
        @Bean
        public static CustomScopeConfigurer scopeConfigurer(ScenarioScope scenarioScope) {
            CustomScopeConfigurer configurer = new CustomScopeConfigurer();
            configurer.addScope("scenario", scenarioScope);
            return configurer;
        }
    }
    

    【讨论】:

    • 我认为您可以接受自己的答案以结束问题。
    • @kriegaex,当然,我只需要再等三个小时就可以了。 :)
    猜你喜欢
    • 1970-01-01
    • 2012-11-05
    • 2018-06-07
    • 2023-03-21
    • 1970-01-01
    • 2017-07-23
    • 1970-01-01
    • 1970-01-01
    • 2016-10-24
    相关资源
    最近更新 更多