LINUX.ORG.RU

Еще один вопрос про инстанциацию бинов. Не заполняется автоматически поле, объявленное с @Autowired

 , , ,


0

1

Народ, обчитался мануалов, но просветления не приходит. Поэтому прошу помощи у коллективного разума.

Итак. applicationContext.xml

<context:component-scan base-package="ru.diowo.web"/>   
    <context:annotation-config/>
    <mvc:annotation-driven/>

    <import resource="spring-jooq.xml"/>
    <import resource="spring-thymeleaf.xml"/>

spring-jooq.xml

<context:property-placeholder location="classpath:application.properties" ignore-resource-not-found="false"/>
    
    <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
        <property name="driverClass" value="${db.driver}"/>
        <property name="jdbcUrl" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
    </bean>
    
    <bean id="lazyConnectionDataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean id="transactionAwareDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
        <constructor-arg ref="lazyConnectionDataSource" />
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="lazyConnectionDataSource"/>
    </bean>

    <tx:annotation-driven/>

    <bean class="org.jooq.impl.DataSourceConnectionProvider" name="connectionProvider">
        <constructor-arg ref="transactionAwareDataSource" />
    </bean>

    <bean id="dsl" class="org.jooq.impl.DefaultDSLContext">
        <constructor-arg ref="config" />
    </bean>

    
    <bean id="jooqToSpringExceptionTransformer" class="ru.diowo.web.config.jooq.JOOQToSpringExceptionTransformer"/>
    

    <bean class="org.jooq.impl.DefaultConfiguration" name="config">
        <constructor-arg index="0" ref="connectionProvider" />
        <constructor-arg index="1"><null /></constructor-arg>
        <constructor-arg index="2"><null /></constructor-arg>
        <constructor-arg index="3">
            <list>
                <bean class="org.jooq.impl.DefaultExecuteListenerProvider">
                    <constructor-arg index="0" ref="jooqToSpringExceptionTransformer"/>
                </bean>
            </list>
        </constructor-arg>
        <constructor-arg index="4"><null /></constructor-arg>
        <constructor-arg index="5"><value type="org.jooq.SQLDialect">${jooq.sql.dialect}</value></constructor-arg>
        <constructor-arg index="6"><null /></constructor-arg>
        <constructor-arg index="7"><null /></constructor-arg>
    </bean>

Класс DAO или Repository (хрен поймешь, как их правильно обзывать):

@Repository
@Transactional
public class UserSrv {

    Logger log = LoggerFactory.getLogger(UserSrv.class);
    
    
    @Autowired
    DSLContext dsl;

