I have a project where I use heavily autowiring, e.g.
#Component("componentA")
public class ComponentAImpl implements ComponentA
#Component("componentB")
public class ComponentBImpl implements ComponentB {
#Autowired
private ComponentA componentA;
}
But I want customer to extend all the classes, so ideally they would just add a single jar into the classpath the the extended class should be used instead of the original one.
What I think would be great if they could just set a different name for the bean, e.g.
#component("componentA-custom)
I just need some way to customize the autowire process to look for a bean with "-custom" at the end and load it instead of the original bean.
Is there any way to do that?
Any help appreciated.
Thank you,
Mariusz
I found a solution, there is
#Primary
annotation that can be used to specify that the custom code added should be loaded instead of the original code.
What you are looking for is something similiar to this.
This should help you solve your problem.
If you need to get the bean name, you can inject in an instance of applicationContext which will get you the correct bean name (through a reverse lookup). or force all beans (ComponentA and CustomImplementation) to implement BeanNameAware
What You are asking about can be done via #Qualifier annotation, but it cannot generate bean names dynamically.
To allow external beans, You will need to create XML configuration for this, as it overrides annotation - based configuration. Your customers would have to provide:
New beans
XML configuration for your application that uses the new beans.
Related
I have such app conf in, jar which will be added to the classpath after startup:
#Configuration
#ComponentScan("com.transportexchangegroup.testConf")
public class AppConf {
}
How to load beans dynamically? I saw solutionsm when it is required to write and add bean definitions, but if we do not know everything about new beans and just want to load them automatically?
Class conf = jarService.loadClass("com.x.testConf.AppConf");
((AnnotationConfigServletWebServerApplicationContext) applicationContext).register(conf);
((AnnotationConfigServletWebServerApplicationContext) applicationContext).refresh();
As I see, refresh() turns off web app started locally from IDE.
Do you know any other solutions or what is wrong? Will this work for spring rest controllers from jar?
I'm not sure what do you mean "dynamically".
In general you can load beans if some condition applies, usually depending on the configuration. So you can do something like this:
application.properties: // or yaml it doesn't matter
feature.enabled=true
#Component
#ConditionalOnProperty(name="feature.enabled", havingValue="true", matchIfMissing="true" / "false") // matchIfMissing depends on whether you want the bean to be loaded if the property is not defined
public MyBean {
}
Some caveats:
If you have many beans that depend on "business" feature in order to avoid placing #ConditionalOnProperty you can do one of the following:
Define your own #Component annotation:
// runtime retention, place on class
#Component
#ConditionalOnProperty(...)
#MyFeatureComponent
... and use it in all the beans that define the feature:
#MyFeatureComponent
public class MyBean
{}
Use Java Configuration instead of annotations:
#Configuration
#ConditionalOnProperty(...)
public class MyFeatureConfiguration {
#Bean
public MyBean myBean(){return new MyBean();}
#Bean
public MyAnotherBean myAnotherBean(){return new MyAnotherBean();}
}
In this case you don't need to place any annotation on MyBean at all.
Spring also has a concept of profiles which is just the same, something that it under the hood implemented with these conditionals.
It allows however to define configuration files per profile, so you might want to read about #Profile annotation as well.
As for the bean definitions - this is way more advanced stuff, in general when spring loads it "recognizes" which bean should be loaded and in which order and for doing that it creates a bean definition before loading the bean. So if you hook into this process you can define your own bean definitions if you want and spring will create beans based on these definitions as well. So basically its a hook that allows altering the bean defitions / create new one during the startup process and hence affect the actual beans that will be loaded into the application context.
I doubt, but if you really need that, read about Bean Factory Post Processors in spring.
I have a Configuration class that is implementing BeanDefinitionRegistryPostProcessor , In this I have to take some properties from the Property file use them in the postProcessBeanDefinitonRegistry. When I try to autowire the ApplicationContext I noticed that the ApplicationContext object is coming as null and so does all the other injected objects. On looking into the explanation for "BeanDefinitionRegistryPostProcessor" I came to know that it executes early.
Question is :: How do I read my property values in the class that is implementing BeanDefinitionRegistryPostProcessor?
I have resolved the above issue. I understood that for my code logic "BeanDefinitionRegistryPostProcessor" is not suitable. For my scenario I had to implement the "ApplicationContextAware" interface.
I have a collection of classes which I want to be injected into the Spring application context. However, these classes can only be guaranteed to be annotated with one of a group of annotations I have written - i.e. I can assume it will be annotated with #MyAnnotation, but not #Component.
However, #MyAnnotation forms part of an API for my project, and I don't want to state an explicit dependency of this API on Spring. Thus, I can't annotate #MyAnnotation with #Component in order to have it be transitively picked up by Spring.
Is there a way to tell Spring to additionally include #MyAnnotation in its classpath scanning without adding this dependency to my API?
Currently I'm manipulating the bean definition registry to 'manually' add each class annotated with #MyAnnotation, but I'd prefer to rely on Spring's inbuilt support.
Thanks in advance.
It's possible if you create your own BeanDefinitionRegistryPostProcessor to register your own beans. If you implement the postProcessBeanDefinitionRegistry method, you can add beans to the registry by yourself, for example:
#Component
public class FooFactoryBean implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registry.registerBeanDefinition(..);
}
}
To obtain these bean definitions, you can use the ClassPathScanningCandidateComponentProvider class, which will create BeanDefinition objects for all classes found for a specific filter. In this case, an AnnotationTypeFilter will work:
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Foo.class));
Set<BeanDefinition> definitions = scanner.findCandidateComponents("com.example.my");
In this example, it will find all classes annotated with #Foo in the com.example.my package.
#Configuration classes and XML based configuration should work for you. Have a look at this tutorial: https://www.tutorialspoint.com/spring/spring_java_based_configuration.htm
But to get your #MyAnnotations picked up is more difficult (see #g00glen00b's answer), and I'm not sure it makes sense if the above mentioned solutions are available.
I'm wondering if there is possibility to recreate a bean which was already created in java configuration on web app startup.
What I want to do is to reconfigure bean settings.
For example I create new bean with path to database:
#Bean
public TestBean getTestBean() {
TestBean tb = new TestBean("some_path_taken_from_external_point");
return tb;
}
During runtime I want to change the path. Let's assume that this bean doesn't have the setter method for database path.
I will have some kind of event and a listener for this event. Listener should reinitialize TestBean with new path.
Is this possible?
I was thinking of some kind of wrapper. In such case I would have class TestBeanWrapper which will have method get() which will return TestBean instance and recreate(String path) which will create new object with given path.
I'm not sure exactly if such wrapper would work for me, as the TestBean is a class from external library, and I'm not sure if it's not injected somewhere (but probably it's not injected).
More possible is that the other beans may rely on TestBean, so they also must be reinitialized (in case if they won't have setters for my TestBean).
Is this even possible in Spring (4.1) ? What is the best approach for such cases?
So I'm still unsure why you would want to change the path but I have 2 suggestions:
1. Look at setting the scope on the Bean.
By setting the scope on the bean, you can regenerate the bean based on context. Look at Bean Scopes for more information.
2. Look at maybe using a controller or a service.
Controllers and services allow getters and setters which may give you more control.
I'm using annotation configuration and I currently cannot use request scope for my controller, but I need one of the bean that controller uses to be a prototype. So I figured the best way would be getting prototypes for a singleton controller via method injection.
But then I realized that Spring's method injection needs an abstract class so that I couldn't use my annotation configuration ...
Could please anybody tell me how to do that ? It seems to me that it is very common scenario, but currently it can be realized only via "request scope" of controller.
Or I'd have to make my controller ApplicationContextAware and get the bean from context. But can annotation-config #Controller be ApplicationContextAware ?
You can simply #Inject ApplicationContext ctx, but you need your other bean to be defined in the child context (dispatcher-servlet.xml) (you need that anyway). And then you can look it up.
There is no way to define lookup-method injection with annotations currently. There is an open issue about that. So for this particular controller you can use xml configuration to define the lookup-method.