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.