Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring: circular dependencies on constructor\setter injection

I've read that circular dependencies are caused by setter injection. So I tried to check it by my own. And it appears that I can reproduce circular dependency only with constructor injection (see code below).

So the questions:

  1. Can I accomplish circular dependency with setter injection?
  2. How to resolve circular dependencies at code below?

    public class AConstr {
    
      private final BConstr b;
    
      public AConstr(BConstr bConstr) {
        System.out.println("AConstructor:: constructor");
        this.b = bConstr;
      }
    }
    
    public class BConstr {
    
      private final AConstr a;
    
      public BConstr(AConstr aConstr) {
        System.out.println("BConstructor:: constructor");
        this.a = aConstr;
      }
    }
    
    <bean id="aConstr" class="pack.bean.AConstr">
       <constructor-arg ref="bConstr"/>
    </bean>
    <bean id="bConstr" class="pack.bean.BConstr">
       <constructor-arg ref="aConstr"/>
    </bean>
    
like image 355
VB_ Avatar asked Jan 17 '14 09:01

VB_


People also ask

Does constructor injection prevent circular dependencies?

Circular dependency in Spring happens when two or more beans require instance of each other through constructor dependency injections. For example: There is a ClassA that requires an instance of ClassB through constructor injection and ClassB requires an instance of class A through constructor injection.

How do I resolve circular dependencies in Spring?

A simple way to break the cycle is by telling Spring to initialize one of the beans lazily. So, instead of fully initializing the bean, it will create a proxy to inject it into the other bean. The injected bean will only be fully created when it's first needed.

How do you avoid circular dependencies?

To reduce or eliminate circular dependencies, architects must implement loose component coupling and isolate failures. One approach is to use abstraction to break the dependency chain. To do this, you introduce an abstracted service interface that delivers underlying functionality without direct component coupling.


1 Answers

There are 2 ways of injecting dependencies with Spring @Autowired: by field or by constructor.

If you have the following layers:

  • RestController
  • Service
  • JpaRepository
  • Entity

With the following bindings:

  • 1 RestController is bound to the associated Service only
  • 1 Service only use the associated JpaRepositories to manage the associated Entities
  • To interact with Entities which are out of the service's functional scope, a Service uses other Services.
FooController       BarController
+                   +
|                   |
+                   +
FooService<-------->BarService
+                   +
|                   |
+                   +
FooJpaRepository    BarJpaRepository
+                   +
|                   |
+                   +
FooEntity           BarEntity

Thus, it's normal that 2 Services can depend from each other (directly or not), and that's why you can break the circular dependency:

  • with @Autowired on fields instead of on constructor in one of your Service
  • Or just adding @Lazy before a constructor parameter.

here is what Spring suggests: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-dependencies

On the other hand, a Service (or directly a JpaRepository) should be injected to RestControllers by constructor. In that case a circular dependency would be an architecture problem.

like image 144
Pleymor Avatar answered Sep 22 '22 09:09

Pleymor