Autowire Spring Bean в классе Singleton - PullRequest
0 голосов
/ 04 февраля 2020

Я пытаюсь автоматически соединить bean-компонент внутри класса Singleton, я знаю, что всегда лучше избегать ручного автоматического подключения, но этот класс используется во многих местах, поэтому я не хочу менять вызывающего его класс.

Бегунок. java

@Component
public class RunnerClass {
    @Autowired
    public ConfigService configService;
}

ConfigService. java

@Service
public class ConfigService {
    private ConfigServiceDAO = ConfigServiceDAO.getInstance();
}

ConfigServiceDAO. java

public class ConfigServiceDAO {

    //Bean I want to autowire here....
    @Autowired
    ConfigServiceDAOBuilder DAOBuilder

    public static ConfigServiceDAO getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        public static final ConfigServiceDAO INSTANCE = new ConfigServiceDAO();

        private SingletonHolder() {}
    }
}

DAOBuilder внутри ConfigServiceDAO всегда имеет значение null, что имеет смысл, поскольку, насколько я понимаю, когда экземпляр класса создается вручную, инжекция пружины не происходит. Какое решение может быть здесь, если я хочу сохранить ConfigServiceDAO в качестве не пружинного компонента?

==== EDIT ==== Я знаю, что можно сделать ConfigServiceDAO в качестве пружинного компонента и автоматически связать все зависимости. Но многие классы из разных пакетов уже вызывают ConfigServiceDAO.getInstance (). SomeMethod () Поэтому, я думаю, правильный вопрос заключается в том, как лучше всего связать компонент Spring с классом, который создается вручную.

Ответы [ 4 ]

1 голос
/ 04 февраля 2020

Spring обрабатывается @Autowired только в бобах, которыми он управляет сам по себе. Таким образом, у вас есть два варианта:

  1. Избавиться от синглтона - если вы используете пружину, она уже является синглтоном в контексте приложения. Это, безусловно, лучший подход в целом (если предположить, что другие части приложения, которые называют ваш синглтон, также управляются пружиной). Я не думаю, что вы должны бояться менять ConfigServiceDAO.getInstance.method() - инструменты рефакторинга в IDE сделают эту работу.

  2. Если вы не можете сделать 1, не используйте автоматическую аннотацию в синглтон - в любом случае он бесполезен, вместо этого, когда у вас настроен контекст приложения (например, в слушателе, который пружина генерирует при запуске приложения), получите доступ к компоненту ConfigServiceDAOBuilder, вызвав appCtx.getBean(ConfigServiceDAOBuilder.class) и "внедрив его" вручную поразмышляя, это то, что Spring делает с управляемыми bean-компонентами в любом случае:

@EventListener
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
  ConfigServiceDAOBuilder builder = 
    event.getApplicationContext().getBean(ConfigServiceDAOBuilder.class);

    ConfigServiceDao dao = ConfigServiceDAO.getInstance();
    dao.setDaoBuilder(builder); // or alternatively by reflection
}

В качестве дополнительного примечания рассмотрим использование метода setDaoBuilder в качестве частного пакета для защиты синглтона от некоторые случайно звонят сеттеру

1 голос
/ 04 февраля 2020

Я не знаю ваш вариант использования, но вы не можете использовать аннотацию @Autowired вне компонента Spring. Однако, если вам действительно нужен доступ к бину Spring из фрагмента кода, отличного от Spring, вы можете сделать это, как показано ниже. Однако это совсем не Spring способ проектирования ваших зависимостей.

import org.springframework.context.ApplicationContext;

public enum ApplicationContextHolder {
    INSTANCE;

    private ApplicationContext applicationContext;

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}

Тогда у вас есть класс конфигурации:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class SomeConfig {
    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void init() {
        ApplicationContextHolder.INSTANCE.setApplicationContext(applicationContext);
    }
}

Тогда в вашем классе DAO вы получите ссылку на компоновщик боб вы заинтересованы. Примерно так:

public class ConfigServiceDAO {
    public static ConfigServiceDAO getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        public static final ConfigServiceDAO INSTANCE = 
        ApplicationContextHolder.INSTANCE.getApplicationContext().getBean(ConfigServiceDAOBuilder.class).buildConfigServiceDAO()

        private SingletonHolder() {}
    }
}

Опять же, это совсем не Spring способ делать вещи.

1 голос
/ 04 февраля 2020

Насколько я понимаю, что вы хотите: Создать к весне ConfigServiceDAOBuilder. После этого введите его в неуправляемый объект класса ConfigServiceDAO. Вы можете сделать это после того, как будет создан экземпляр приложения Spring. Например, с CommanLineRunner:

@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
    @Autowired
    ConfigServiceDAOBuilder DAOBuilder

    @Override
    public void run(String...args) throws Exception {
        ConfigServiceDAO.getInstance().init(DAOBuilder);
    }
}

В ConfigServiceDAO должен быть метод init, который помогает зарегистрировать все необходимые бины.

0 голосов
/ 04 февраля 2020

Я запутался после прочтения ваших комментариев, поэтому позвольте мне выразить это так. То, что вы имеете в виду при ручном подключении, - это способ внедрения зависимостей Spring. Всякий раз, когда вы используете любую из аннотаций Spring Stereotype с экземпляром области действия по умолчанию, всегда используется Singleton.

Ваш класс ConfigService имеет проблему. Вы путаете вещи, вы должны создать отдельный класс конфигурации с @configuration и создать Bean для класса ConfigServiceDAO, что-то вроде ниже

@Configuration
Class Config{ 

@Bean
public ConfigServiceDAO configServiceDAO( ){
return ConfigServiceDAO.getInstance();
}
}

, затем автоматически подключить ConfigServiceDAO в классе ConfigService. С помощью этого Spring все зависимости будут исправлены в правильном порядке, и DAOBuilder не должен быть нулевым.

...