Kartoffelsup Kartoffelsup - 6 months ago 72
Java Question

Spring Bean Dependency Missing but Log and Debug state it was instantiated

I am using Spring Boot 1.3.4.RELEASE with Spring 4.2.6.RELEASE.

I have the following class:

@Component
class ServerRunner implements Runnable {


With Constructor:

@Autowired
ServerRunner(@Value(HEARTBEAT_SECONDS) final long secondsBetweenHeartbeats,
final ServerSocket socket,
final PlayerRepository playerRepository,
final DeckProcessor deckProcessor) {
this.serverSocket = socket;
this.maintainer = new Maintainer(secondsBetweenHeartbeats);
this.playerRepository = playerRepository;
final ExecutorService executorService = newCachedThreadPool();
executorService.execute(this);
this.running = true;

this.deckProcessor = deckProcessor;
executorService.execute(maintainer);
}


When I start my Application, it can not find the Dependency
DeckProcessor
.

@Component
class DeckProcessor extends AbstractPlayerProcessor {
private final DeckRepository repository;

@Autowired(required = true)
DeckProcessor(final DeckRepository repository) {
super(QUERY_DECKS);
this.repository = repository;
}


The following Exception is thrown:

23:12:42 ERROR org.springframework.boot.SpringApplication - Application startup failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'serverRunner' defined in file [GameServer\arml-server\build\classes\main\com\arml\services\ServerRunner.class]: Unsatisfied dependency expressed through constructor argument with index 3 of type [com.arml.services.DeckProcessor]: No qualifying bean of type [com.arml.services.DeckProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.arml.services.DeckProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) ~[spring-boot-1.3.4.RELEASE.jar:1.3.4.RELEASE]
at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) [spring-boot-1.3.4.RELEASE.jar:1.3.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-1.3.4.RELEASE.jar:1.3.4.RELEASE]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134) [spring-boot-1.3.4.RELEASE.jar:1.3.4.RELEASE]
at com.arml.ArmlServer.main(ArmlServer.java:21) [main/:?]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.arml.services.DeckProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
... 17 more


But the Log clearly states:

16:44:05 DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean deckProcessor
16:44:05 DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean deckProcessor
16:44:05 DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'deckProcessor' via constructor to bean named 'deckRepository'
16:44:05 DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'deckProcessor' to allow for resolving potential circular references
16:44:05 DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'deckProcessor'


Both classes reside in the same package.

What am I doing wrong? I've been debugging for hours and can not find a solution.

SpringBoot App:

@EnableJpaRepositories(basePackages = "com.arml.persistence.repositories")
@EnableTransactionManagement
@SpringBootApplication
public class ArmlServer {

public static void main(final String[] args) {
new SpringApplicationBuilder()
.web(false)
.bannerMode(OFF)
.sources(ArmlServer.class)
.run(args);
}
}


Configuration:

@Configuration
public class ArmlConfiguration {
private static final Base64.Decoder DECODER = getDecoder();

@Bean
public ServerSocket armlSslSocket(@Value("${arml.server.bind-address}") final String bindAddress,
@Value("${arml.server.port}") final int port,
@Value("${arml.ssl.password}") final String password)
throws IOException {
final String keyPass = new String(DECODER.decode(password), UTF_8);
return createServerSocket(bindAddress, port, keyPass);
}

@Bean
public DataSource dataSource(final DataSourceProperties dataSourceProperties)
throws PropertyVetoException {
final ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(dataSourceProperties.getDriverClassName());
dataSource.setUser(dataSourceProperties.getUsername());
dataSource.setPassword(new String(DECODER.decode(dataSourceProperties.getPassword()), UTF_8));
dataSource.setJdbcUrl(dataSourceProperties.getUrl());
return dataSource;
}
}


Edit:
Project Structure

Edit2: I did try the suggestions by @Gemini Keith. The
ServerRunner
now looks like like this (I've removed some constants/methods for brevity):

@Component
@DependsOn("deckProcessor")
class ServerRunner implements Runnable {

@Value(HEARTBEAT_SECONDS)
private long secondsBetweenHeartbeats;
private Maintainer maintainer;
@Autowired
private ServerSocket serverSocket;
@Autowired
private PlayerRepository playerRepository;
@Autowired
private DeckProcessor deckProcessor;

ServerRunner() {
}

@PostConstruct
void init() {
this.maintainer = new Maintainer(secondsBetweenHeartbeats);
final ExecutorService executorService = newCachedThreadPool();
executorService.execute(this);
this.running = true;

executorService.execute(maintainer);
}


But the exception remains:

07:12:36 ERROR org.springframework.boot.SpringApplication - Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverRunner': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.arml.services.DeckProcessor com.arml.services.ServerRunner.deckProcessor; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.arml.services.DeckProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) ~[spring-boot-1.3.4.RELEASE.jar:1.3.4.RELEASE]
at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) [spring-boot-1.3.4.RELEASE.jar:1.3.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-1.3.4.RELEASE.jar:1.3.4.RELEASE]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134) [spring-boot-1.3.4.RELEASE.jar:1.3.4.RELEASE]
at com.arml.ArmlServer.main(ArmlServer.java:20) [main/:?]
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.arml.services.DeckProcessor com.arml.services.ServerRunner.deckProcessor; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.arml.services.DeckProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
... 15 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.arml.services.DeckProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
... 15 more


I also rechecked the Annotations and they are:

import org.springframework.stereotype.Component;


The Constructor injection works once I remove the
deckProcessor
. This is really confusing!

Edit3:
I'm an idiot. The parameter should've had the interface type. After changing the constructor to:

@Autowired
ServerRunner(@Value(HEARTBEAT_SECONDS) final long secondsBetweenHeartbeats,
final ServerSocket armlSslSocket,
final PlayerRepository playerRepository,
final Processor<ServerClientHandler> deckProcessor) {
this.serverSocket = armlSslSocket;
this.maintainer = new Maintainer(secondsBetweenHeartbeats);
this.playerRepository = playerRepository;
final ExecutorService executorService = newCachedThreadPool();
executorService.execute(this);
this.running = true;

this.deckProcessor = deckProcessor;
executorService.execute(maintainer);
}


It works fine.

Answer

It seems like a initialization dependency problem. Please try to use @DependsOn together with @Component on class ServerRunner as following:

@DependsOn(value = "deckProcessor")
@Component

value means the bean name of DeckProcessor, which is always deckProcessor by default. However, you can name it by specifying the name at @Component(value = 'nameOfYourBean').

In your case, your constructor need autowire a DeckProcessor, which is too strict for instantiate a ServerRunner bean, which means a DeckProcessor bean must be there when a ServerRunner is being created. Otherwise, the ServerRunner could not be instantiated. However, you can initial the ServerRunner by create a @PostConstruct method and move the code from constructor into it. Such as:

@Value(HEARTBEAT_SECONDS)
long secondsBetweenHeartbeats;
@Autowired
ServerSocket socket;
@Autowired
PlayerRepository playerRepository;
@Autowired
DeckProcessor deckProcessor;
@PostConstruct
public void init() {
  this.serverSocket = socket;
  this.maintainer = new Maintainer(secondsBetweenHeartbeats);
  this.playerRepository = playerRepository;
  final ExecutorService executorService = newCachedThreadPool();
  executorService.execute(this);
  this.running = true;

  this.deckProcessor = deckProcessor;
  executorService.execute(maintainer);
}

At this point, spring can instantiate all beans and later continue initialize them one by one. It should work.

Comments