Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Singleton, NullPointerException


Автор: LLIbIcpEP 9.2.2011, 13:06
Доброго времени суток.
Есть синглтон:
Код

public class DataRepositoryProducer {
    private static final LoggerFilter log = LoggerFilter.getLoggerFilter(DataRepositoryProducer.class);
    
    /**
     * Constructor.
     */
    private DataRepositoryProducer() {
        factory = Persistence.createEntityManagerFactory("pu-update");
        log.info("Created EntityManagerFactory");
    }
    
    /**
     * Holder.
     */
    private static class DataRepositoryProducerHolder {
        private static final DataRepositoryProducer instance = new DataRepositoryProducer();
    }
    
    /**
     * @return {@link DataRepositoryProducer}
     */
    public static DataRepositoryProducer getInstance() {
        return DataRepositoryProducerHolder.instance;
    }

    /**
     * Factory.
     */
    private EntityManagerFactory factory;
    
    /**
     * Singleton {@link EntityManagerFactory} getter.
     * @return {@link EntityManagerFactory}
     */
    private EntityManagerFactory getEntityManagerFactory() {
        return factory;
    }

    /**
     * Get {@link EntityManager}.
     * @return {@link EntityManager}
     */
    private EntityManager getEntityManager() {
        return getEntityManagerFactory().createEntityManager();
    }

        // дальше паблик-методы, работающие с EntityManager...
}

Дальше этот синглтон использую хочу использовать так:
Код

@Entity
public abstract class Unit implements Cloneable {
    private static final LoggerFilter log = LoggerFilter.getLoggerFilter(Unit.class);
    private static final DataRepositoryProducer drp = DataRepositoryProducer.getInstance();
        // Ну и все остальное...
}

Может я чего-то не понимаю, но в юнит-тестах получаю NullPointerException smile 
drp почему-то null. Никаких static { } конструкций не юзаю. Логгер (не синглтон, моя обертка к log4j для отложенной обработки строк) работает нормально, хотя объявляется и инициализируется там-же и так-же.
log.info("Created EntityManagerFactory"); - отрабатывает.
Как так?

Автор: Kircul 9.2.2011, 13:11
LLIbIcpEP, стектрэйс в студию.

Автор: LLIbIcpEP 9.2.2011, 13:19
Код

java.lang.NullPointerException
    at my.model.Unit.save(Unit.java:165)
    at my.admin.model.Config.<init>(Config.java:55)
    at my.admin.bean.DataFactory.createConfigs(DataFactory.java:63)
    at my.admin.bean.DataFactory.createData(DataFactory.java:43)
    at my.test.GatewayTest.testInitEntityManagerFactory(GatewayTest.java:78)

Config унаследован от Unit. Метод save() в Unit:
Код

    /**
     * Save this unit to DB.
     * @return {@link Unit}
     */
    @SuppressWarnings("unchecked")
    public <T extends Unit> T save() {
        return (T) drp.saveUnit(this);
    }

Вылетаем при попытке drp.saveUnit(this), drp - null.

Автор: LSD 9.2.2011, 14:02
Код

@Entity
public abstract class Unit implements Cloneable {
    private static final LoggerFilter log = LoggerFilter.getLoggerFilter(Unit.class);
    private static final DataRepositoryProducer drp = DataRepositoryProducer.getInstance();
        // Ну и все остальное...
}

Есть подозрение что LoggerFilter.getLoggerFilter(Unit.class) вызывает метод save(), а значит он будет вызван до того как будет проинициализирован drp.

Автор: LLIbIcpEP 9.2.2011, 14:09
Да нет, это разные компоненты, функционально не связанные. LoggerFilter - простая обертка для log4j, в которую передаю String format и аргументы, а конкатенация строк и toString() аргументов происходит только если соответствует уровень логов в конфиге. Я в курсе, что slf4j умеет такое из коробки, http://forum.vingrad.ru/forum/topic-321828/kw-maven-weld--servlet-slf.html

Код

public class LoggerFilter {
    private Logger log;
    
    @SuppressWarnings("unchecked")
    private LoggerFilter(Class clazz) {
        log = Logger.getLogger(clazz);
    }
    
    @SuppressWarnings("unchecked")
    public static LoggerFilter getLoggerFilter(Class clazz) {
        return new LoggerFilter(clazz);
    }
        // и т.д...
    
