Чтобы иметь возможность проверить некоторые аспекты моего приложения, я создал эту настройку теста. Так как мне нужна пользовательская реализация ApplicationContext
, это не может быть сделано с классической аннотацией SpringBootTest
, вместо этого у меня есть это initialisazion:
public class CityWallSerializationTest extends SaveLoadBase<CityWall> {
private DependentAnnotationConfigApplicationContext context;
@BeforeEach
private void initialize() {
SpringApplicationBuilder builder = new SpringApplicationBuilder(SavegameTestApplication.class);
context = (DependentAnnotationConfigApplicationContext) builder.contextClass(DependentAnnotationConfigApplicationContext.class).profiles("server").run();
}
}
Приложение SavegameTestApplication, к которому обращаются:
@SpringBootApplication
@Import({ServerTestConfiguration.class})
@PropertySource(value = {"application.properties", "server.properties"})
public class SavegameTestApplication {
}
ServerTestConfiguration
- это конфигурация, которая объединяет определенную конфигурацию и требуемые bean-компоненты:
@Configuration
@ActiveProfiles("server")
@Import(value = {ClientServerInterfaceServerConfiguration.class, ServerConfiguration.class, ImageConfiguration.class})
public class ServerTestConfiguration {
@Bean
public Jaxb2Marshaller jaxb2MarshallerImage() {
Jaxb2Marshaller bean = new Jaxb2Marshaller();
bean.setContextPath("ch.sahits.game.graphic.data.image");
return bean;
}
@Bean
public ITextSizing textSizing() {
return new ITextSizing() {
@Override
public Dimension2D calculate(int size, Font font) {
return null;
}
@Override
public Dimension2D calculate(String text, Font font) {
return null;
}
};
}
@Bean
public IEventPropagator serverEventPropagator() {
return message -> log.info("Propagate targeted event {}", message);
}
@Bean
public TestableLoadAndSaveService testableLoadAndSaveService() {
return new TestableLoadAndSaveService();
}
}
Затем ссылка ClientServerInterfaceServerConfiguration
, где два bean-компонента подключаются автоматически:
@Configuration
@Profile("server")
@Import({ClientServerInterfaceCommonConfiguration.class})
@ClassCategory(EClassCategory.STARTUP)
public class ClientServerInterfaceServerConfiguration {
@Autowired
@Qualifier("serverThreadPool")
private Executor serverThreadPool;
@Autowired
private SubscriptionLoggingExceptionHandler subscriptionExceptionHandler;
@Bean
public PausableAsyncEventBus clientServerEventBus() {
return new PausableAsyncEventBus(serverThreadPool, subscriptionExceptionHandler);
}
@Bean
public PausableSyncEventBus syncServerClientEventBus() {
return new PausableSyncEventBus(subscriptionExceptionHandler);
}
@Bean
public AsyncEventBus timerEventBus() {
return new AsyncEventBus(serverThreadPool, subscriptionExceptionHandler);
}
}
Два bean-компонента, которые автоматически подключены, определены в ClientServerInterfaceCommonConfiguration
, который импортируется. Когда я запускаю тест с -Dlogging.level.org.springframework=DEBUG
, я получаю этот вывод журнала для фактически обнаруженных bean-компонентов (по крайней мере, в начале вывода большая часть опускается, поскольку она не имеет никакого отношения):
2019-12-11 17:07:52,487 [main] INFO c.i.r.j.JUnitStarter : The following profiles are active: server
2019-12-11 17:07:52,487 [main] DEBUG o.s.b.SpringApplication : Loading source class ch.sahits.game.savegame.SavegameTestApplication
2019-12-11 17:07:52,517 [main] DEBUG o.s.b.c.c.ConfigFileApplicationListener : Loaded config file 'file:/home/andi/development/intellij/OpenPatrician/OpenPatricianTest/target/classes/application.properties' (classpath:/application.properties)
2019-12-11 17:07:52,583 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianTest/target/test-classes/ch/sahits/game/savegame/ServerTestConfiguration.class]
2019-12-11 17:07:52,608 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/ClientServerInterfaceServerConfiguration.class]
2019-12-11 17:07:52,611 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/event/BuyWares.class]
2019-12-11 17:07:52,612 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/event/PostponedDisplayDialogMessage.class]
2019-12-11 17:07:52,613 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/event/PostponedDisplayMessage.class]
2019-12-11 17:07:52,615 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/logging/EventBusAspect.class]
2019-12-11 17:07:52,617 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/PathInterpolatorMap.class]
2019-12-11 17:07:52,624 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/AIStrategyLoader.class]
2019-12-11 17:07:52,625 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/BuildingFactory.class]
2019-12-11 17:07:52,626 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/CityFactory.class]
2019-12-11 17:07:52,627 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/GameFactory.class]
2019-12-11 17:07:52,627 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/MapSegmentImageFactory.class]
2019-12-11 17:07:52,628 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/PeopleFactory.class]
2019-12-11 17:07:52,628 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/PlayerInteractionFactory.class]
2019-12-11 17:07:52,628 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/ShipFactory.class]
2019-12-11 17:07:52,629 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/factory/StateFactory.class]
2019-12-11 17:07:52,629 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/task/CelebrationTask.class]
2019-12-11 17:07:52,630 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/task/MarriageTask.class]
2019-12-11 17:07:52,630 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/task/ReschedulableSailorHireTask.class]
2019-12-11 17:07:52,630 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/model/task/ReschedulableWeaponBuyTask.class]
2019-12-11 17:07:52,631 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/BuildingProduction.class]
2019-12-11 17:07:52,631 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/CelebrationService.class]
2019-12-11 17:07:52,632 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/CityProductionAndConsumptionService.class]
2019-12-11 17:07:52,632 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/ClientServerFactory.class]
2019-12-11 17:07:52,632 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/ClientServerTaskFactory.class]
2019-12-11 17:07:52,633 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/ConvoyService.class]
2019-12-11 17:07:52,633 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/DateService.class]
2019-12-11 17:07:52,634 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/DialogTemplateFactory.class]
2019-12-11 17:07:52,634 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/DifficultyPropertyInitializer.class]
2019-12-11 17:07:52,635 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/FormattedDateSupplier.class]
2019-12-11 17:07:52,635 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/GuildService.class]
2019-12-11 17:07:52,636 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/LinearDeadlinePremiumCalculator.class]
2019-12-11 17:07:52,636 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/LoanerService.class]
2019-12-11 17:07:52,636 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/MapProxy.class]
2019-12-11 17:07:52,637 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/MapService.class]
2019-12-11 17:07:52,637 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/ModelStateAccessor.class]
2019-12-11 17:07:52,637 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/OutriggerService.class]
2019-12-11 17:07:52,638 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/PlayerProductionService.class]
2019-12-11 17:07:52,638 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/PointService.class]
2019-12-11 17:07:52,639 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/ShipService.class]
2019-12-11 17:07:52,639 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/StrategyHolderService.class]
2019-12-11 17:07:52,640 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/service/TradeService.class]
2019-12-11 17:07:52,661 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner : Identified candidate component class: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/ClientServerInterfaceServerConfiguration.class]
Особая вещь, которую я не могу понять: почему ClientServerInterfaceServerConfiguration
дважды в списке, но ClientServerInterfaceCommonConfiguration
отсутствует вообще?
Это, конечно, заканчивается тем, что контекст приложения не может быть построен:
Вызывается: org.springframework.beans.factory.NoSuchBeanDefinitionException: нет квалифицированного компонента типа 'java.util.concurrent.Executor': ожидается, что по крайней мере 1 компонент будет квалифицирован как кандидат для автоматической передачи. Аннотации зависимостей: {@ org.springframework.beans.factory.annotation.Autowired (обязательно = true), @ org.springframework.beans.factory.annotation.Qualifier (value = "serverThreadPool")}
ОБНОВЛЕНИЕ: При работе с журналированием трассировки есть еще несколько интересных моментов:
2019-12-12 12:58:53,469 [main] TRACE o.s.c.a.ClassPathBeanDefinitionScanner : Scanning file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/ClientServerInterfaceCommonConfiguration.class]
2019-12-12 12:58:53,470 [main] TRACE o.s.c.a.ClassPathBeanDefinitionScanner : Ignored because not matching any filter: file [/home/andi/development/intellij/OpenPatrician/OpenPatricianClientServerInterface/target/classes/ch/sahits/game/openpatrician/clientserverinterface/ClientServerInterfaceCommonConfiguration.class]
Это действительно нормально, поскольку класс конфигурации игнорируется при сканировании компонента, так как он импортируется.
2019-12-12 13:00:04,484 [main] TRACE o.s.c.a.ConfigurationClassBeanDefinitionReader : Registered bean definition for imported class 'ch.sahits.game.openpatrician.clientserverinterface.ClientServerInterfaceCommonConfiguration'
2019-12-12 13:00:04,484 [main] TRACE o.s.c.a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method ch.sahits.game.openpatrician.clientserverinterface.ClientServerInterfaceCommonConfiguration.serverThreadPool()
2019-12-12 13:00:04,484 [main] TRACE o.s.c.a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method ch.sahits.game.openpatrician.clientserverinterface.ClientServerInterfaceCommonConfiguration.subscriptionExceptionHandler()
Конфигурация фактически обработана, и компоненты зарегистрированы (по крайней мере, с помощью считывателя определений). Фактическое исключение происходит здесь:
2019-12-12 13:00:09,516 [main] TRACE o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'serverConfiguration'
2019-12-12 13:00:09,516 [main] TRACE o.s.b.f.a.InjectionMetadata : Registered injected element on class [ch.sahits.game.openpatrician.server.ServerConfiguration$$EnhancerBySpringCGLIB$$b77d1330]: AutowiredFieldElement for private java.util.concurrent.Executor ch.sahits.game.openpatrician.server.ServerConfiguration.serverThreadPool
2019-12-12 13:00:09,516 [main] TRACE o.s.b.f.a.InjectionMetadata : Registered injected element on class [ch.sahits.game.openpatrician.server.ServerConfiguration$$EnhancerBySpringCGLIB$$b77d1330]: AutowiredFieldElement for private ch.sahits.game.openpatrician.clientserverinterface.event.SubscriptionLoggingExceptionHandler ch.sahits.game.openpatrician.server.ServerConfiguration.subscriptionExceptionHandler
2019-12-12 13:00:09,516 [main] TRACE o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'serverConfiguration' to allow for resolving potential circular references
2019-12-12 13:00:09,516 [main] TRACE o.s.b.CachedIntrospectionResults : Getting BeanInfo for class [ch.sahits.game.openpatrician.server.ServerConfiguration$$EnhancerBySpringCGLIB$$b77d1330]
2019-12-12 13:00:09,517 [main] TRACE o.s.b.CachedIntrospectionResults : Caching PropertyDescriptors for class [ch.sahits.game.openpatrician.server.ServerConfiguration$$EnhancerBySpringCGLIB$$b77d1330]
2019-12-12 13:00:09,517 [main] TRACE o.s.b.CachedIntrospectionResults : Found bean property 'beanFactory' of type [org.springframework.beans.factory.BeanFactory]
2019-12-12 13:00:09,517 [main] TRACE o.s.b.CachedIntrospectionResults : Found bean property 'class' of type [java.lang.Class]
2019-12-12 13:00:09,519 [main] TRACE o.s.b.f.a.InjectionMetadata : Processing injected element of bean 'serverConfiguration': AutowiredFieldElement for private java.util.concurrent.Executor ch.sahits.game.openpatrician.server.ServerConfiguration.serverThreadPool
2019-12-12 13:00:09,520 [main] TRACE o.s.b.TypeConverterDelegate : Converting String to [class java.lang.Class] using property editor [org.springframework.beans.propertyeditors.ClassEditor@538e74fc]
2019-12-12 13:00:09,520 [main] TRACE o.s.b.TypeConverterDelegate : Converting String to [class java.lang.Class] using property editor [org.springframework.beans.propertyeditors.ClassEditor@6475e778]
2019-12-12 13:00:09,520 [main] TRACE o.s.b.TypeConverterDelegate : Converting String to [class java.lang.Class] using property editor [org.springframework.beans.propertyeditors.ClassEditor@55b62db8]
2019-12-12 13:00:09,521 [main] WARN o.s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'serverConfiguration': Unsatisfied dependency expressed through field 'serverThreadPool'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.concurrent.Executor' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value="serverThreadPool")}
Таким образом, исключение не происходит при попытке вставить serverThreadPool
в ClientServerInterfaceServerConfiguration
, который импортирует ClientServerInterfaceCommonConfiguration
, но при попытке внедренияэто в ServerConfiguration
, который не импортирует ClientServerInterfaceCommonConfiguration
. Добавление его в ServerConfiguration
решает эту проблему, но остается вопрос: почему существует проблема с внедрением bean-компонента в ServerConfiguration
, который определен в другом Configuration
, когда оба класса конфигурации импортируются в одном и том же «родительском»Конфигурация?