Ответ 1
Вы должны объявить поле INSTANCE
как volatile
для корректной работы блокировки с двойным проверкой.
См. Эффективная Java, Пункт 71.
Я конвертирую синглтон в Spring bean, так что, если singleton не удается инициализировать, контекст полного веб-приложения Spring не загружается должным образом.
Преимущество в том, что контекст Spring не загружается должным образом, заключается в том, что люди будут замечать и исправлять конфигурацию во время развертывания. В отличие от использования 'non spring bean' singleton: когда это генерирует исключение во время инициализации, никто не замечает.. пока фактический пользователь не пожалуется на недостающие функциональные возможности.
Мои изменения работают так, как ожидалось. но я не уверен, что я поступаю правильно.
Любые мысли?
Код выглядит следующим образом:
public class MySingleton {
private static MySingleton INSTANCE = null;
private MySingleton(){}
public static MySingleton getInstance(){
if(INSTANCE == null){
synchronized(MySingleton.class){
if(INSTANCE == null){
try{
doWork()
}catch(Exception e){
throw new IllegalStateException("xyz", e);
}
INSTANCE = new MySingleton();
}
}
}
return INSTANCE;
}
private static void doWork() {
// do some work
}
}
И в Spring config xml, bean будет определен как:
<bean id="MySingletonBean"
class="com.MySingleton"
factory-method="getInstance" lazy-init="false" singleton="true">
</bean>
Примечание: Большинство из них аналогично стратегии, обсуждаемой в этой статье: http://springtips.blogspot.com/2007/06/configuration-hell-remedy-with.html
Классы, которые используют этот синглтон, не сами Spring beans.. они просто не-w763 > pojos, что я не могу преобразовать в spring. Они должны полагаться на метод getInstance(), чтобы получить синглтон.
Изменить 2: (копирование комментария, сделанного ниже в этом разделе описания) Я пытаюсь настроить две вещи:
Вы должны объявить поле INSTANCE
как volatile
для корректной работы блокировки с двойным проверкой.
См. Эффективная Java, Пункт 71.
Почему вы используете одноэлементный шаблон на первом месте? Просто позвольте Spring создать bean для вас (с областью по умолчанию singleton
) и... использовать его. Конечно, всегда кто-то может создать bean вручную, но это никогда не было проблемой в моем случае.
Включение зависимостей и Spring -установленный bean жизненный цикл значительно облегчат вашу жизнь (просто посмотрите, сколько ошибок вы можете избежать). Также обратите внимание, что исключения, отбрасываемые из метода c-tor или @PostContruct
, будут распространяться и также запускать запуск контекста приложения.
ОБНОВЛЕНИЕ. Я понимаю вашу точку зрения. Вот что мне пришло в голову:
@Service
public class Singleton {
private static AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
public Singleton() {
final Singleton previous = INSTANCE.getAndSet(this);
if(previous != null)
throw new IllegalStateException("Second singleton " + this + " created after " + previous);
}
public static Singleton getInstance() {
return INSTANCE.get();
}
}
И пусть Spring выполнит свою работу. Вы можете использовать DI, когда это возможно, и Singleton.getInstance()
, где вам нужно.
Кроме того, существуют более жесткие решения, такие как компиляция AspectJ и впрыскивание Spring beans в основном ко всему.
Я не уверен, почему вы хотите это сделать. Когда вы укажете Spring, что bean должен быть одноточечным, соответствующий класс не должен быть одиночным, и ему не нужен factory. Spring просто просто создает только один экземпляр.
Связанная статья не имеет для меня никакого смысла, так как нет НИКАКОЙ инъекции, которую я вижу: "AnyService" вызывает метод singleton factory; что singleton ссылается в контексте приложения, не имеет значения до тех пор, пока он не ссылается, и, похоже, никакой другой bean не ссылается на него.
True singleton трудно работать.
Неустойчивая блокировка с двойной проверкой также не работает. Читайте об этом на wiki http://en.wikipedia.org/wiki/Double-checked_locking
Лучше всего просто сделать это
public class MySingleton {
private static MySingleton INSTANCE = new MySingleton();
То есть, если у вас нет каких-либо параметров конструктора в вашем реальном коде.
По моему мнению, это решение для ремней и подтяжек.
Если вы создаете bean и объявляете его как одноэлемент в конфигурации, тогда не нужно защищать bean от того, чтобы его было многократно создано.
Теперь вы в основном защищаете себя от кого-то, неправильно настроив bean.
Я лично "решаю", что по документации в конфигурации spring и Javadoc.
Для запуска кода при запуске (и сбое при ошибке) используйте один из многих способов регистрации событий запуска, например. см. http://www.baeldung.com/running-setup-logic-on-startup-in-spring
Пример:
@Component
public class InitializingBeanExampleBean implements InitializingBean {
private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class);
@Autowired
private Environment environment;
@Override
public void afterPropertiesSet() throws Exception {
LOG.info(Arrays.asList(environment.getDefaultProfiles()));
}
}