如何将 spring bean 注入 Spring 引导控制台应用程序中的非托管 bean?
How do I inject spring beans into non-managed beans in a Spring boot console application?
我已尝试遵循此处的建议:https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
才发现它正在编译但实际上并没有做它应该做的事情。未在非托管对象中设置自动装配的 bean。
@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";
}
}
这就是代码的要点。完整代码在 github 此处结束:https://github.com/AlexMakesSoftware/SpringConsoleApp3
我得到的输出是:
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)
这只能意味着 @Autowired MessageSender 没有被注入。
知道我做错了什么吗?
编辑:我应该指出,这是一个更复杂项目的简单示例,用于将 Spring 缓慢集成到遗留代码库中。我不能简单地 'make it all Spring',也不能移动 Spring 初始化的位置,因为这个遗留代码是从另一个用另一种语言编写的应用程序(尽管更简单)调用的,但是 运行虚拟机。是的,这很可怕,我知道。
问题是:
您正在使用 new
关键字初始化 LegacyCode
。现在的问题是 LegacyCode
使用 autowire
,它仅适用于使用 Spring 创建的 bean。因此 NPE 作为 @autowired
将不适用于 new
.
解决方案:
您可以用 @Component
标记您的 LegacyCode
,然后在 ApiClient
中标记 autowire
。这样 MessageSender
bean 将被创建并可用。
@SpringBootApplication
注释必须在您的主 class 中使用。因此将它从 SpringApp
移动到 DummyApplication
.
解决此问题的一种方法是使用 ApplicationContextAware,如下所示:
/** 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;
}
}
然后您可以在遗留代码的构造函数中调用它来检索您需要的内容:
public LegacyCode() {
sender = SpringContext.getBean(MessageSender.class);
}
...它有效。
好的,您现在对这个 SpringContext class 有依赖性,但如果您 Spring-ify 整个应用程序并且它还不错。
我没有克隆你的项目,只是开始在浏览器中快速查看你的 POM。立即跳到我面前的是:
你的 Maven POM 是错误的。您只需在 pluginManagement
部分中预配置 AspectJ Maven 插件,而无需在常规 plugins
部分中实际声明该插件。也就是说,该插件不会在您的构建过程中使用。
如果修复后还有后续问题,我可以再看一下。
更新:我之前说的是真的,但我也注意到你的POM和测试代码中还有一些疏忽:
- 您使用了一个不存在的 AspectJ Maven 插件版本。
- 对于编译时编织,您需要 class 路径上的 AspectJ 运行时
aspectjrt
。
- 你需要
spring-tx
在依赖列表中,否则将找不到spring-aspects
引用的class。
- 您的测试预期结果不同于实际的“ok”。
更新 2: 您可以简单地接受 this pull request。
我已尝试遵循此处的建议:https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
才发现它正在编译但实际上并没有做它应该做的事情。未在非托管对象中设置自动装配的 bean。
@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";
}
}
这就是代码的要点。完整代码在 github 此处结束:https://github.com/AlexMakesSoftware/SpringConsoleApp3
我得到的输出是:
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)
这只能意味着 @Autowired MessageSender 没有被注入。
知道我做错了什么吗?
编辑:我应该指出,这是一个更复杂项目的简单示例,用于将 Spring 缓慢集成到遗留代码库中。我不能简单地 'make it all Spring',也不能移动 Spring 初始化的位置,因为这个遗留代码是从另一个用另一种语言编写的应用程序(尽管更简单)调用的,但是 运行虚拟机。是的,这很可怕,我知道。
问题是:
您正在使用 new
关键字初始化 LegacyCode
。现在的问题是 LegacyCode
使用 autowire
,它仅适用于使用 Spring 创建的 bean。因此 NPE 作为 @autowired
将不适用于 new
.
解决方案:
您可以用 @Component
标记您的 LegacyCode
,然后在 ApiClient
中标记 autowire
。这样 MessageSender
bean 将被创建并可用。
@SpringBootApplication
注释必须在您的主 class 中使用。因此将它从 SpringApp
移动到 DummyApplication
.
解决此问题的一种方法是使用 ApplicationContextAware,如下所示:
/** 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;
}
}
然后您可以在遗留代码的构造函数中调用它来检索您需要的内容:
public LegacyCode() {
sender = SpringContext.getBean(MessageSender.class);
}
...它有效。
好的,您现在对这个 SpringContext class 有依赖性,但如果您 Spring-ify 整个应用程序并且它还不错。
我没有克隆你的项目,只是开始在浏览器中快速查看你的 POM。立即跳到我面前的是:
你的 Maven POM 是错误的。您只需在 pluginManagement
部分中预配置 AspectJ Maven 插件,而无需在常规 plugins
部分中实际声明该插件。也就是说,该插件不会在您的构建过程中使用。
如果修复后还有后续问题,我可以再看一下。
更新:我之前说的是真的,但我也注意到你的POM和测试代码中还有一些疏忽:
- 您使用了一个不存在的 AspectJ Maven 插件版本。
- 对于编译时编织,您需要 class 路径上的 AspectJ 运行时
aspectjrt
。 - 你需要
spring-tx
在依赖列表中,否则将找不到spring-aspects
引用的class。 - 您的测试预期结果不同于实际的“ok”。
更新 2: 您可以简单地接受 this pull request。