[英]How can I access a Spring Boot environment variable from a Jackson @JsonCreator constructor
I have an immutable model class which can be instantiated by Jackson using the @JsonCreator
annotated constructor. 我有一个不变的模型类,可以由Jackson使用@JsonCreator
注释的构造函数实例化。 One of the properties is optional and I want to have a dynamic default value based on a variable in application.yml
to replace the fixed value of 30 below. 这些属性之一是可选的,我想基于application.yml
的变量创建一个动态默认值,以替换下面的固定值30。
public class DataRequest {
private final List<String> fields;
private final int sessionTimeoutSecs;
@JsonCreator
public DataRequest(@JsonProperty("fields") final List<String> fields,
@JsonProperty("sessionTimeoutSecs") final Integer sessionTimeoutSecs) {
this.fields = fields;
this.sessionTimeoutSecs = MoreObjects.firstNonNull(sessionTimeoutSecs, 30);
}
}
I tried adding an additional constructor parameter @Value("${default-session-timeout-secs}") final int defaultSessionTimeout
but that results in a MessageConversionException
when attempting to map. 我尝试添加一个附加的构造函数参数@Value("${default-session-timeout-secs}") final int defaultSessionTimeout
但是在尝试映射时会导致MessageConversionException
。
You can do this by bridging your properties and making use of @JacksonInject
. 您可以通过桥接属性并使用@JacksonInject
。 This annotation works well with the object-mapper. 此注释与对象映射器配合使用良好。 I am skipping the part on how to bridge spring to jackson in code, but give you a plain ObjectMapper
example. 我跳过了有关如何在代码中将spring桥接到jackson的部分,但是给了您一个简单的ObjectMapper
示例。 See this code: 参见以下代码:
package de.pandaadb.jackson;
import java.io.IOException;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class DataModel {
private String test;
private String optionalValue;
@JsonCreator
public DataModel(@JsonProperty("test") String test, @JacksonInject("opt") @JsonProperty(value= "opt", required=false)String opt) {
this.optionalValue = opt;
this.test = test;
}
@Override
public String toString() {
return "test=" + test + " optional=" + optionalValue;
}
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
InjectableValues.Std std = new InjectableValues.Std();
std.addValue("opt", "alternative");
String withOptional = "{\"test\" : \"hello\" , \"opt\" : \"optss\"}";
String withoutOptional = "{\"test\" : \"hello\"}";
mapper.setInjectableValues(std);
System.out.println(mapper.readValue(withOptional, DataModel.class));
System.out.println(mapper.readValue(withoutOptional, DataModel.class));
}
}
This code prints: 此代码打印:
test=hello optional=optss
test=hello optional=alternative
Few notes: 一些注意事项:
The constructor is using the JacksonInject
annotation in the constructor in addition to a property. 除了属性之外,构造函数还使用构造函数中的JacksonInject
批注。 This will inject the configured value first, optionally overwriting it with the json value coming from the input. 这将首先注入配置的值,可选地使用来自输入的json值覆盖它。
The ObjectMapper
is being fed properties that match the annotation we have 正在向ObjectMapper
提供与我们具有的注释匹配的属性
The second part is the most important. 第二部分是最重要的。
As for making this work with spring, the approach would be to have this code in your configuration: 至于使用spring进行此工作,方法是在您的配置中包含以下代码:
@Configuration
public class ObjectMapperConfiguration {
ObjectMapper mapper(@Value("${my.test.string}") String test) {
ObjectMapper mapper = new ObjectMapper();
InjectableValues.Std std = new InjectableValues.Std();
std.addValue("my.test.string", "test");
mapper.setInjectableValues(std);
return mapper;
}
}
You can alternatively also inject ALL properties and feed them back to the object mapper, as to not having to extend this with every new property. 另外,您也可以注入ALL属性,并将其反馈给对象映射器,以免不必对每个新属性进行扩展。
I hope that helps, 希望对您有所帮助,
Some more reading material here: https://www.concretepage.com/jackson-api/jackson-jacksoninject-example#ObjectMapper 一些更多的阅读材料在这里: https : //www.concretepage.com/jackson-api/jackson-jacksoninject-example#ObjectMapper
Latest edit: How to do this with only 1 constructor argument, having jackson handle the overwrite itself 最新编辑:如何仅使用1个构造函数参数,让jackson自己处理覆盖
-- Artur -阿图尔
You can do this by using two helper classes. 您可以通过使用两个帮助程序类来实现。
The first is ContextHolder
, which allows you to access Spring's ApplicationContext from outside Spring-managed bean: 第一个是ContextHolder
,它使您可以从Spring托管的bean外部访问Spring的ApplicationContext:
@Component
public class ContextHolder implements ApplicationContextAware {
public static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ContextHolder.applicationContext = applicationContext;
}
}
The second is DefaultTimeoutWrapper
, which holds default timeout value: 第二个是DefaultTimeoutWrapper
,其中包含默认超时值:
@Component
public class DefaultTimeoutWrapper {
@Value("${default-session-timeout-secs}")
private int defaultTimeout;
public int getDefaultTimeout() {
return defaultTimeout;
}
}
Then you need to change your DataRequest
class to use them like this: 然后,您需要更改DataRequest
类以使用它们,如下所示:
public class DataRequest {
private final List<String> fields;
private final int sessionTimeoutSecs;
@JsonCreator
public DataRequest(@JsonProperty("fields") final List<String> fields,
@JsonProperty("sessionTimeoutSecs") final Integer sessionTimeoutSecs) {
DefaultTimeoutWrapper defaultTimeoutWrapper = ContextHolder.applicationContext.getBean(DefaultTimeoutWrapper.class);
int defaultTimeout = defaultTimeoutWrapper.getDefaultTimeout();
this.fields = fields;
this.sessionTimeoutSecs = sessionTimeoutSecs != null ? sessionTimeoutSecs : defaultTimeout;
}
}
Note that in ContextHolder
instance method is used to set value of a static field, which is generally considered to be bad design. 请注意,在ContextHolder
实例方法用于设置静态字段的值,通常认为这是不好的设计。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.