Вот мой код.
public class PropertyLoader {
private Properties appProperties;
/**
* The instance.
*/
private static PropertyLoader inst = null;
/**
* Instantiates a new property data loader.
*/
private PropertyLoader() {
try
{
appProperties = new Properties();
appProperties.load(this.getClass().getClassLoader().getResourceAsStream("app.properties"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
public static PropertyLoader getInstance() {
if (inst == null) {
inst = new PropertyLoader();
}
return inst;
}
}
public String getPropertyAPP(String key) {
return appProperties.getProperty(key);
}
}
Синхронизация необходима для метода getPropertyAPP
: это одноэлементный режим, поэтому многие потоки могут одновременно обращаться к одному экземпляру и вызывать его.
Может ли кто-нибудь предложить мне правильный путь?
вы можете использовать следующее решение
public class PropertyLoader {
private Properties appProperties;
/** The instance. */
private static PropertyLoader inst = null;
static{
inst = new PropertyLoader();
}
/**
* Instantiates a new property data loader.
*/
private PropertyLoader() {
try
{
appProperties = new Properties();
appProperties.load(this.getClass().getClassLoader().getResourceAsStream("app.properties"));
}
catch(IOException e)
{
e.printStackTrace();
}
}
public static PropertyLoader getInstance() {
return inst;
}
public String getPropertyAPP(String key) {
return appProperties.getProperty(key);
}
}
Более простым решением, которое также является безопасным потоком, является использование статической инициализации для инициализации статического поля.
public class AppProperties {
private static final Properties appProperties;
static {
try {
appProperties = new Properties();
appProperties.load(AppProperties.class
.getClassLoader().getResourceAsStream("app.properties"));
} catch(IOException e) {
e.printStackTrace();
}
}
public String get(String key) {
return appProperties.getProperty(key);
}
public String get(String key, String defaultValue) {
return appProperties.getProperty(key, defaultValue);
}
}
Несколько проблем с кодом:
(a) Вы уверены, что вам нужна ленивая инициализация? Стоимость инициализации должна быть заметной, и вероятность того, что ресурс никогда не используется, должна быть отличной от нуля. Кроме того: может быть предпочтительнее сбой во время запуска программы, а не в какой-то неопределенный момент позже, когда ресурс будет впервые доступен. Это действительно зависит от вашего приложения.
(b) Один из способов реализации ленивой инициализации - использовать правильную версию двойной проверки блокировки (ключевое слово volatile является существенным):
private static volatile PropertyLoader inst;
...
public static PropertyLoader getInstance() {
if (inst == null) {
synchronized(PropertyLoader.class) {
if (inst == null) {
inst = new PropertyLoader();
}
}
}
return inst;
}
В этой статье, посвященной википедиям, объясняется, почему это работает (начиная с Java 5, но не раньше): http://en.wikipedia.org/wiki/Double-checked_locking
(c) Захват исключений и просто их регистрация в большинстве случаев ошибочны. В вашем случае никакое свойство никогда не будет возвращено из getPropertyAPP. Это может быть нормально, если наличие свойств явно объявлено необязательным.
В вашем методе getInstance()
происходит массовое состояние гонки. Если несколько потоков обращаются к getInstance()
одновременно, вы создадите несколько экземпляров и назначаете их по очереди статической переменной. Учитывая то, что вы здесь делаете, это не вызовет каких-либо логических проблем, но это означает, что вы делаете больше работы, чем необходимо.
Я рекомендую вам немного прочитать о шаблоне Singleton, почему это плохо, и как реализовать его безопасным способом в Java.
Но, короче говоря, не используйте шаблон Singleton, он отвратительно сломан и сделает любой код, который использует этот объект тесно связан и будет препятствовать вашей способности делать какие-либо изолированное тестирование.
Singleton
для Properties
. Кстати: шаблон не нарушен - многие его реализации в Java не работают.