Announcement Announcement Module
Collapse
No announcement yet.
Circular dependency identification inconsistent? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Circular dependency identification inconsistent?

    Say that I have the following classes:

    Code:
    package org.springframework.test;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ServiceConfig {
        @Autowired
        private ServiceConsumer consumer;
    
        @Bean
        public String serviceUrl() {
            return "http://www.google.com";
        }
    }
    Code:
    package org.springframework.test;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ServiceConsumer {
        @Autowired
        private ServiceHitter hitter;
    
        public ServiceHitter getServiceHitter() {
            return hitter;
        }
    }
    Code:
    package org.springframework.test;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ServiceHitter {
        private final String serviceUrl;
    
        @Autowired
        public ServiceHitter(String serviceUrl) {
            this.serviceUrl = serviceUrl;
        }
    
        public String getServiceUrl() {
            return serviceUrl;
        }
    }
    With this kind of a setup, there is technically a circular dependency:
    • ServiceHitter is dependent on a bean named "serviceUrl"
    • "serviceUrl" is provided by the ServiceConfig object, which is dependent on the ServiceConsumer bean
    • The ServiceConsumer bean is dependent on the ServiceHitter object

    Thus, through the "serviceUrl" bean, the chain is completely circular (even though ServiceHitter is not directly dependent on ServiceConfig). This kind of setup would cause Spring to throw an exception about circular dependencies - but only sometimes. My investigation seems to indicate that this is a result in which Spring discovers the beans and tries to initialize them.

    In the situations where the build works, the beans are discovered and initialized in the following order:
    • ServiceConfig
    • ServiceConsumer
    • ServiceHitter

    The positions of ServiceHitter and ServiceConsumer are interchangeable; the important part is that ServiceConfig is initialized before ServiceHitter. When Spring tries to initialize the beans, it discovers the "serviceUrl" bean and, when autowiring ServiceHitter, it finds the previously-discovered "serviceUrl" bean and wires it in, noting in the logs that ServiceConfig isn't fully initialized and warning of a possible circular dependency.

    In the case of builds that failed, however, the bean wiring order looked something more like the following:
    • ServiceConsumer
    • ServiceHitter
    • ServiceConfig

    As before, ServiceConsumer and ServiceHitter are interchangeable in their positions; the important part is that ServiceConfig is wired after ServiceHitter. In this case, ServiceHitter is discovered that it needs the "serviceUrl" bean; unlike before, though, the ServiceConfig bean is not initialized yet. Spring, because we didn't annotate ServiceConfig with @Lazy, begins trying to initialize the entire bean when looking for the "serviceUrl" bean, sees that ServiceHitter is already in the current queue of beans being created, and throws the aforementioned exception.

    The honus is definitely on us to solve this issue of a circular dependency, but I was wondering if Spring should be a bit more deterministic in its ordering of bean creation. The root of this ordering seems to be found in the usage of File.listFiles() in org.springframework.core.io.support.PathMatchingRe sourcePatternResolver.doRetrieveMatchingFiles(); the ordering of this list is non-deterministic, and, as I found between machines on which our build succeeded and machines on which our build failed, the ordering of the list of files and directories returned by File.listFiles() corresponded directly to the order in which beans were initialized.

    Phew. So, tl;dr: should we be doing something different to ensure consistent behavior of bean wiring across machines, or is there room for an enhancement to Spring's IoC container to be more deterministic about this order of wiring?
Working...
X