我有一个Spring应用程序,它使用持久存储在数据库中的各种配置参数。为了最大限度地减少数据库访问周期,我创建了一个Singleton类,它将参数保存在Properties对象中。有时在运行应用程序期间需要刷新Properties对象,因此为此我有一个load()方法和一个reload()方法。为了访问数据库,我有一个@Autowired服务对象。简化一点:
public class AppConfig {
private static AppConfig instance = null;
private Properties appProperties;
@Autowired ConfiguraitonService configService;
protected AppConfig() {
load();
}
public static AppConfig getInstance() {
if (instance == null) {
instance = new AppConfig();
}
return instance;
}
public void reload() {
load();
}
private void load() {
List<Configuration> configList configService.findAll()
for (Configuration myConfiguration : configList) {
if (myConfiguration != null && myConfiguration.getAttribute() != null) {
appProperties.setProperty(myConfiguration.getAttribute(),myConfiguration.getValue());
}
}
}
public String getValue(String key) {
return appProperties.getProperty(key);
}
在Spring配置文件中,我有:
<bean id="appConfigBean" class="foo.bar.AppConfig"></bean>
对此Singleton调用'getValue'会生成空指针异常。据我所知,虽然我不明白为什么,但这与@Autowired有关并且无法正确初始化。我想我的问题与解决这个问题的最佳方法有关。
对于其他人来说,这是修改后的代码:
public class AppConfig {
private static Properties myProperties = new Properties();
@Autowired
private ConfigurationService configService;
private static AppConfig instance = null;
protected AppConfig() {
}
public static AppConfig getInstance() {
if (instance == null) {
instance = new AppConfig();
}
return instance;
}
@PostConstruct
public void load() {
List<Configuration> configList = configService.findAll();
for (Configuration myConfiguration : configList) {
if (myConfiguration != null && myConfiguration.getAttribute() != null) {
myProperties.setProperty(myConfiguration.getAttribute(), myConfiguration.getValue());
}
}
}
答案 0 :(得分:8)
当构造函数调用load()时,Autowired依赖项仍然是无线的。接线在构造器完成后进行。要么使用configService final并使用构造函数自动装配,要么从构造函数中删除load(),但使用@PostConstruct注释load()。
答案 1 :(得分:1)
对于任何试图使用Spring创建某种静态属性存储机制的人,我创建了一个不错的单例类,您可以使用该类来集中从环境中拉出的属性(在我的情况下为文本文件属性)和/或使用服务从数据库中获取。可以从代码中的任何位置静态调用refreshParameters()方法,以再次从其各自的源中提取所有属性(例如,可以将其分解为不同的方法,以仅提取某些类型的属性)。整洁的事情是,依靠getter方法查询实例字段可以完全隐藏对基础实例的访问。
缺点是我们不建议通过setter注入来注入静态上下文,但是通常似乎需要允许从上下文中静态拉出实例bean(请告诉我是否还有其他方法!)。另外,我确信可以提高其线程安全性,但这完全可以满足我的需求:
package com.somepackage.utilities;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import com.somepackage.service.GlobalParamService;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
@Log4j2
public class ParameterUtils
{
private static ParameterUtils instance = null;
//Core
private boolean headless = false;
//Logging
private String logLevel = "";
private static ApplicationContext applicationContext;
private final Environment environment;
private final GlobalParamService globalParamService;
@PostConstruct
//Runs on bean creation to initialize parameters from environment and DB, is also
//called by the refreshParameters() method
private void init()
{
//Core
headless = !environment.getProperty("serviceGUI").equals("ON");
//Logging
logLevel = globalParamService.findByName("logLevel").getValue();
}
//creates instance if null, getting the ParameterUtils bean from the static
//context autowired via setter-injection
private synchronized static ParameterUtils getInstance()
{
if (instance == null)
{
instance = applicationContext.getBean(ParameterUtils.class);
}
return instance;
}
//Refreshes all parameters by querying the environment and DB again
public static void refreshParameters()
{
getInstance().init();
}
//Core
public static boolean headless()
{
return getInstance().headless;
}
//Logging
public static String logLevel()
{
return getInstance().logLevel;
}
@Autowired
//Autowires static context to allow creating the fully autowired
//instance variable with a getBean() call;
public void setApplicationContext(ApplicationContext applicationContext)
{
ParameterUtils.applicationContext = applicationContext;
}
}
有了这个,我可以从代码中的任何位置静态地执行ParameterUtils.headless()来查看我是否以无头模式运行。此类消除了我程序中的数百行代码。
******编辑******
您可能会很喜欢,并使用反射将单个getter方法替换为具有可变返回类型的单个getter方法,就像我最后做的那样:
@SuppressWarnings("unchecked")
public static <T> T getParameter(String name, Class<T> returnType)
{
try
{
return (T) Stream.of(getInstance().getClass().getDeclaredFields())
.filter((field) -> field.getName().equals(name))
.findAny().get().get(instance);
}
catch (Exception ex)
{
instance.logEntryService.logError(LogEntrySource.SERVICE, LogEntryType.CORE, "Error retrieving " + name
+ " parameter : " + ex.getMessage(), log);
}
return null;
}
在这种情况下,对ParameterUtils的调用如下所示:
boolean headless = ParameterUtils.getParameter("headless", boolean.class);
请注意,您需要在班级顶部使用@SuppressWarnings("unused")
,以避免有关未使用字段的警告。反思地使用了它们。
可以随时窃取或建议改进!
干杯!