英文:
Spring: Inject a value into a statically accessed class
问题
Consider the following class:
public class ExpressionTokenClassifier {
private static final String PROMQL_KEYWORDS = "promql-keywords.json";
private static final List<String> KEYWORDS;
static {
List<PromQLKeyword> words = KeywordsExtractor.extractKeywords(PROMQL_KEYWORDS);
KEYWORDS = words.stream().map(PromQLKeyword::getName).collect(Collectors.toList());
}
public static void classifyTokens(List<ExpressionToken> tokens) {
... // KEYWORDS is used in here
}
}
Notice the hardwired file name (PROMQL_KEYWORDS
) specified and used in the static initialization block. This works, but I would like to remove the hardcoded value and instead use dependency injection (Spring) to set the value from application.yaml
. This class is intended to only be used statically.
My research indicates that the Spring framework does not allow setting a static field with the @Value
annotation. I have seen some suggestions of a workaround, but do not understand how they would work. Here is an example where I attempted to do this:
@Component
public class ExpressionTokenClassifier2 {
private static final List<String> KEYWORDS;
private static String KEYWORDS_FILE;
@Value("${app.keywords-file}")
public void setKeywordsFileName(String name) {
KEYWORDS_FILE = name;
}
static {
List<PromQLKeyword> words = KeywordsExtractor.extractKeywords(KEYWORDS_FILE);
KEYWORDS = words.stream().map(PromQLKeyword::getName).collect(Collectors.toList());
}
public static void classifyTokens(List<ExpressionToken> tokens) {
... // KEYWORDS is used in here
}
}
When I tried it, the application fails to start because of NPE (KEYWORDS_FILE
is null). My immediate question is: Where and when would the setKeywordsFileName()
method be called? Nowhere, as far as I can tell - and the results agree. Am I missing some logic?
Does anyone know of a way to accomplish this? I could change this class so that it's instantiated instead of statically accessed, but the class that uses this class is also a statically accessed class. I really don't want to have to change a bunch of code to fix this.
UPDATE:
Upon further investigation, it appears that this is essentially not doable, at least not without a lot of extra work that probably defeats the purpose. This link, to me, describes the situation quite well.
Looks like I'm going to have to think about this for awhile.
英文:
Consider the following class:
public class ExpressionTokenClassifier {
private static final String PROMQL_KEYWORDS = "promql-keywords.json";
private static final List<String> KEYWORDS;
static {
List<PromQLKeyword> words = KeywordsExtractor.extractKeywords(PROMQL_KEYWORDS);
KEYWORDS = words.stream().map(PromQLKeyword::getName).collect(Collectors.toList());
}
public static void classifyTokens(List<ExpressionToken> tokens) {
... // KEYWORDS is used in here
}
}
Notice the hardwired file name (PROMQL_KEYWORDS
) specified and used in the static initialization block. This works, but I would like to remove the hardcoded value and instead use dependency injection (Spring) to set the value from application.yaml
. This class is intended to only be used statically.
My research indicates that the Spring framework does not allow setting a static field with the @Value
annotation. I have seen some suggestions of a workaround, but do not understand how they would work. Here is an example where I attempted to do this:
@Component
public class ExpressionTokenClassifier2 {
private static final List<String> KEYWORDS;
private static String KEYWORDS_FILE;
@Value("${app.keywords-file}")
public void setKeywordsFileName(String name) {
KEYWORDS_FILE = name;
}
static {
List<PromQLKeyword> words = KeywordsExtractor.extractKeywords(KEYWORDS_FILE);
KEYWORDS = words.stream().map(PromQLKeyword::getName).collect(Collectors.toList());
}
public static void classifyTokens(List<ExpressionToken> tokens) {
... // KEYWORDS is used in here
}
}
When I tried it, the application fails to start because of NPE (KEYWORDS_FILE
is null). My immediate question is: Where and when would the setKeywordsFileName()
method be called? Nowhere, as far as I can tell - and the results agree. Am I missing some logic?
Does anyone know of a way to accomplish this? I could change this class so that it's instantiated instead of statically accessed, but the class that uses this class is also a statically accessed class. I really don't want to have to change a bunch of code to fix this.
UPDATE:
Upon further investigation, it appears that this is essentially not doable, at least not without a lot of extra work that probably defeats the purpose. This link, to me, describes the situation quite well.
Looks like I'm going to have to think about this for awhile.
答案1
得分: 2
I would just use a @Component
and @PostConstruct
method for init task:
Spring conponent is singleton, so no need for static.
@Component // 单例
public class ExpressionTokenClassifier {
@Value("${app.keywords-file}")
private String file;
private List<String> KEYWORDS;
@PostConstruct
public void init() {
[...]
}
}
if static access is essential, you can init your static class as soon as the application is started:
public class ExpressionTokenClassifier {
private List<String> KEYWORDS;
static void init(String file) {
List<PromQLKeyword> words = KeywordsExtractor.extractKeywords(file);
KEYWORDS = words.stream().map(PromQLKeyword::getName).collect(Collectors.toList());
}
public static void classifyTokens(List<ExpressionToken> tokens) {
// KEYWORDS is used in here
}
}
@Component
public class ExpressionTokenClassifierInitializer {
@Value("${app.keywords-file}")
private String file;
@EventListener(ContextRefreshedEvent.class)
public void onApplicationStartedEvent() {
ExpressionTokenClassifier.init(file);
}
}
英文:
I would just use a @Component
and @PostConstruct
method for init task:
Spring conponent is singleton, so no need for static.
@Component // singleton
public class ExpressionTokenClassifier {
@Value("${app.keywords-file}")
private String file;
private List<String> KEYWORDS;
@PostConstruct
public void init() {
[...]
}
}
if static access is essential, you can init your static class as soon as the application is started:
public class ExpressionTokenClassifier {
private List<String> KEYWORDS;
static void init(String file) {
List<PromQLKeyword> words = KeywordsExtractor.extractKeywords(file);
KEYWORDS = words.stream().map(PromQLKeyword::getName).collect(Collectors.toList());
}
public static void classifyTokens(List<ExpressionToken> tokens) {
// KEYWORDS is used in here
}
}
@Component
public class ExpressionTokenClassifierInitializer {
@Value("${app.keywords-file}")
private String file;
@EventListener(ContextRefreshedEvent.class)
public void onApplicationStartedEvent() {
ExpressionTokenClassifier.init(file);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论