1

I have a big project with SpringBoot, I have a lot of @Serviceand, inside these, I have other @Autowired service as dependencies.

Now, to reduce code verbosity, I have annotated every service class with @RequiredArgsConstructor(onConstructor = @__(@Autowired)) and put all nested service private. For Example:

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DomandaDocumentoService {

private final DomandaDocumentoRepository domandaDocumentoRepository;

private final DomandaDatoBeneficiarioService domandaDatoBeneficiarioService;
private final DomandaDatiGeneraliService domandaDatiGeneraliService;
private final DomandaOperazioneService domandaOperazioneService;
private final ParametroValoreService parametroValoreService;
private final SecurityService securityService;

private final DomandaDocumentoMapper domandaDocumentoMapper;

...
...  // other method

Now wen launching my Tomcat (for test) server I obtain the follow error:

The dependencies of some of the beans in the application context form a cycle:
...
... // some inputated classes

How can I solve this?

----------------------- UPDATE ------------------

Predominantly the question is: why I can do this:

@Service
class A {
  @Autowired
  private B b;
}

@Service
class B {
  @Autowired
  private A a;
}

And cannot do this:

@Service
@RequiredArgsConstructor(onConstructor_={@Autowired})
class A {
  private final B b;
}

@Service
@RequiredArgsConstructor(onConstructor_={@Autowired})
class B {
  private final A a;
}

Thanks.

Constantin Beer
  • 5,447
  • 6
  • 25
  • 43
CoderJammer
  • 599
  • 3
  • 8
  • 27

1 Answers1

1

This question is not directly connected to lombok, but rather why circular dependency is problematic when you use constructor injection and why is not so problematic with field injection, or in other words why can you do this:

@Service
public class A {
  @Autowired
  private B b;
}

@Service
public class B {
  @Autowired
  private A a;
}

and not this

@Service
public class A {
  private B b;
  
  public A(B b) {
    this.b=b;
  }
}

@Service
public class B {
  private A a;

  public B(A a) {
    this.a=a;
  }
}

From Spring docs:

...a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario)...One possible solution is to edit the source code of some classes to be configured by setters rather than constructors.

So to work around your issue Spring documentation suggests that you use setter (or field) injection for beans that cause circular dependency. You don't have to do that for both sides, something like this should be enough:

@Service
public class A {
  @Autowired
  private B b;
}

@Service
@RequiredArgsConstructor(onConstructor_={@Autowired})
public class B {
  private final A a;
}

I don't think that you'll have that many circular dependent beans so I'd set the field injection for only those that you'll see in error logs (again, not all of them, but just some to break the cycle of constuctor injected beans).

As a side note, when beans form a cycle, it could be a hint that there is something fishy in the code and that it perhaps should be refactored.

Pistolnikus
  • 161
  • 1
  • 5