英文:
Spring Boot 2.x Jackson ObjectMapper instance does not get injected in service class - tried both boot default & with bean method from config class
问题
我在使用Spring Boot 2.3.4.RELEASE注入Jackson的ObjectMapper时遇到了奇怪的行为。
我在Maven依赖中同时添加了spring-boot-starter-web
和spring-boot-starter-json
。然而,当我在一个@Service
类中尝试自动注入ObjectMapper
时,它并没有被注入,引用是null。
然后,我尝试在一个@Configuration
类的@Primary
@Bean
方法中手动创建并返回一个ObjectMapper
,但结果仍然相同。
我知道@Configuration
是正常工作的,因为它内部的其他Bean方法被用来正确地注入其他对象。此外,我还在返回ObjectMapper
实例的Bean方法内部添加了日志语句,这些日志也被记录下来,然而在@Service
类中的引用仍然是null?(我还尝试徒劳地添加了@AutoConfigureAfter(JacksonAutoConfiguration.class)
)
还有其他人遇到过这个问题吗?或者有谁知道发生了什么?请指点一下...
谢谢
编辑(10/22):以下是@Service
类的片段,
@Lazy
@Service
@EnableSpringConfigured
public class SampleServiceSingletonService {
private static final Logger LOGGER = LoggerFactory.getLogger(SampleServiceSingletonService.class);
@Autowired
protected ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
private ObjectMapper objectMapper;
@EventListener({ApplicationReadyEvent.class})
private void initOnAppReady() {
LOGGER.info("不需要创建ObjectMapper实例,大多数定制可以在application.properties中设置/覆盖,在这里可以找到参考");
LOGGER.info("注入的ObjectMapper: {}", objectMapper);
LOGGER.info("应用就绪初始化完成..");
//...
}
}
编辑 2(10/22):对于其他遇到这个问题的人,
问题似乎是(感谢下面的@M. Deinum),在ApplicationReadyEvent
被触发并且其事件处理程序被调用的时候,实例化和/或注入似乎并没有发生。这对我来说很奇怪,因为根据文档,该事件“尽可能晚地发布,以指示应用程序已准备好提供服务...因为到那时所有的初始化步骤都已经完成”,而且我之前没有看到过其他对象的注入出现这种行为,所以我从未怀疑过这可能是一个原因。以下是我看到注入正常工作的片段,
@Lazy
@Service
public class SampleServiceSingletonService {
private static final Logger LOGGER = LoggerFactory.getLogger(SampleServiceSingletonService.class);
@Autowired
public ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
public ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
public ObjectMapper objectMapper;
@EventListener(ApplicationReadyEvent.class)
private void initOnAppReady() {
// 这里是null..
//LOGGER.info("The Injected ObjectMapper: {}", objectMapper);
LOGGER.info("应用就绪初始化完成..");
runAsync();
}
@Async
public void runAsync() {
LOGGER.info("这是异步运行的。");
// 现在不再是null了
LOGGER.info("The Injected ObjectMapper: {}", objectMapper);
}
}
谢谢
英文:
I am seeing a weird behavior with injecting Jackson ObjectMapper
with Spring Boot 2.3.4.RELEASE.
I have both spring-boot-starter-web
& spring-boot-starter-json
in my maven dependencies. Yet when i auto-wire ObjectMapper
in one of my @Service
classes, it does not get injected & the reference is null.
I then tried creating & returning one manually in an @Primary
@Bean
bean method of a @Configuration
class but still with the same result.
I know the @Configuration
is working fine since other bean methods in it are used to inject other objects correctly. In addition i also added a log statement inside the bean method that returns the ObjectMapper
instance which is also getting logged, yet the reference on the @Service
class is STILL null? (i also tried adding @AutoConfigureAfter(JacksonAutoConfiguration.class)
in vain)
Anyone else faced this or knows what's going on here? please throw light..
Thanks
EDIT (10/22): Below is a snippet of the @Service
class,
@Lazy
@Service
@EnableSpringConfigured
public class SampleServiceSingletonService {
private static final Logger LOGGER = LoggerFactory.getLogger(SampleServiceSingletonService.class);
@Autowired
protected ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
private ObjectMapper objectMapper;
@EventListener({ApplicationReadyEvent.class})
private void initOnAppReady() {
LOGGER.info("No need to create ObjectMapper instances, most customizations can be set/overriden in application.properties, look at the one here for reference");
LOGGER.info("Injected ObjectMapper: {}", objectMapper);
LOGGER.info("Init on app ready complete..");
//...
}
EDIT 2 (10/22): For others who face this,
The problem appears to be (thanks to @M. Deinum below) that the instantiation and/or injection doesn't seem to happen at the time the ApplicationReadyEvent
is fired & its event handlers are invoked. That seems strange to me for two reasons, one is as per the docs, the event is "published as late as conceivably possible to indicate that the application is ready to service requests...since all initialization steps will have been completed by then" and second i have not seen this behavior with other injections of other objects till now so i never suspected this as a cause. Below is a snippet where i see the injection working,
@Lazy
@Service
public class SampleServiceSingletonService {
private static final Logger LOGGER = LoggerFactory.getLogger(SampleServiceSingletonService.class);
@Autowired
public ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
public ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
public ObjectMapper objectMapper;
@EventListener(ApplicationReadyEvent.class)
private void initOnAppReady() {
// Its null here..
//LOGGER.info("The Injected ObjectMapper: {}", objectMapper);
LOGGER.info("Init on app ready complete..");
runAsync();
}
@Async
public void runAsync() {
LOGGER.info("This is run asynchronously.");
// Its no longer null here
LOGGER.info("The Injected ObjectMapper: {}", objectMapper);
}
}
Thanks
答案1
得分: 1
你已经使用@Lazy
注解标记了你的bean。当应用在一个类型上时,会创建一个惰性代理对象。在这种情况下,将会创建一个基于类的代理(类上没有接口),为此会创建一个子类以添加惰性行为。
然而,由于你的方法是private
的,无法在动态创建的类中对其进行重写,因此该方法将在代理上被调用,而不是传递到实际对象。代理对象将不会注入任何依赖项,至少不会有private
字段的注入。
所以,为了修复这个问题,将你的方法改为protected
或者public
,这样它就可以被正确地子类化和重写。或者如果不需要的话,移除@Lazy
注解。
更详细的解释请参考这篇博客。
英文:
You have marked your bean with @Lazy
. When put on a type what will happen is that a lazy proxy will be created for the object. In this case a class based proxy will be created (no interface on the class) and for this a subclass will be created to add the lazy behavior.
However due to the fact that your method is private
there is no way to override this method in the dynamic created class and thus it will be called on the proxy instead or relaying it through to the actual object. The proxy object will not have any dependencies injected, at least not the private
fields.
So to fix, make your method protected
or public
so it can be properly subclasses and overridden. Or remove the @Lazy
if you don't need it.
See this blog for a more detailed explanation.
答案2
得分: -2
你应该为你的私有ObjectMapper
字段添加setter
和getter
,这样Spring就能够为其赋值。
英文:
You should add setter
and getter
to your
private ObjectMapper objectMapper;
so Spring is able to assign it a value
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论