How to override spring beans with Java config - java

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.

Related

Inheritance with SqsListener

I have a strange situation that I don't understand.
I would like to decide in a bean, should I enable or disable the SQS listener.
So I've created a config class with a definition:
#Bean
MyListener createListener(Features f){
return f.shouldListen() ? new RealListener() : new MockListener();
}
As you can see, I have such an inheritance:
interface MyListener{}
class RealListener implements MyListener{
#SqsListener(...)
list handleMessage(SqsMessage message){
...
}
}
class MockListener implements MyListener{}
Now, the funny part:
It sometimes works.
After few restarts of the application, the handleMessage() method is called but in most cases it isn't without any exception. The queue is created, all permissions are in place.
To make it working I need to return RealListener from createListener() method or move #SqsListener annotation to a method in the MyListener interface. Both are not options for me, because I don't want to call AWS, when the mock is enabled.
I've tried with conditional bean creation but as Features depends on a DB (indirect entityManager dependency, to be more precise), I can't make it working. I've tried with abstract class instead of interface, but without luck. I've tried to register the RealListener bean in the BeanFactoryPostProcessor but this also doesn't work (same entityManager dependency issue). I've tried to move the annotation to the interface and use #ConditionalOnBean with #Primary to create an empty AmazonSqsClient when the mock is enabled, but it doesn't work.
I could understand that it doesn't work, because the bean has to be created for a type with the method annotated with #SqsListener (not with it's supperclass/interface type), but I have three such beans and a lottery - sometimes all work, sometimes one or two but sometimes none of those.
Do you have any suggestions?
Well... I've found the issue but it still would be nice, to know, what has happened.
So... There is a class QueueMessageHandler using detectHandlerMethods(...) method from the superclass AbstractMethodMessageHandler. This method uses MethodIntrospector.selectMethods() to select methods to scan. This class takes into consideration methods annotated with #SqsListener when there is #EnableSqs in some configuration class.
The problem is, in my project the #EnableSqs annotation is located in some file - not the file with createListener(...) method and not the main class of the Spring Boot application. That means, the class with #EnableSqs can be loaded before or after MethodIntrospector.selectMethods().
The output is:
I have no idea, why it works fine without inheritance and doesn't work with inheritance
To fix the issue, I've moved #EnableSqs to the main class of the project

Is #Bean inside of #Component a singleton?

I've read in some places that the difference between #Bean inside of #Component and #configuration, if that the latter provides a singleton bean while the former does not. Can anyone confirm if this is true or not?
When using #Configuration annotated classes those are processed in full mode mode, meaning the class is read using ASM, enhanced with CGLIB so that inter method calls will return the same instance.
Using #Component to declare beans those are process in lite mode. Not allowing for inter method references (you can do it but each call will create a new bean).
See als this section in the Spring Reference Guide which explains this in great(er) detail.
Please specify the meaning of singleton in your question. Actually, in Spring world SINGLETON is the default bean scope and it doesn't matter where bean defined in Component or Configuration its scope will be SINGLETON, but can be overridden by supplying the scope attribute to #Bean annotation. Beans defined in Configuration has some constraints. More details you can find here: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html

How to tell if a class is Spring loaded in java?

I have been given a task to assign a property from .properties file to a non Spring bean class using #Value annotation. To do this, I created a method on a #Component annotated class and set the property into it, then called that method from the non Spring bean class. I thought this would work, however, still showing as null.
I was told this is because the #Component annotated class I used is not spring loaded. Question, how can I tell if a class is Spring loaded bean? I have been searching on google but can't find anything helpful aside from examples with #Component or #Configuration annotations. Thanks.
Spring Container is responsible for creating or managing beans. It all satisfy the dependencies by injecting them either through constructor or setter method. But in your case you want the #Value injection in your non spring bean which is really not possible as per my understanding. Because here the spring does not creating the object then how it satisfy the dependencies of it.
You have two options for this situation.
Either annotate class using #Component
Either read property file using Properties
https://www.mkyong.com/java/java-properties-file-examples/

Usage of #Component in case of inheritance

I have 2 scenarios where I would like to understand/confirm the usage of #Component:
Extending concrete class:
I have a concrete super class A and its sub-class Aa in my web application. I have annotated with Aa with #Component(value="aa") and #Scope(value=WebApplicationContext.SCOPE_SESSION). Also, I have annotated A class with #Component(value="a") and #Scope(value=WebApplicationContext.SCOPE_SESSION).
My question -> I am only doing applicationContext.getBean("aa"). I can skip the annotations in A class (please correct me if I am wrong), but I don't know why and how? My understanding has been that if a class is not annotated with #Component or defined in bean configuration file then Spring doesn't handle its instance management.
Abstract concrete class:
Same scenario and question as above just that in this case super class is an abstract class.
You have to register the beans via beans in a config or via component annotations (Repo or controller or service). If not the bean is not contained in your application context container.
#Component add a bean in the Spring registry. Then, you can retrieve this bean later.
If you don't use the bean, there is no need to add it to the bean registry. (so just remove #Component(value="a") and #Scope(value=WebApplicationContext.SCOPE_SESSION) )
On your use case, you set a scope to SESSION. It means that every time you create a session, Spring will instantiate your class (A / Aa) and put it on the session. As it is an instance of the class you don't need the super class instance (A) to be able to create the Aa instance.
With A beeing abstract, it is exactly the same thing, except if you try to scan it for Spring to pick it, Spring will throws an error saying A cannot be instantiated.