    public boolean checkLoginAvailable(String login) {
        if (dsl == null) {
            log.error("DSL is NULL!");
        }
        List<String> nicks = dsl
            .select()
            .from(Users.USERS)
            .where(Users.USERS.NICK.eq(login))
            .fetch()
            .getValues(Users.USERS.NICK);
        
        return nicks.isEmpty();
    }

Работаю этим DAO из контроллера так:

private boolean isLoginAvailable(String login) {
        UserSrv userSrv = new UserSrv();
        return userSrv.checkLoginAvailable(login);
    }

Вот после всего этого я получаю ошибку, что DSL is NULL!

Использовал официальные мануалы по jOOQ:

Подскажите, пожалуйста, что я упустил из виду? Где порылась моя собака?

★★★★★

Ты же конструктор вызываешь самостоятельно, кто по-твоему будет тебе заполнять поля? Ты думаешь, там какая-то магия работает? Нет никакой магии.

Или после вызова конструктора вызови метод Spring-а, который инициализирует твой объект, если тебе очень уж надо вызывать конструктор самому, или (правильный вариант) объяви в своём контролере @Autowired private UserSrv userSrv; и он у тебя будет заполнен при инициализации объекта контролера.

И настоятельно рекомендую прочитать мануал спринговский по DI. Там всё просто и никакой магии нет.

Legioner ★★★★★
()

почему @Autowired использовано на конструкторе. Как же тогда инстанциировать этот класс?

В смысле, как? Это autowiring через конструктор. Spring при создании бина сам тебе туда зависимость подтянет.

Ещё, инжектить в поля - моветон, лучше бы всегда через конструктор, если есть возможность. Тут man SOLID.

bytecode ★★
()
Последнее исправление: bytecode (всего исправлений: 1)
Ответ на: комментарий от bytecode

Инжектить конструктор — глупости. Гарантированные проблемы с закольцованными зависимостями, которые рано или поздно появятся.

Инжектить лучше всего сеттеры, но на первое время и текущее решение очень даже ничего.

anonymous
()

Спринг/EE работают так: есть контейнер (ApplicationContext), который разок проходит по конфигу и создает все объекты, а потом прописывает зависимости. Например, в конфиге видит @Autowired DSLContext dsl; ищет в созданных объектах этот самый DSLConfig и присваивает его нужной переменной.

Если ты просишь контекст: а отдай-ка мне объект UserSrv, то он тебе и возвращает созданный-проинициализированный UserSrv.

А если ты вручную командуешь UserSrv userSrv = new UserSrv(), то получаешь голый объект. Оператор new не знает, откуда брать переменную DSLContext dsl, она и будет null-ом.

Аннотация здесь совсем никак не влияет — это то же самое, что xml-ный конфиг, который где-то лежит и никого не трогает. Контейнер знает, что и где искать, и сможет сделать всю работу, а «базовая» Java об этом не в курсе, так что null и выходит.

anonymous
()
Ответ на: комментарий от bvn13

Нет. Он работает с полями или с методами-сеттерами.

Legioner ★★★★★
()
Ответ на: комментарий от anonymous

Говнокодер детекдед. Циклические зависимости это баги в архитектуре. Обязательные зависимости нужно инжектить через конструктор. Почему - отвечать надоело, ответ легко гуглится. И даже в документации спринга он описан.

f1xmAn ★★★★★
()
Ответ на: комментарий от f1xmAn

Единственное, что хорошо в конструкторе — иммутабельность для многонитности. А ведь в норме контекст разруливается один-единственный раз, после этого бины не создаются, так что иммутабельность рояля не играет.

Минусы будут в любом случае — длиннотные конструкторы (больше двух аргументов) и фиг с маслом вместо циклов. Такое счастье и даром не нужно.

anonymous
()
Ответ на: комментарий от anonymous

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

Более того, спринг и так гарантирует безопасную работу в многпоточном окружении, поэтому если ты получил объект из контекста - он уже полностью инициализирован и дополнительной синхронизации не требуется.

maloi ★★★★★
()
Ответ на: комментарий от maloi

А потом твой коллега взял класс, чтобы заюзать его в другом приложении, вызвал конструктор, после которого ожидается, что объект уже полностью готов к работе, и на вызове первого же метода получил NPE.

f1xmAn ★★★★★
()
Ответ на: комментарий от f1xmAn

С коллегой так же бьюсь в вопросе - инжектить в сеттеры с атрибутом @Required или же в конструкторы? Как по мне - лучше первый метод, т.к. конструкторы с 5-6 аргументами - это мрак и ужас. В спринговом контексте бины с непроинициализированными обязательными сетерами один черт не создадутся, а для использования в другом проекте без спринга - читайте документацию.

matroskin
()
Ответ на: комментарий от f1xmAn

после которого ожидается, что объект уже полностью готов к работе

кем ожидается?
Ты ещё скажи что @PostConstruct/init-method/InitializingBean нельзя использовать, т.к. дурачок из другого приложения не будет знать, что их нужно обязательно вызвать до первого использования объекта.

maloi ★★★★★
()
Последнее исправление: maloi (всего исправлений: 1)
Ответ на: комментарий от matroskin

Конструктор с 5 аргументами это признак того, что класс берет на себя слишком много ответственности. Это повод для рефакторинга, а не для инжекта через методы.

читайте документацию

Класс. Забьем на то, что код может сам себя документировать, лучше напишем документацию, которую нужно поддерживать, и которую никто не будет читать.

f1xmAn ★★★★★
()
Ответ на: комментарий от maloi

Не стоит. Вместо этого лучше заюзать статик фэктори метод, в котором вызовется приватный конструктор и пост констракт метод. Чем меньше неоднозначности в коде, тем лучше.

f1xmAn ★★★★★
()
Ответ на: комментарий от f1xmAn

Вместо этого лучше заюзать статик фэктори метод, в котором вызовется приватный конструктор и пост констракт метод.

Чем меньше неоднозначности в коде, тем лучше.

ты похоже просто не в курсе для чего нужны пост констракт методы.
а подход с горожением фабричных методов приводит к тому что все сторонятся джавы с её фабриками фабрик.

maloi ★★★★★
()
Ответ на: комментарий от f1xmAn

Конструктор с 5 аргументами это признак того, что класс берет на себя слишком много ответственности.

Отрефакторь, пожалуйста, org.springframework.jdbc.core.JdbcTemplate чтобы в нем не было 11 полей (а их всех нужно передавать в конструкторе).

Или скажи, как ты отрефакторишь такой конструктор?

public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait,
            int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis,
            int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle,
            long softMinEvictableIdleTimeMillis, boolean lifo)

maloi ★★★★★
()
Ответ на: комментарий от maloi

ты похоже просто не в курсе для чего нужны пост констракт методы.

Просвети меня.

f1xmAn ★★★★★
()
Ответ на: комментарий от maloi

Отрефакторь, пожалуйста, org.springframework.jdbc.core.JdbcTemplate чтобы в нем не было 11 полей (а их всех нужно передавать в конструкторе).

Не все из них являются обязательными. Опциональные параметры сетать через конструктор необязятально.

Или скажи, как ты отрефакторишь такой конструктор?

https://commons.apache.org/proper/commons-pool/api-2.3/org/apache/commons/poo...

f1xmAn ★★★★★
()
Ответ на: комментарий от f1xmAn

Опциональные параметры сетать через конструктор не обязательно

Алло, у тебя объект после конструктора должен быть готов к работе (твои слова), а ты еще должен в него необязательные параметры передать через сеттеры и потом инит-метод вызвать.

ссылка на cmp2

Отлично, спрячем 10 параметров в 1 объект, может быть никто не заметит, что ничего не изменилось.

maloi ★★★★★
()
Ответ на: комментарий от maloi

Они потому и опциональные, что даже если их не сетать, объект можно использовать сразу после вызова конструктора.

Отлично, спрячем 10 параметров в 1 объект, может быть никто не заметит, что ничего не изменилось.

Это называется группировка. Параметры группируются по конфигурационным классам до тех пор, пока код не перестанет быть отвратительным. К сервисам это не относится, если один сервис юзает слишком много других сервисов, его разбивают на сервисы поменьше. Часть конфигурационных параметров в результате этого процесса также может быть перенесена в другой сервис. Существует такое понятие как SOLID и им не стоит пренебрегать.

f1xmAn ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.