Конфигурация Java аналог конфигурации XML не работает - PullRequest
0 голосов
/ 15 октября 2019

TL / DR: проблема сводится к созданию настраиваемой области Spring, внедряющей bean-объект, подобный prototype, в синглтон с proxyMode = ScopedProxyMode.TARGET_CLASS, но все еще получающий синглтон в версии конфигурации Java для конфигурации (тогда какотлично работает с XML).

ОБНОВЛЕНИЕ: Проблема решена, см. ответ.


Я использую jBehave для написания тестовых сценариев BDD для нашего приложения Spring. Недавно мы подумали, что нам нужна независимость при выполнении тестовых сценариев (то есть контекст тестирования должен быть сброшен перед каждым сценарием), и нашли в Интернете эту статью, которая точно решает проблему, с которой мы имеем дело.

В статье рекомендуется создать настраиваемую область Spring Scenario, назначить ее классу, представляющему тестовый контекст, и внедрить прокси-сервер AOP вместо файла контекста.

Я кодировал все в соответствии со статьей, и это прекрасно работало, но дело в том, что нам это нужно с точки зрения конфигурации Java, а не XML, и когда я преобразовал все изменения в конфигурацию Java, он остановилсяработающий - то есть Map в StoryContext не сбрасывался после каждого тестового сценария и содержал значения из предыдущего сценария.

Мои изменения были следующими:

  • помечены *Класс 1024 * с аннотацией @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";
    }
}
  • создал класс конфигурации Spring для добавления новой области:
@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, комментируя / декомпозируя соответствующие строки в файлах, и выясняю, что проблема в этой части кода:

    <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 становится одноэлементным. Я попытался сделать это другим способом: зарегистрировать пользовательский BeanFactoryPostProcessor и использовать метод registerScope(), как описано здесь , но он тоже не сработал.

1 Ответ

0 голосов
/ 16 октября 2019

Мне удалось решить проблему, и решение было тривиальным: экземпляр 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;
    }
}
...