Order of Configuration in SpringBoot

I am trying to understand how beans that we make using #Configuration tends to override the beans that are generated by SpringBoot by default. I have been working on a project where in many cases we create beans for things like ZuulConfigs and the assumption is, whatever we are making shall take precedence over the default generated bean. I have been trying to figure this out but can't. Basically,
Is Spring achieving this via some custom class loader
If not how is this precedence working. Can I give some precedence in similar manner to my beans
Can I generate similar hierarchy in my project,if so how
The help is highly appreciated
Spring AutoConfiguration is used to provide a basic configuration if certain classes are in the classpath or not.
If you want to configure the order in which beans are instantiated by spring you can use
#DependsOn("A")
public class B {
...
}
This would create bean "A", then "B". Hence you can order the configuration depending upon the beans need first to be done. Anyways Spring automatically detects the dependencies by analyzing the bean classes.
for more help check this question
Spring Boot AutoConfiguration Order
Alternative :
There is also "#AutoConfigureOrder" annotation(where you can prioritise the configuration), you can have a look in the code for deeper understanding.
Documentation of AutoConfiguration is here
First of all, class loading and bean creation are two different things. We don't need to create a bean to load a class, however, a class has to be loaded in order to create a bean.
Now, coming back to Spring's example, Spring looks into all the packages configured by #componentScan and creates beans of all the classes annotated with #Bean, #Configuration and/or #Component. Spring's container keeps track of all the beans created and hence, when it encounters user defined bean with same name and class type as default bean, it replaces the original definition with user defined one (e.g. we can create our custom #ObjectMapper to override Spring boot's own instance). You can also use #Primary annotation to make you bean take precedence if another definition with same class exists (documentation here).
Below are the answers for your questions:
Spring uses reflection to load the classes and create instances. Although you can load the classes with your custom class loader (more on that here), you don't need to worry about it for #Configuration.
Yes, you can use #Primary annotation to give your bean a precedence. You can also use #Order(here) to define the creation order for your beans.
With #Primary, #Order and #Qualifier annotation you can define your own hierarchy for bean creation.
Can I give some precedence in similar manner to my beans
Yes.
A) To define a specific order your Configuration classes will be handled (by the way, a Configuration class does not have to be annotated with #Configuration (so-called full definition), but it's enough to be annotated with #Component, #ComponentScan, #Import, #ImportResource or just have a method annotated with #Bean - so-called lite definition), you should
1) add your Configuration Candidates to your SpringApplication's primarySource, for example, in your main method like that
SpringApplication.run(
new Class[]{YourSpringBootApplication.class, Config1.class, Config2.class, ...},
args);
2) and annotate each of your Configuration Candidates with #Order annotation, any other ordering means like Ordered interface, #DependsOn etc will be ignored by ConfigurationClassPostProcessor, the order in the primarySource array will also be ignored.
Then ConfigurationClassPostProcessor will sort your Configuration Candidates and handle them according the #Order annotation value you specified.
B) The precedence can also be achieved by defining your own AutoConfiguration classes. Although both Configuration and AutoConfiguration are handled by the same ConfigurationClassPostProcessor, they are essentially distinctive machineries. To do so
1) define in your classpath /META-INF/spring.factories file and put in the EnableAutoConfiguration section of it your AutoConfiguration classes like that
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
your.package.AutoConfig1,your.package.AutoConfig2
2) and annotate your AutoConfiguration classes with #AutoConfigureOrder, #AutoConfigureAfter, or #AutoConfigureAfter annotations, any other ordering means again will be ignored.
Like #Strelok pointed out, AutoConfiguration classes, your own and provided e.g. by spring-boot-autoconfigure library alike, will be added to the end of the list of Configuration Candidates.
Remember, however, that the order the Configuration Candidates will be handled by ConfigurationClassPostProcessor does not necessarily coincide with the order the beans defined by the Configuration classes will be created. For example, you might define your Configuration class that overrides TomcatServletWebServerFactory to make your own customization of Tomcat web server like
#Configuration
public class EmbeddedTomcatConfig {
#Bean
public TomcatServletWebServerFactory containerFactory() {
...
return customizedTomcatWebServerFactory;
}
but this method will be called right at the moment when your Spring Boot application decides to create a Web server, regardless of how you defined the precedence for your EmbeddedTomcatConfig Configuration class.
Is Spring achieving this via some custom class loader
There is no need to. Although you could, as always with Spring, define your own ClassLoader for BeanFactory, standard ClassLoader is good enough if everything you need for Configuration in your application is available in the classpath. Please notice, that at first phase ConfigurationClassPostProcessor does not load (i.e. does not resolve) the Configuration candidates classes (otherwise, most of the classes in spring-boot-autoconfigure library will fail to load). Instead it analyzes their annotations with bytecode analyzer, ASM by default. For that purpose, it is just enough to get a binary form, a byte array, of a class to feed it to bytecode analyzer.
Just know this: Spring Boot (specifically) auto configuration classes are always configured last. After all user beans have been created. Spring Boot auto configuration classes almost always use the #ConditionalXXXX annotations to make sure that any beans of the same type/name and other conditions that are configured in your application will take precedence over the Spring Boot auto-configured beans.
If you want your #Component to take precedence over other #Component while scanning all the components by spring, use #Order(Ordered.LOWEST_PRECEDENCE) i.e. the max value to load your component over other.
#Primary is used to give your bean a default preference, we can override the default preference using #Qualifier

Categories

Resources