春天@Autowired和Singletons

时间:2014-03-04 22:36:21

标签: spring

我有一个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());
            }
        }
    }

2 个答案:

答案 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"),以避免有关未使用字段的警告。反思地使用了它们。

可以随时窃取或建议改进!

干杯!