Accessing static Application method from RunTime created class in spring fails - java

I have two projects A and B. I'm adding B as a pom dependecy of A.
My project A is a springboot application that has a Application.java class which implements ApplicationContextAware
Project B also has a App.java class that implements the ApplicationContextAware. In both I add the ApplicationContext to a static variable context
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
When I start A, and try to get a Bean of A from B using the context like :
Class<?> klass = Class.forName(klassName);
Myclass myclass = (MyClass) App.context.getBean(klass)
Application B has a class 1 that has a method init() that basically instantiates another class 2 with some external config parameters. The problem happens in the constructor of the class 2 where I try to create the bean for Myclass.
I get NoSuchBeanDefinitionException : No qualifying bean of type Myclass, However if I check(debugging) for App.context.classLoader() I can see my class defined there.
Another weird behaviour is that if I try to see Application.context from this debugging breakpoint, the context is null. This same static context is not null when I access this same attribute from the classes of project A.
I tested to add B to project A as an folder(copying everything) instead of using it as a jar. This made it work! So I think I have some problem with spring contexts here. But I really don't know what else to do.
Any suggestions would help,
Thanks!

Related

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");
}
}

How to spy on bootstrap context beans

I am using spring.factories to set bootstrap context
org.springframework.cloud.bootstrap.BootstrapConfiguration=sompePackage.MyBootstrapConfiguration
I was following what is mentioned in this link
https://cloud.spring.io/spring-cloud-commons/multi/multi__spring_cloud_context_application_context_services.html
I noticed 2 things hope you please can help me with
1- I cannot spy on any bean that is created through the bootstrap context, in other words, if I create a bean of type x in MyBootstrapConfiguration, spying on that bean with #SpyBean is not working, I can spy on all other beans but the bootstrap context ones (I am using springboottest)
2- If I inject ApplicationContext somewhere and print all the defined beans, i cannot see the beans that were created in the bootstrap context, in other words, the bean x that is created in MyBootstrapConfig is not there. However, #Autowired is working fine and the bean is injected correctly.
My questions are:
1- How can i spy or mock the bootstrap context beans?
2- If i cannot find these beans in ApplicationContext, where are they?
Thanks,
try to put break point where you are creating your bean and run with debug mode, then you will find out whether your bean is created or not
You could define your own context for the test scope you are trying to run:
This would look something like:
public class TestApplicationContext implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(final ApplicationContext context) throws
BeansException {
TestApplicationContext.context = context;
}
public static Object getBean(final String beanName) {
return context.getBean(beanName);
}
}
Then you can create a bean for the context in a #Configuration annotated class.
#Bean
public TestApplicationContext testApplicationContext() {
return new TestApplicationContext();
}
Then you can easily reference whatever bean you need by simply:
TestApplicationContext.getBean("someBean");

What's the minimal configuration needed to init an Autowired member in a web-app?

What's the minimal configuration needed to have the following class member to be initialized using the #Autowired annotations:
public class A {
#Autowired
private B b;
// ...
}
When invoking A a = new A(), I'd like b to be initialized from a predefined bean without the need to configure it in code.
Probably some files are needed: A.java, web.xml, spring-context.xml (for configuring B) and jars(spring and a jar containing B).
What's the minimal needed configuration and files content?
Based on this post, I created the this project. Steps to have B initiated:
Download and extract the compressed folder.
Run mvn clean install.
Copy the war (spring-autowired-1.0-SNAPSHOT.war) from the target to a web server's webapps folder.
Run the server. (for tomcat: ./catalina.sh run)
Call the API and see B's hascode - curl -X GET http://localhost:8080/spring-autowired-1.0-SNAPSHOT/rest/a/a.
See b is initialized - not null.
The actual class:
#Component
#Path("/a")
public class A {
#Autowired
B b;
#GET
#Path("/a")
public String a() {
return b.toString();
}
}
* The difference between my implementation vs. mkyong's is that my pom has less dependencies and #Autowired member is not an interface.
If for some reason you cant configure class A to be a bean in your application context you can have class A implement SpringBeanAutowingSupport. This works in a web environment.
The SpringBeanAutowingSupport default constructor looks up the application context from the request. It then injects the dependencies.
public class A extends SpringBeanAutowiringSupport{
#Autowired
private B b;
}

Spring : Create beans at runtime

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?

Using class.forname but want to autowire members of the target class

I have this requirement,
My framework is in a way that it reads the class name from the configuration file as a string and I would like to use methods inside that class.
Obvious solution is to use reflection,
I have used reflection and able to call methods I wanted, but the problem is the variables inside the target class are not autowired. I understand I am not letting spring to autowire the fields by using reflection (Spring with class.forname()).
Is there a way for me to autowire the class variables instead of creating new instance? Or Am I in a deadlock situation?
Option 1: If you have access to the current Spring ApplicationContext, you could do this as follows:
String className = <load class name from configuration>
Class<?> clazz = Class.forName(className);
ApplicationContext applicationContext = <obtain Spring ApplicationContext>
applicationContext.getBean(clazz);
This of course means that the class whose instance you wish to load is a Spring managed bean. Here is a concrete example:
package org.example.beans;
#Component
class Foo { ... }
#Component
class SpringApplicationContext implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
#Override
public void setApplicationContext(final ApplicationContext context) throws BeansException
CONTEXT = context;
}
public static <T> T getBean(String className) {
return CONTEXT.getBean(Class.forName(className));
}
}
Option 2: You could manually create an instance of the required class and then ask Spring to populate its dependencies.
This again requires access to the ApplicationContext. For example:
T object = Class.forName(className).newInstance();
applicationContext..getAutowireCapableBeanFactory().autowireBean(object);
It's possible. Have a look at how Spring's JUnit test integration does it. That's in the spring-test module.
The runner is SpringJUnit4ClassRunner, but the actual injection code is in DependencyInjectionTestExecutionListener.injectDependencies. It uses a Spring context that implements AutowriteCapableBeanFactory.
The code to do this looks like below. Note that this assumes that you have used annotations to indicate which fields need to be autowired.
Object bean = ...;
AutowireCapableBeanFactory beanFactory = ...;
beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
beanFactory.initializeBean(bean, "beanName");

Categories

Resources