-
Notifications
You must be signed in to change notification settings - Fork 40.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prohibit circular references by default #27652
Comments
This breaks the circularity by making the executor be lazy, but that means it can't be used during startup or shutdown of the DB connector. Fortunately, that's also when we don't really need it; the init and cleanup SQL are allowed to run as long as they need. See spring-projects/spring-boot#27652 for the trigger Also, by wrapping it we can return a constant future in the cases where we can't actually schedule anything, making the rest of the transaction handling a bit neater. Which is nice.
@wilkinsona I'm missing a good solution for the self references (needed für transaction handling) which are detected now. |
If you'd like some help, please ask on Stack Overflow or Gitter. Someone trying to help you will need to see the code that contains the circular references so that they can advise you on how to remove them. |
@wilkinsona That's not true. It is a common pattern to inject the bean itself to be able to use spring annotations for "internal" calls. So if this behavior is disabled by default there should be a general suggestion how to solve these kind of problems (without the need changing the structure of the beans which are designed that way because of a good reason). spring-projects/spring-framework#13096 So I'm not looking for solution in a special case, there should be a feasible general solution for these use cases. |
I would also like to know of a recommended solution. Here is another very common use case: I have repo and a custom repo, where the custom repo uses the repo inside. Right now, the only thing that I came up with to prevent the circular reference problem is by using interface DogRepo : DogRepoCustom, MongoRepository<Dog, String> { ... }
class DogRepoImpl(@Lazy private val dogRepo: DogRepo) : DogRepoCustom { ... }
interface DogRepoCustom { ... } The problem is that using |
If you are looking for a general solution that does not require you to change your code, you can use the configuration property to permit circular dependencies again. This property is described in the release notes for Spring Boot 2.6. |
@wilkinsona Thanks! I think the Spring team had good reasons to prohibit circular dependencies and I want to follow that. |
With regards to repository fragments, that's really a question for the Spring Data team (/cc @mp911de). I'm not familiar with the other areas where self-injection has been recommended and I've never had a need for it so it's hard for me to comment. |
Generally speaking, if you defer repository bean access to after its creation ( |
In the project I work we have been using self invocations for caching quite a lot. I was curios to get some feedback on a solution like that like the one below. Basically I would replace self-invocations with calls to that ProxyExecutor bean and do something like:
I reckon it's a bit of a hack, but from a general perspective I don't see issues with it. |
Where can we get a more detailed and constructed answer? Everybody seems to think that cyclic dependencies is the sign of a bad design, but I don't get it. Of course, if the beans are injected with the constructor, a cyclic dependency is terribly problematic. But the simple solution is doing injections by setters, and from my experience, this approach is more convenient and less verbose. In one of my project, we have a data model close to a graph, with some Node and Edge POJO. So, to process theses data, we need two beans, a NodeProcessor and an EdgeProcessor, and naturally, each one depends of the other: each processor is complex enough to be in is own class, but to process a node you need to process every output edges, and to process an edge, you have to process the associated node. This design seems to me clear and understandable. Every suggested solutions introduce every time a hack based on an obscure class without any business semantic meaning and with the only purpose of break the cycle, one way or another. That's seems to me a worst design than my just two cyclic beans. So, where come from this very dogmatic "cyclic dependencies are bad"? Does anyone have a creepypasta with a sadistic or diabolically evil cyclic dependency? Spring Core seems to forbid the cyclic dependencies for now. Spring Boot introduced a way to allow it, but the release notes indicate clearly "you are strongly encouraged to update your configuration to break the dependency cycle". What is the long term vision of each Spring teams on that subject? Is the property |
No, its not a hack. Self-injection is completely fine in order to allow reentrant calls to honor AOP advises, like @Cacheable, @transactional, @retryable |
In our project we have a lot of references to other beans, because we separated the code by logical context. For example there is UserService that handles all user actions and there is CustomerService that handles customer actions. Of course UserService has reference to CustomerService, if we want to find customer. And CustomerService has UserService to check role of user for example. We have no idea how to break this "dependency cycle" and make better architecture and not make big mess of services with everything in one place. Is there any good practices how to make better architecture? How to even start solving this problem? |
You have to separate your business logic into coherent pieces. Although I have no info what are your UserService and CustomerService doing, it seems fishy separation as User and Customer are similar things. Instead of grouping business logic by Entity (User, Customer), try grouping by processes, use cases, etc... If you cannot clearly identify business process groups for User and Customer, you can try following purely technical technique:
|
@Majstr What works quite well is to sepparate low level operations of customer service (usually doesn't need user service) from more complex operation of customer service and divide that into different services. Same for user service. |
Is there an official strategy for these legitimate use cases ? using spring.main.allow-circular-references for the whole context just to allow one service to self-inject doesn't seem right. |
Completely agree, there should be official strategy for legitimate use cases of circular dependencies, like self-ijnection. Maybe a special annotation just for self-injection is a possible solution. Or some other explicit way to address AOP interceptors for re-entrant calls. |
If this is something that you would like to see, please raise it with the Framework team. The only tool at Boot’s disposal is a BeanFactory-wide configuration option which is what |
We believe that circular references between beans should be avoided if at all possible. To help users avoid accidentally creating such dependency cycles, we should configure the
BeanFactory
to prohibit circular references by default. We'll allow them to be enabled via API and configuration property for those that really need them, as we did with bean definition overriding in 2.1. We should also updateApplicationContextRunner
at the same time.The text was updated successfully, but these errors were encountered: