I have a use case of creating a spring bean factory that scans Types annotated with specific annotation and create spring beans based on annotation parameters.
Spring beans generated by this bean factory is of different type e.g. Class1, Class2. Multiple instances of theses types can be created with different qualifier.
Finally, I should be able to inject them using #Autiwired, #Inject, etc. annotations in any spring bean.
Notes: I need factory to avoid creating so many marker components.
For e.g.
There is an annotation #MyAnnotation that accepts a parameter "Class clazz".
There are two classes annotated with this annotation.
#MyAnnotation(clazz = Component1.class) FirstService {}
#MyAnnotation(clazz = Component2.class) FirstComponent {}
#MyAnnotation(clazz = Component1.class) SecondService {}
There should be a component that filters classes with this annotation and registers them as a bean of Component1, Component2 and Component1 respectively with custom parameters.
And finally I wanted to inject them in any spring bean like
#Autowired
Component1 component1Instance1;
#Autowired
Component2 component2;
#Autowired
Component1 component1Instance2;
I tried to implement this using ApplicationEvent, ApplicationContextAware, BeanPostProcessor, etc. But, couldn't achieve.
Kindly provide any hint to make this possible.
Related
From the official documentation:
When registered by type, any existing single bean of a matching type (including subclasses) in the context will be replaced by the mock
What if the service under test is autowired in the constructor, though? E.g. in Kotlin (I suppose #MockkBean and #MockBean work the same regarding DI):
#RunWith(SpringRunner.class)
class ExampleTests #Autowired constructor(val userOfService: UserOfService) {
#MockkBean
private lateinit var service: ExampleService
...
}
I would expect this example to fail because in order to instantiate ExampleTests Spring has to first obtain a proper instance of UserOfService. That shouldn't be possible at that time, though, because there's no bean of type ExampleService in the application context yet.
Contrary to my expectation, this works. How is it possible?
Because you miss the other part from the documentation :
In either case, if no existing bean is defined a new one will be
added.
So #MockBean will also instantiate a bean automatically if that bean is not found in the spring context.
The sequence of actions are mainly as follows :
Start up the spring context which create all the spring BeanDefinition only that are registered in the spring context.
Process #MockBean which will replace the BeanDefinition in (1) or create a new BeanDefinition
Actually instantiate all the beans based on these BeanDefinition. It will handle which bean to be actually instantiated first and later.
Create a test instance (i.e ExampleTests) to execute its test methods. If any beans required to be auto-wired into the test instance are not created , it will fail.
So as long as you define UserOfService bean , ExampleTests can be instantiated as you are now using #MockBean on the ExampleService which means it must exist no matter you define it or not in the spring context for the test.
I need to register a series of BeanDefinition(s) before every other Bean gets created. That's because those registered Bean(s) are needed for autowiring and ApplicationContext#getBean calls.
I cannot use #DependsOn, obviously.
Example:
final var beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(...);
beanDefinition.setLazyInit(true);
beanDefinition.setAbstract(false);
beanDefinition.setAutowireCandidate(true);
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
registry.registerBeanDefinition("...", beanDefinition);
Which point/interface/lister can I use to obtain this? Keep in mind I need an instance of BeanDefinitionRegistry.
Adding explanation as required.
Those definitions are created from a list of Classes gathered by scanning the classpath. Those classes are not Spring Bean(s) natively, so I need to integrate them into my ApplicationContext. Those classes, however, accepts constructor arguments which are Spring Beans.
That's why I'm setting
beanDefinition.setAutowireCandidate(true);
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
Those new registered Beans are there used by other Bean(s) (native Beans).
You are trying to make the solution too complex. If your only goal is to have non #Component annotated classes be detected by component scanning and have them used as Spring Beans simply define a custom includeFilter for the #COmponentScan.
You can use a filter of type ASPECTJ or REGEX to match a package or type.
#ComponentScan(includeFilter = #Filter(type=REGEX, expression="com.foo.bar.*))
Something like that will automatically detect your beans (assuming they are in a packaged being scanned) and create spring beans out of them. If they have a single constructor that will automatically be used to create an instance.
Register a new BeanFactoryPostProcessor or BeanDefinitionRegistryPostProcessor bean in your context. This bean will get invoked after bean definitions are scanned but before actual beans are constructed:
Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in. In particular, BeanDefinitionRegistryPostProcessor may register further bean definitions which in turn define BeanFactoryPostProcessor instances.
Even when we create the Beans in a Spring configuration class, I feel it is still useful to use #Component annotation on the class just to document/indicate that it's a Bean. Is it a good idea? Can there be any other issue that Spring will find the same bean defined in two different ways?
It is bad practice to create two beans from one class...
#Component
class SimpleComponent {
}
#Bean
public SimpleComponent simpleComponentBean(){
new SimpleComponent();
}
By default if we use #Component spring creates bean with name of class, but starts with small letter simpleComponent, if we use #Bean it takes method name and create bean with name simpleComponentBean and we have duplicated beans...
If we have method name the same, as component class name, spring replace one of them and we can Inject unexpected bean.
PS: intellij idea enterprise - correct indicate about all beans.
I am injecting a spring-managed bean into an abstract class and it seems that it doesn't work. Here is an explanation of what I do, if it is not enough, I can edit the post and add some code:
implement the java code - 1 abstract class, 2 classes that inherit from it and the bean to be injected and used in the abstract class and thus by the subclasses.
define the bean in the spring config.
define the abstract class and 2 subclasses in the spring config.
define the first bean as a property of the abstract class bean.
result - it's not working.
Then I tried to move the injected bean from the abstract class into the two subclasses. Now it works.
So, did I do something wrong, or is it theoretically impossible to inject a bean into an abstract class with spring 3 and Java EE 5?
Is it theoretically impossible to inject a bean into an abstract class with spring 3 and Java EE 5?
Dependency Injection happens on an Object not its Class. Spring injects dependencies into managed beans which are Objects already instantiated by Spring. Since, an Abstract Class cannot be instantiated it would be conceptually wrong to think about DI here.
On a somewhat related note, you can also define a non-abstract Class as an abstract Spring bean by adding the attribute abstract=true to the <bean> tag. This indicates that the bean would not be instantiated and would serve as a mere template of properties that other beans can use through bean inheritance (which isn't related to Java inheritance at all).
In this context as well, (if you've configured it) the Dependency Injection would only happen for the child beans and not their parent bean since it has been defined as abstract. (Note: It's not mandatory for the parent bean to be abstract but it makes sense to do so when all it does is to act as a set of common bean properties.)
i have a little trouble in Spring with two component of a service.
I have this component:
#Component
public class SmartCardWrapper
and this one:
#Component
public class DummySmartCardWrapper extends SmartCardWrapper
The service autowire both but spring fails due this expection:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.cinebot.smartcard.SmartCardWrapper] is defined: expected single matching bean but found 2: [dummySmartCardWrapper, smartCardWrapper]
Why it doesn't use class names?
That's one of the most basic concepts of Spring - Inversion of Control.
You don't need to declare your dependencies using their implementation types (to avoid coupling with implementation). You can declare them using interfaces or superclasses instead, and make Spring find the proper implementation class in the context.
In other words, bean are not distinguished by their implementation classes, because you may want to change implementation class of a bean without changing the beans that depend on it. If you want to distinguish between different beans of the same type, use logical bean names instead:
#Autowired #Qualifier("smartCardWrapper")
private SmartCardWrapper smardCardWrapper;
#Autowired #Qualifier("dummySmartCardWrapper")
private SmartCardWrapper dummySmardCardWrapper;