In my project I'm using the observer pattern in several places, i.e. the subject notifies the observers about something, and expect them to act. The subject does not know anything about the details of these observers.
With Spring I autowired/injected the observers as follows (constructor injection):
@Autowired public Subject(List<Observer> observers) {...}
This works, and as Observer
is an interface, there's no compile-time dependency from Subject
to Observer
. However, there needs to be a runtime dependency, so that the observers can be notified by the subject.
With the approach shown above, I experienced bean dependency cycle issues, as one of the observer's transitive dependencies was the subject itself.
To fix this, I introduced a new class SubjectInitializer
(and changed the Subject
accordingly):
@Autowired
public SubjectInitializer(Subject subject, List<Observer> observers) {
subject.addObservers(observers);
}
This way I don't have any bean dependency cycle, as SubjectInitializer
is not the target of any dependency.
However, this fix seems weird to me:
- The observer pattern is used quite a lot, so I guess there's a lot of experience on how to use it with Spring. However, I didn't find anything helpful.
- The whole purpose of the
SubjectInitializer
is to initialize theSubject
, which only has to happen once. Creating a new bean (and having the singleton in memory?) seems over the top.
Is there a better way to autowire the observer instances?
3 Answers 3
What you’re looking at is a chicken and egg situation. You can’t have your cake and eat it too in this case.
If A needs B and B needs A at construction time you’re basically stuck. Haven’t used Spring in ages, but if it is indeed supported it’s almost certainly implemented through property injection and/or reflection or other dynamic approach behind the scenes.
If you’re concerned about the purity/fragility of the design, it’s worth looking into method injection. That is, maybe the dependency is only needed to perform one or two specific functions. If that’s the case, as opposed to the dependency playing a more central role and needed in many places, then one could argue that method injection is in fact better since it’s only being brought in when needed as a parameter and not as part of the object that needs it itself.
The problem is that you wanted to use constructors. You can’t create cyclic dependencies with constructors, which normally, is a good thing.
What you need is a setter so that after you build A without B and build B with A you can then introduce B to A. You don’t need to invent a C.
This problem has nothing to do with spring. It’s a constructor thing.
-
I prefer constructor injection over field/setter injection so that it is impossible to have cyclic dependencies. Why do you think it is a good idea to relax this requirement? I know that non-constructor injection (or maybe something like
@Lazy
) would help.C-Otto– C-Otto05/10/2020 15:44:07Commented May 10, 2020 at 15:44 -
I do too. But you physically can’t create cycles with constructors. It’s one of the reasons I like constructors. I’m not questioning the constructor. I’m questioning the cycle. You can’t be immutable with this stupid setter either. But none of this is fixed by adding a C.candied_orange– candied_orange05/10/2020 15:50:10Commented May 10, 2020 at 15:50
-
1It’s you observer with the "transitive dependency on the subject" that’s spoiling your immutable fun.candied_orange– candied_orange05/10/2020 15:54:36Commented May 10, 2020 at 15:54
-
The observer has good reasons to have the transitive dependency (example: is notified about event "X for 123" and needs to create an event "X for 456"). However, the code that creates those events (the subject) does not need to know anything about the observers. So, no, I don't think the transitive dependency on the subject is the issue, although of course every edge on the cycle is part of the problem.C-Otto– C-Otto05/10/2020 19:20:04Commented May 10, 2020 at 19:20
-
@C-Otto it is the issue if you want construction code to establish it. Setter injection can but constructors can’t.candied_orange– candied_orange05/10/2020 20:03:41Commented May 10, 2020 at 20:03
As this Observer depends on the Subject, the best seems to invert the setup of observable pattern: this Observer takes the subject in its constructor, then let it add itself as observer:
subject.addObserver(this)
This way, there's no dependency circle, both dependencies go the same way.
Explore related questions
See similar questions with these tags.
@Inject
in JavaEE environments. And yes, each observer may be provided to multiple subjects:EmitsSomething
could be a concreteSubject
, andY implements ReceivesSomething, ReceivesSomethingElse
might be an observer for this subject that also observes notifications from another subject.