Spring, вводящий статический (глобальный) синглтон

У меня есть класс, который выглядит так:

public class Configurator {
    private static Configurator INSTANCE = null;

    private int maxRange = 1;

    // many other properties; each property has a default value

    private static synchronized Configurator getInstance() {
        if(INSTANCE == null)
            return new Configurator();

        return INSTANCE;
    }

    public static int getMaxRange() {
        getInstance().maxRange;
    }

    public static void setMaxRange(int range) {
        getInstance().maxRange = range;
    }

    // Getters and setters for all properties follow this pattern
}

Он служит в качестве глобального объекта конфигурации, который может быть установлен при запуске приложения, а затем используется десятками классов по всему проекту:

// Called at app startup to configure everything
public class AppRunner {
    Configurator.setMaxRange(30);
}

// Example of Configurator being used by another class
public class WidgetFactory {
    public void doSomething() {
        if(Configurator.getMaxRange() < 50)
            // do A
        else
            // do B
    }
}

Теперь я импортирую этот код в проект Spring и пытаюсь настроить свой Sprinig XML (beans). Я предполагаю, что я мог бы определить одиночный Configurator bean так (или что-то подобное):

<bean id="configurator" class="com.me.myapp.Configurator" scope="singleton">
    <property name="maxRange" value="30"/>
    <!-- etc., for all properties -->
</bean>

Таким образом, когда WidgetFactory#doSomething выполняется, Spring уже загрузил класс Configurator и настроил его раньше времени.

Правильно ли мне установить scope="singleton", или это не имеет значения? Правильно ли устанавливаю статические свойства? Есть ли что-нибудь еще, что мне нужно сделать или рассмотреть здесь? Спасибо заранее.

Ответ 1

Существует некоторая разница между Синглтоном как шаблоном проектирования и установкой Spring singleton. Синглтон как шаблон проектирования гарантирует, что у вас есть один объект класса, определенный для Class Loader. Spring singleton facility (и подход), напротив, определит один экземпляр для контекста Spring.

В этом случае вы можете использовать свой метод getInstance(), который будет использоваться Spring, чтобы захватить экземпляр объекта:

<bean id="configurator" class="com.me.myapp.Configurator" factory-method="getInstance">
</bean>

С Spring область singleton bean является значением по умолчанию, поэтому вам не нужно ее определять.

Если вы хотите использовать configurator как Spring bean, вам придется вводить его в другие объекты, а не использовать getInstance() для его захвата. Поэтому в другом Spring beans используйте @Autowired или определите ссылку на bean через xml файл. Если вы не реорганизовываете использование configurator в других классах, разница не будет, Spring будет создавать экземпляр вашего класса, но вы будете использовать его по-прежнему.

Также я увидел, что у вас возникла ошибка при разработке вашего синглтона. Ваш метод getInstance() должен быть общедоступным, а другие методы не должны быть статическими. В примере, который вы использовали, вы должны использовать Singleton следующим образом:

Configurator.getInstance().someMethod()

В этом случае вы просто используете класс Singleton, не создавая экземпляров каких-либо объектов! См. wikipedia на Singleton (с примером Java) для получения дополнительной информации о шаблоне проектирования Singleton и о том, как его использовать.


ПРИМЕЧАНИЕ. Стоит знать и пытаться использовать configurator в качестве Singleton и использовать средство Spring singleton. Если вы сделаете это, преимущества будут в том, что вы можете
  • Удалите getInstance() метод
  • Сделайте свой конструктор общедоступным.
  • Пусть Spring создает экземпляр этого единственного объекта.

Ответ 2

Beans по умолчанию являются одноточечными. Вы можете найти эту/дополнительную информацию через веб-сайт spring.

Вы не должны создавать экземпляр нового конфигуратора в getInstance, потому что он не будет ссылаться на spring загруженный bean и может вызвать некоторые серьезные проблемы. Вы можете подключить этот bean, а затем оставить его в покое, он не будет пустым, потому что вы его подключили (и если у вашей программы будет неудачная инициализация).

Ответ 3

Да, если вы хотите что-то глобальное, однопользовательская область - это правильный вариант. несколько вещей, которые стоит упомянуть здесь:

  • Область по умолчанию в spring является одиночной, поэтому вам не нужно явно задайте bean как одноэлементную область.
  • Используя spring, вам не нужно писать код стиля стиля Singleton, например частные экземпляры и методы factory. Это потому, что spring убедитесь, что существует только один экземпляр на Spring контейнер. Даже не сказать, ваш метод factory является закрытым.

Ответ 4

Кстати: это не Thread-safe:

if(INSTANCE == null)
        return new Configurator();

    return INSTANCE;
}

В то время как это было бы:

private static Configurator INSTANCE = new Configurator();

(Eager Initialization)

private static volatile Singleton _instance = null;

(ленивая инициализация с ключевым словом volatile)

Это связано с тем, как Java выделяет память и создает экземпляры. Это не атомный, но выполняется в два этапа и может быть помешано планировщиком потоков.

См. также http://regrecall.blogspot.de/2012/05/java-singleton-pattern-thread-safe.html.