I got a circular dependencies (cross-reference) issue when building a Spring boot project, and the dependencies trend like below:
Processor class autowired Criteria class via the constructor injection;
Criteria class autowired CacheManager via the constructor injection;
CahceManager class autowired the RuleSet class via the setter injection;
RuleSet class autowired the Processor again via the constructor injection.
The dependencies of some of the beans in the application context form a cycle:
app
┌─────┐
| XXXProcessor defined in file ...
↑ ↓
| XXXCriteria defined in file ...
↑ ↓
| XXXCacheManager
↑ ↓
| XXXRuleSet defined in file ...
└─────┘
While I can make an effort to remove the dependency of Processor from RuleSet class, I was wondering if there is a way of keeping the current references but still eliminating the cross-reference issue as presented here? I looked up this forum and someone suggested that the #Lazy annotation might help. I tried to apply it to either the Processor class or the RuleSet class (on either class level or method level), the issue didn't go away.
Another observation is that, the above quoted error didn't appear all the time - sometime the program proceeds just fine, it's that the error randomly occurs that bugged me. And why is that?
One way to solve it is to replace one instance with a Provider like this:
public Processor(Provider<Criteria> criteria) {
this.criteria = criteria;
}
Then when using it you need to get() it first.
Criteria c = this.criteria.get();
This means that Processor can be constructed before Criteria since the injected Provider will get the Criteria bean once it's ready.
This means that you cannot call get() in the constructor or you'll get a runtime error since that would still mean a circular construction dependency.
#Lazy just means that Spring should wait with initialising a bean until it is actually requested instead of eagerly creating it on startup (which is standard behaviour). This has zero impact on both circular dependencies and beans that are injected into other beans constructors. It is useful for beans that are very slow to initialize and must almost always be used from a Provider to actually defer initialisation.
You can replace constructor injection with field injection in one place. It shall break the cycle of circular objects instantiation.
It's hard to say why does this issue happen from time to time. Perhaps, there are some #Configuration classes that build different implementations of the same interface depending on config values. You should provide more details to deal with this problem.
Related
I'm learning spring and I'm stuck with the bean lifecycle !
When creating a bean, spring gives us several maners to interact with it. We can use one or all of :
BeanFactoryPostProcessor
BeanPostProcessor#postProcessBeforeInitialization
#PostConstruct
InitializingBean#afterPropertiesSet
#Bean#initMethod
BeanPostProcessor#postProcessAfterInitialization
Spring calls them in the order above
My question is : why all these points of interaction ? and when to use each of them (the use case) ?
A BeanFactoryPostProcessor and a BeanPostProcessor are quite different beast and also apply to different things.
A BeanFactoryPostProcessor will operate on the metadata for a bean (I like to call it the recipe) and will post process that. A well know example is the PropertySourcesPlaceholderConfigurer which will inject/replace all #Value in configuration with the value. A BeanFactoryPostProcessor operates on the metadata and thus before any bean has been created.
The BeanPostProcessor can be applied to a bean and can even replace a bean. Spring AOP uses this quite a lot. An example is the PersistenceExceptionTranslationPostProcessor, when a bean has been created it will pass through this BeanPostProcessor and when it is annotated with #Persistence the bean will be replaced by a proxy. This proxy adds exception translation (i.e. it will convert JpaException and SQLException to the Spring DataAccessException). This is done in a BeanPostProcessor. And can be be done before the init-callbacks are called (the postProcessBeforeInitializationor after they have been called thepostProcessAfterInitialization). The PersistenceExceptionTranslationPostProcessorwill run in thepostProcessAfterInitialization` as it needs to be certain the bean has been initialized.
The ServletContextAwareProcessor will run right after the object has been created to inject the ServletContext as early as possible as the initializing of a bean might depend on it.
The 3 callbacks for initializing a bean are more or less the same but are called in sequence because they have been included in later versions. It starter with only an interface InitializingBean and init-method in xml (later also added to #Bean and the annotation support was added when annotations became a thing.
You need init methods to initialize a bean, you might want to check if all properties have been set (like a required datasource) or you might want to start something. A DataSource (especially a connection pool) is a good example to initialize. After all dependencies have been injected you want to start the pool so it will open the connections. Now as you probably cannot modify the code you want to use the init-method if you control the code you probably want to add #PostConstruct. If you are writing an framework that depends on Spring I would use the InitializingBean.
Next to those 3 init methods you also have the destruction counter-parts. The DisposableBean interface (and destroy-method) and the #PreDestroy annotation. Again when you stop your application you also want to close the connections in your connection pool and you want to probably call the close method on the DataSource. A perfect sample of a destruction callback.
I use Java WebSocket API to declare client (Java class annotated by #ClientEndpoint):
#ClientEndpoint
public class MySock {
MySock(ExecutorService exec){}
...
}
Instance is created via constructor:
webSocket = new MySock(exec);
session = wsContainer.connectToServer(webSocket, url);
And I have error during the build via quarkus-maven-plugin:
[error]: Build step ...ArcProcessor#validate threw an exception:
javax.enterprise.inject.UnsatisfiedResolutionException:
Unsatisfied dependency for type ...ExecutorService and qualifiers [#Default]
- java member: edu.MySock#<init>()
- declared on CLASS bean [types=[edu.MySock, java.lang.Object], qualifiers=[#Default, #Any], target=edu.MySock]
Pay attention: there is no #Inject annotation
Should have it been validated, if it can be passed to #connectToServer as a class and as instance too?
So, Is it ok if validation processes a pessimistic case, where validation is useful, but it brokes an optimistic case?
Pessimistic case, where dependencies may not be declared:
session = wsContainer.connectToServer(MySock.class, url);
In the following case, validation is harmful because it brokes build phase:
session = wsContainer.connectToServer(webSocket, url);
Maybe ClientEndpoint should not be validated at all?
And before you ask me...
We are not going to inject something into WebSocket and we would not like to use programmatic EndPoints. But we want to create an instance for the annotated class. Why not? WebSocket incapsulates complex logic inside itself and this variant we used multiple times (e. g. in applications on WildFly).
The best solution for me would be to disable validation for my bean only, but I cannot find an idea of how to do it.
This article has not helped me https://quarkus.io/guides/cdi-reference. The fact that beans.xml is ignored cannot help me too.
The second-way, for the future, is to disable validation if there is no one class member with #Inject annotation. It can be not correct, but here there is some explanation:
First, the container calls the bean constructor (the default constructor or the one annotated #Inject), to obtain an instance of the bean.
So, my constructor is not "default" and I did not use #Inject annotation.
Work around is so simple: #javax.enterprise.inject.Vetoed annotation disables bean for validation.
I encountered this issue when I'm trying to override the RibbonRoutingFilter bean defined in spring zuul. To emphasis, I'm doing an override, not just creating a bean of the same type. So end of the day, I want the "ribbonRoutingFilter" bean from zuul not registered at all.
So I have my own implementation. First thing I tried, I used the #component annotation and autowire the dependencies. Added a breakpoint in the constructor, and it ended up never being called. So I realize my definition must be loaded earlier than zuul's. So I created a configuration class with #Configuration annotation and #Order(Ordered.HIGHEST_PRECEDENCE), and use a #Bean annotation to instantiate my class there. Still, my method is always loaded earlier.
It turned out there's certain order Spring is following when loading configuration classes definitions and that is where overrides happen. Class org.springframework.context.annotation.ConfigurationClassParser has the detailed logic in method doProcessConfigurationClass(). I'll put my simplified summarization or the ordering rule here:
if you application class(where main() method is defined) has any classes defined in it, they are parsed and definition inside them are registered first
then it will registered Beans defined as #component and defined in #Configuration class
then it will add definitions introduced by #Import
then it will add definitions introduced by #ImportResource
then add definitions from #bean methods inside the application class
then from default methods on interfaces( I think it's java 8)
then try to do the same steps above for any parent classes you application class has extended.
This explained why my override was not working. It's because all I have been trying is in step 2. But zuul defined the bean by a #Import which is step 3.
So to solve my problem, I added a #Bean annotated method to my application class there and do the instanciation and the override just happend as expected.
The above summarization might not be accurate, it just give you an idea about what could have failed your override. You'd better debug the ConfigurationClassParser when you are trying your specific use case.
I have some legacy code where the class is a Spring bean defined and initialized through xml. It is a singleton with a field member which is a class dependency. There is a setter method for it, so I am assuming its supposed to be set via Spring, although I didn't find any xml defining it. There is also a get() method for the dependency, it has a null check and if its null it manually creates it outside of Spring like so
Class Test{
Dependency d;
setD(Dependency d){this.d=d;}
getD(){
if(this.d==null){
this.d = new Dependency();
}return this.d
}
}
I am trying to understand why this Spring bean is initializing a dependency outside of Spring and what are the implications if any, is this just bad/old design? or Am I not understanding something with how Spring works.
I would say it's a bad design, probably author wanted to provide fall-back for the case when D was not injected in spring. Another idea is an attempt to make D a lazy dependency. You should explore what's inside D.
Generally you can use #Required to mark the members that always should be injected. Or just use simple and nice constructor injection. If you're concerned about lazy injection, that's how Spring works by default.
Doing my first steps with spring 4 I tried the #Conditional annotation following this article.
My problem -
I would like to get access to a classpath resource (basically a properties file) from method matches in class OnSystemPropertyCondition.
To do that currently I'm loading the required properties file from the matches method every time it is invoked (which means for every class annotated with the ConditionalOnSystemProperty annotation).
This is a bit ugly. I thought that an elegant solution would be to simply autowire my resource or some properties (using the #Value annotation) but this can't be done since this class gets instanciated before the beans.
Any suggestions than can help me avoid reload this resource again and again?
The single method of the annotation gets in its signature the input param ConditionContext context. You can obtain an Environment from the context by calling context.getEnvironment(). The environment gives access to all my resources (look at this to see how to get access to your resources via spring environment).