Home > Mobile >  How do I inject spring beans into non-managed beans in a Spring boot console application?
How do I inject spring beans into non-managed beans in a Spring boot console application?

Time:10-07

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.

CodePudding user response:

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.

I hope this helps you and clears your doubt. If not, let me know and I will help you solve it.

Good luck and Happy coding!

CodePudding user response:

@SpringBootApplication annotation must be used in your main class. Thus move it from SpringApp to DummyApplication.

CodePudding user response:

Honestly, the easiest and cleanestway 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.

But if anyone has a better idea, please provide one and I'll review. Thanks.

  • Related