    public boolean isTraceEnabled() {
        return log.isEnabledFor(Level.TRACE);
    }
...
    public void trace(String format, Object... args) {
        if (isTraceEnabled()) {
            log.trace(String.format(format, args));
        }
    }
    
    public void trace(String format, Throwable t, Object... args) {
        if (isTraceEnabled()) {
            log.trace(String.format(format, args), t);
        }
    }
    
    public void trace(Throwable t) {
        if (isTraceEnabled()) {
            log.trace(t);
        }
    }
...
}

Метод save() вызывается в не пустых конструкторах сущностей, если это важно.

Автор: LLIbIcpEP 9.2.2011, 15:00
Насколько правильно будет определять drp только в тех методах, где он юзается? Это ведь синглтон, по производительности ударить не должно?

Автор: LSD 9.2.2011, 17:10
Честно говоря, я вообще не понимаю зачем тут такие сложности. Зачем внутри DataRepositoryProducer объявлять класс DataRepositoryProducerHolder все назначение которого держать ссылку на внешний класс? Тут явно не в том порядке инициализируются поля, отсюда и null.

Добавлено через 2 минуты и 56 секунд
Если все таки решишь разобраться, что там и как - добавь в каждый класс:
Код

    static {
        System.out.println("Init class " + <put_class_name_here>.class);
    }

Автор: LLIbIcpEP 9.2.2011, 18:19
Это просто такая реализация синглтона.
user posted image
Смена реализации на классический вариант не помогает.
user posted image

У этой реализации недостаток - нельзя обработать исключения в конструкторе синглтона. Но мне это не надо. Зато есть выгода - нет лишних ифов и synchronized-блоков при поддержке многопоточности и отложенной инициализации, плюс к перфомансу.

Добавлено через 9 минут и 26 секунд
PS:
Код

    private static final DataRepositoryProducer drp = DataRepositoryProducer.getInstance();
    static {
        System.out.println("Init class " + Unit.class + " " + drp);
    }

Код

Init class class my.model.Unit null

Автор: LLIbIcpEP 9.2.2011, 18:38
Это вообще смешно)
Код

    private static final DataRepositoryProducer drp;
    static {
        drp = DataRepositoryProducer.getInstance();
        System.out.println("Init class " + Unit.class + " " + drp);
    }

Код

Init class class my.model.Unit null


Добавлено через 7 минут и 30 секунд
Ага, все стало на свои места. Сделал в синглтоне:
Код

    /**
     * Holder.
     */
    private static class DataRepositoryProducerHolder {
        private static final DataRepositoryProducer instance;
        static {
            instance = new DataRepositoryProducer();
            System.out.println("Init class " + DataRepositoryProducer.class + " " + instance);
        }
    }

И оставил первое. Выводится следующее:
Код

Init class class my.model.Unit null
Init class class my.admin.bean.DataRepositoryProducer it.treed.quasar.dreamplanet.admin.bean.DataRepositoryProducer@e5ace9

Юнит инициализируется до статик поля в DataRepositoryProducerHolder. Как бороться? У меня уже чисто спортивный интерес)

Автор: _sten_ 9.2.2011, 23:30
а какая JVM?

Автор: techmax 10.2.2011, 04:11
Как вариант:
Код

@SuppressWarnings("unchecked")
    public <T extends Unit> T save() {
        return (T) DataRepositoryProducer.getInstance().saveUnit(this);

Автор: LLIbIcpEP 10.2.2011, 17:44
Цитата(_sten_ @ 9.2.2011,  23:30)
а какая JVM?

Код

Java: Sun Microsystems Inc. Java(TM) SE Runtime Environment 1.6.0_22
Java VM: Sun Microsystems Inc. Java HotSpot(TM) Server VM 17.1-b03


Добавлено через 1 минуту и 10 секунд
Цитата(techmax @ 10.2.2011,  04:11)
Как вариант:
Код

@SuppressWarnings("unchecked")
    public <T extends Unit> T save() {
        return (T) DataRepositoryProducer.getInstance().saveUnit(this);

Да, вариант, просто не хотелось бы делать громоздкое объявление DataRepositoryProducer в каждом методе, где он нужен...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)