Reputation: 1167
I've tried to follow the advice found here: https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
Only to find that it's compiling but not actually doing what it should. The autowired bean is not being set in the non-managed object.
@SpringBootApplication
@EnableSpringConfigured
public class SpringApp {
...
public class ApiClient {
private static String result = "not set";
//this would be called from another applicaiton written in a different language potentially.
public String apiInterface(String message) {
//This is where we're going to have to create Spring, where the languages 'join'.
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringApp.class);
builder.run();
System.out.println("Running legacy code...");
LegacyCode oldCode = new LegacyCode();
result = oldCode.doLegacyStuff("hello world");
return result;
}
}
...
@Configurable(preConstruction = true)
public class LegacyCode {
@Autowired
MessageSender sender; //let's pretend we Spring-fied this bit of code but not the Legacy code that uses it.
public String doLegacyStuff(String message) {
sender.send(message);
sender.close();
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
return "interupted";
}
return "ok";
}
}
That's the gist of the code. The full code is over at github here:https://github.com/AlexMakesSoftware/SpringConsoleApp3
The output I get is:
Exception in thread "main" java.lang.NullPointerException
at demo.LegacyCode.doLegacyStuff(LegacyCode.java:13)
at demo.ApiClient.apiInterface(ApiClient.java:17)
at demo.DummyApplication.main(DummyApplication.java:7)
Which can only mean that the @Autowired MessageSender isn't getting injected.
Any ideas what I'm doing wrong?
EDIT: I should point out that this is a simple example of a more complicated project to slowly integrate Spring into a legacy codebase. I cannot simply 'make it all Spring', nor can I shift the location of Spring's initialisation because this legacy code gets called from another application (albeit a simpler one) written in another language but running in the JVM. Yes, it's horrible, I know.
Upvotes: 0
Views: 3308
Reputation: 1167
One way to solve this problem is to use ApplicationContextAware, like so:
/** In a perfect world, this wouldn't exist. Maybe one day we can spring-ify everything and this won't need to. */
@Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContext.context = applicationContext;
}
}
You can then call this in the constructor of your legacy code to retrieve what you need to like so:
public LegacyCode() {
sender = SpringContext.getBean(MessageSender.class);
}
...and it works.
Ok, you now have a dependency on this SpringContext class but that will go in time if you Spring-ify the entire app and it's not so bad.
Upvotes: 0
Reputation: 545
Problem is :
You are initializing your LegacyCode
using new
keyword. Now problem is that LegacyCode
uses autowire
which works only for beans created using Spring. Hence NPE as @autowired
will not work with new
.
Solution :
You can mark your LegacyCode
with @Component
and then autowire
it in ApiClient
. This way MessageSender
bean will be created and will be available.
Upvotes: 1
Reputation: 67477
I did not clone your project, just started to take a quick look at your POM in the browser. What immediately jumped at me was this:
Your Maven POM is wrong. You only preconfigure the AspectJ Maven Plugin in the pluginManagement
section without ever actually declaring the plugin in the regular plugins
section. I.e., the plugin is not going to be used during your build.
If after fixing that you are still having follow-up problems, I can take a second look.
Update: What I said before is true, but I also noticed some more oversights in your POM and test code:
aspectjrt
on the classpath.spring-tx
on the dependency list, otherwise a class referred to by spring-aspects
will not be found.Update 2: You can simply accept this pull request.
Upvotes: 1
Reputation: 17510
@SpringBootApplication
annotation must be used in your main class. Thus move it from SpringApp
to DummyApplication
.
Upvotes: 0