Why does Spring get circular dependency issues on

2020-05-19 07:12发布

I have a problem getting a Spring Data based application to run in my environment. I am running Debian, but my co-workers are either using Mac or Ubuntu. I have nothing special set up in my environment variables, and am using the exact same version of Java as others.

I have seen this in the logs, suggesting that it is a circular reference problem that is leading to the instantiation failure:

nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'flyway.CONFIGURATION_PROPERTIES':
Initialization of bean failed;
...
nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'flyway': Requested bean is currently in
creation: Is there an unresolvable circular reference?

So the problem appears to be that flyway needs some dependencies and they need flyway.

The question is, why does this only happen on my environment not anyone elses? Even on the tests using H2 in memory, I see the problem, so its not my database that is at fault.

Is it possible that Spring autowiring is confused somehow, and tries to do things in the wrong order, so that the repository is null when it tries to set it?

Does Spring have a badly implemented topological sort for ordering dependencies?

Why would it misbehave on my environment?

Could ordering of the classpath influence its behaviour?

======================

The application will not start with this error:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contentItemRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Repository interface must not be null on initialization!
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:127)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1127)

============================

The ContentItemRepository signature is:

@Repository
@Transactional
public interface ContentItemRepository extends JpaRepository<ContentItem, String>, JpaSpecificationExecutor<ContentItem> {

============================

This used to work for me, and I was able to identify the commit that broke the build, by iterating through all commits, doing a mvn clean install, and trying to start the server, until I found the delta that broke it.

The 'contentItemRepository' that cannot be null is this one:

@Component
+public class UrlAliasRequestConverter implements Mapper<UrlAliasRequest, UrlAlias> {
+
+    /**
+     * The content item contentItemType repository.
+     */
+    @Autowired
+    private ContentItemRepository contentItemRepository;

8条回答
爷、活的狠高调
2楼-- · 2020-05-19 07:15

I've the same issue on Ubuntu 16.04.

I found that the problem with

@ComponentScan(basePackages = "com.my.app")

The code is running at least 5 different machine (windows, ubuntu 15.04 and ubuntu 16.04 desktop) but doesn't start our CI server (ubuntu 16.04 server).

After I changed

@ComponentScan(basePackages = "com.my.app")

to

@ComponentScan(basePackages = {"com.my.app.service", "com.my.app.config", "com.my.app"})

the code is running on CI server too.

I think this is a Spring issue with beans loader...

UPDATE:

https://github.com/spring-projects/spring-boot/issues/6045

https://jira.spring.io/browse/SPR-14307

查看更多
不美不萌又怎样
3楼-- · 2020-05-19 07:19

We came across this problem today, in a nearly identical situation - application failing to start due to a circular reference, apparently while constructing Spring Data @Repository instances, and only on one developer's machine.

In our case the solution was to refactor our configuration so that an ApplicationListener<BeforeSaveEvent> was moved from being an anonymous class defined within a @Configuration class, and turned into a top-level @Component.

It seems that in some non-obvious way Spring Data repositories keep some sort of handle to these application listeners, and by having ours as an anonymous inner class the repository was implicitly maintaining a reference to its enclosing @Configuration (and thus a dependency loop being detected due to beans instantiated via that @Configuration which eventually autowired the repository).

All of the answers describing changing lazy init, changing your component scan order, reinstalling your OS, turning it off and on again etc. are only hiding rather than really addressing the problem, and I would suggest instead fixing it at its source :) The reason things work or don't work on various machines is just that, as @xki alludes to, the object graph construction order is non-deterministic.

查看更多
相关推荐>>
4楼-- · 2020-05-19 07:26

I don't understand why this happened, but here is the only solution that I came up with:

Install Debian 8, and it works.

I tried it in another clean install of Debian 7, and got less errors there, but still had some. A clean install of Debian 8 seemed to work.

I can only conclude that Java must be calling some system library, that in some way affects the order in which Spring dependencies are resolved. That library must be upgraded in Debian 8, bringing me in-line with the Ubuntu installs that other devs and production are using.

I don't know what that library might be... Something that scans the files in the filesystem, reporting them in a different order? Something that unpacks a .jar file, reporting its contents in a different order?

It seems wrong to me that our code is so sensitive to the exact order of dependency resolution and injection, but that would appear to be the case. It also does not look like there is anything in our code that should make it sensitive to the ordering, we're not doing anything crazy, and following pretty standard patterns of usage.

Too much Spring magic keeping the house of cards standing if you ask me. My other projects are on DropWizard, and there the dependency injection is manually coded up, so no surprises.

=== Update

I upgraded the Debian 7 box to 8, and the problem still persists. Therefore my hypothesis about it being library version is wrong. Must be something about my environment.

I created a new user on the box. The problem is still there for that user. There is something about this box it really does not like, but I cannot figure out what it is.

I'd like to get to the real cause and understand it, but I don't think I can really dedicate any more time to figuring it out.

Anyway, a clean install of Debian 8 solves the problem.

查看更多
Luminary・发光体
5楼-- · 2020-05-19 07:29

I got the same problem yesterday. I don't know why, but I found a way to solve the problem: mark all the beans involved in the circular dependency tree as lazy-init. Not only the ones directly circular dependent to each other, but all the beans that reference them!

The project is an old one thus it only uses spring 3. I am not sure whether the later versions of spring still get this problem.

查看更多
地球回转人心会变
6楼-- · 2020-05-19 07:30

This is very likely related to the order in which the class files are read in line

dir.listFiles() in PathMatchingResourcePatternResolver.doRetrieveMatchingFiles()

Since the order of file listing (class files) is dependent on the platform, and there is no sorting done on the array, the order the classes are loaded in is dependent on how the platform returns them.

ref: http://forum.spring.io/forum/spring-projects/container/115998-circular-dependency-identification-inconsistent

查看更多
Ridiculous、
7楼-- · 2020-05-19 07:36

What is your repository interface extending? You can look at the Spring source code and see why the exception is thrown:

https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java

@SuppressWarnings("unchecked")
public Class<? extends T> getObjectType() {
    return (Class<? extends T>) (null == repositoryInterface ? Repository.class : repositoryInterface);
}

Here's an example of my repository:

@Repository
public interface GameRepository extends JpaRepository<Game, Long> {
查看更多
登录 后发表回答