Spring : Create beans at runtime - java

I am working on a module which needs spring beans to be created at runtime. My driver app is a spring boot app, it needs to create a bean (which is outside the component scan packages of the app) and invoke a method in that bean. Fully qualified class name and method name will be runtime parameters. Below is an example:
Bean to be created:
#Component
#ComponentScan(basePackages = {"com.test"})
public class TestClass {
#Autowired
private AnotherTestClass anotherTest;
public void test(){
System.out.println("In test");
anotherTest.test();
}
}
Here is the driver class code (which obviously doesn't work):
public void test(){
try{
Class testClass = Class.forName("com.test.TestClass");
Object newInstance = testClass.newInstance();
applicationContext.getAutowireCapableBeanFactory().autowireBean(newInstance);
Method testMetod = testClass.getMethod("test");
testMetod.invoke(newInstance, null);
}catch(Exception e){
e.printStackTrace();
}
}
Here, applicationContext is autowired.
As it is instantiating the class via reflection, the object won't have autowired dependency set. What I want to achieve is, to scan all the packages (as mentioned in the component scan annotation), create the beans and set the dependencies. i.e. I want to do exactly the same as what spring does when application starts, but at runtime.
Also, I want it to be completely generic as the beans will mostly be residing in external jars and fully qualified class path and method name will be passed at runtime (like a pluggable architecture).
Any ideas how to achieve this?

Related

How to load manual objects created via reflection into spring context

There are two application. Main Application and Utility Application where utility Application is packaged as jar in Main application. Both are spring boot application.
In utility application I create an object of a class A which is present in Main Application using reflection API.
Class A has one Autowired instance variable defined in it. Issue is when Utility Application tried to run some method one the same class A object, then that autowired variable results in null.
I guess, since I am creating the object manually that is why it is not managed by Spring.
Now my question is how can provide this object to spring context so that any Autowiring happening inside the class A actually gets autowired.
Below is the code samples
under Main Application
package m.main.application.A;
class A implements Action{ //Action interface coming from utility application
#Autowired
private Calculator calculator
#override
public void execute(){
calculator.add(5+2); //Here calculator is null. Autowire is not working
}
}
Utility Application packaged as Jar in Main Application
Class Util{
public createObjectAndRun(){
Class<?> class = Class.forName("com.main.application.A");
Action newObject= (Action) class.getConstructor().newInstance();
//executing execute
newObjects.execute(); //This fails as calculator is not autowired
}
}
I want to know if there is a way we can make calculator gets autowired properly.
Your class A is not annotated with any spring stereotype (#Service, #Component, etc), so it's not a spring managed bean. So one way you could achieve this would be making it a spring bean (using a stereotype) and get it through ApplicationContext's method getBean on your Util class.

Load Jar dynamically and register class(es) in applicationContext at runtime

I've a jar which contains java classes (Could have multiple classes). In my spring boot application, I've to add this jar in classpath at runtime and need to register classes as bean in ApplicationContext.
I also have to make sure that loading jar and bean registration happens in certain order so that bean wiring happens correctly. Can some one please help.
Best Regards,
Maneesh Saxena
For example, if you have a class Foo on your external jar which looks like:
public class Foo {
public Foo(String arg1, String arg2){
// your constructor
}
}
On your spring-boot application, you can register it as bean using #Bean:
#Configuration
public class AppConfig{
#Bean
public Foo fooBean(){
return new Foo("toto","bar");
}
}

Why is the bean 'reactorServiceInstanceLoadBalancer' instantiated just when the method 'getInstance' is called?

Im trying to define a custom LoadBalancer by implementation the interface ReactorServiceInstanceLoadBalancer to replace the default load balancer defined by RoundRobinLoadBalancer.
But it doesn't work.
I found that the original bean defined in class 'LoadBalancerClientConfiguration' isn't instantiated when the application startup,but instantiated when 'LoadBalancerClientFactory.getInstance' is called, and the contructor is autowired with a bean of StandardEnvironment, while the bean defined in my configuration is instantiated when the application startup, and autowired with a bean of StandardReactiveWebEnvironment.
Very confused!
My english is not so good.Thank you for reading the whole description!
Here is my code below:
`#Slf4j
public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
// ...detail omitted
}
#Configuration(proxyBeanMethods = false)
#ConditionalOnDiscoveryEnabled
public class CustomLoadBalancerClientConfiguration {
#Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory, ServiceInstanceChooser serviceInstanceChooser) {
String name = loadBalancerClientFactory.getName(environment);
return new CustomLoadBalancer(serviceInstanceChooser, loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
A separate context, including the ReactorServiceInstanceLoadBalancer instance is created per requested serviceId. It is possible, to provide your own implementation, both default or for a specific service. Please see the documentation on how to provide custom configuration for Spring Cloud LoadBalancer.

How does spring recognize the param inside a constructor

I was trying to configure promethues in my code and I just had to create a bean like below. I was wondering how spring recognised CollectorRegistry. How did spring instantiate all the necessary variable for CollectorRegistry and setup all the necessary cofnfiguration?
#Component
public class TestProm{
public TestProm(CollectorRegistry registry){
// Some initialization code here
}
}
However, when I tried to define it in another way by defining a #Bean in my #Configuration class, it didn't seem to work properly as my own constructor for CollectorRegistry didn't have all the necessary properties.
#Configuration
public class PromConfiguration{
#Bean
public TestProm getTestProm() {
return new TestProm(new CollectorRegistry());
}
}
public class TestProm{
public TestProm(CollectorRegistry registry){
//Some code here
}
}
How do I recognise/replicate the initialization of CollectorRegistry done by spring when I do my custome implementation.
How did spring instantiate all the necessary variable for CollectorRegistry and setup all the necessary cofnfiguration?
In the first example you require a Bean of type CollectorRegistry and Spring will actually create such bean for you if you have spring-boot-starter-actuator and Prometheus dependencies on your classpath and you have autoconfiguration enabled :
if you use #EnableAutoConfiguration
if you are using Spring Boot and #SpringBootApplication annotation (#EnableAutoConfiguration is part of this annotation underneath) :
...
#EnableAutoConfiguration
#ConfigurationPropertiesScan
...
public #interface SpringBootApplication
In this case Spring will scan the classpath and load all configurations. The bean that you are interested in is part of PrometheusMetricsExportAutoConfiguration :
#Bean
#ConditionalOnMissingBean
public CollectorRegistry collectorRegistry() {
return new CollectorRegistry(true);
}
In the second example the instance of CollectorRegistry is not managed by Spring because you create it through new keyword. However it should work as CollectorRegistry has default constructor which initializes autoDescribe field to false. And the default Bean of this class which is created by Spring (in a way described above) has this field set to true. So the value of this field is the source of your differences.
Also if this instance is not managed by Spring - it prevents it to be injected into other components which require it. As the scope of CollectorRegistry is Singleton (shown above) other beans might require to share the instance to work properly (for example some beans might want to register/deregister collectors) but if you create CollectorRegistry like that with new keyword - you will not get the singleton instance but a new instance which cannot be shared across other beans.
How do I recognise/replicate the initialization of CollectorRegistry done by spring when I do my custom implementation?
If you want to use the default CollectorRegistry (assuming you want to use the bean that is created by default in the way described above) just inject bean of this type to your beans and it should be enough.

How do I add a bean to Spring context in my library without breaking consumers who have their own instance of that bean?

I have a library which produces beans into a Spring context for use by clients. The beans I produce are configured by Spring. I need to add a new bean to my context in order to satisfy a dependency of a new bean I'm publishing. However, I believe some of my clients already have an instance of this bean and are autowiring it by type. So I have something like this:
// Code in my Library
#Component
public class PublicUtilityClass {
// This is all new code in my library
private NewDependency newDependency;
public void newCapability() {
newDependency.doNewThing();
}
#AutoWired
public void setNewDependency(NewDependency newDependency) {
this.newDependency = newDependency;
}
// Other library code omitted.
}
How can I use Spring to instantiate NewDependency and inject it into PublicUtilityClass without impacting customers who already have a NewDependency bean in their context?
You should look at #Qualifier annotation. Qualifier allows you to have multiple instance of your bean

Categories

Resources