Самостоятельная инъекция с пружиной

Я пробовал следующий код с Spring 3.x, который не удалось с BeanNotFoundException и это должно в соответствии с ответами на вопрос, который я задавал ранее - Могу ли я вводить тот же класс с помощью Spring?

@Service
public class UserService implements Service{
    @Autowired
    private Service self;
}

Так как я пробовал это с Java 6, я обнаружил, что следующий код работает нормально:

@Service(value = "someService")
public class UserService implements Service{
    @Resource(name = "someService")
    private Service self;
}

но я не понимаю, как это разрешает циклическую зависимость.

РЕДАКТИРОВАТЬ:
Вот сообщение об ошибке. ФП упомянул об этом в комментарии к одному из ответов:

Вызвано: org.springframework.beans.factory.NoSuchBeanDefinitionException: не найден соответствующий компонент типа [com.spring.service.Service] для зависимости: ожидается, что по крайней мере 1 компонент, который квалифицируется как кандидат для автоматического подключения для этой зависимости. Аннотации зависимостей: {@org.springframework.beans.factory.annotation.Autowired(обязательно =true)}

6 ответов

Решение

Обновление: февраль 2016

Самостоятельная автопроводка будет официально поддерживаться в Spring Framework 4.3. Реализацию можно увидеть в этом коммите GitHub.


Окончательная причина того, что вы не можете самостоятельно подключиться, заключается в том, что реализация Spring DefaultListableBeanFactory.findAutowireCandidates(String, Class, DependencyDescriptor) Метод явно исключает возможность. Это видно в следующем фрагменте кода этого метода:

for (String candidateName : candidateNames) {
    if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, descriptor)) {
        result.put(candidateName, getBean(candidateName));
    }
}

К вашему сведению: имя бина (т. Е. Бина, который пытается автоматически подключиться) beanName, Этот бин на самом деле является кандидатом autowire, но приведенное выше условие if возвращает false (поскольку candidateName на самом деле равен beanName). Таким образом, вы просто не можете автоматически связывать бин с самим собой (по крайней мере, не начиная с Spring 3.1 M1).

Теперь о том, является ли это предполагаемым поведением семантически говоря, это другой вопрос.;)

Я спрошу Юргена и посмотрю, что он скажет.

С Уважением,

Сэм (Core Spring Committer)

ps Я открыл проблему Spring JIRA, чтобы рассмотреть возможность поддержки самостоятельной автопроводки по типу с использованием @Autowired. Не стесняйтесь смотреть или голосовать за эту проблему здесь: https://jira.springsource.org/browse/SPR-8450

Этот код тоже работает:

@Service
public class UserService implements Service {

    @Autowired
    private ApplicationContext applicationContext;

    private Service self;

    @PostConstruct
    private void init() {
        self = applicationContext.getBean(UserService.class);
    }
}

Я не знаю почему, но кажется, что Spring может получить боб от ApplicationContext если создан, но не инициализирован. @Autowired работает до инициализации и не может найти тот же компонент. Так, @Resource возможно работает после @Autowired и раньше @PostConstruct,

Но я не знаю, просто спекулировать. Во всяком случае, хороший вопрос.

Получить прокси AOP от самого объекта вопрос предлагает альтернативный хакерский подход с AopContext.currentProxy() это может быть подходящим для особых случаев.

Между прочим, более элегантное решение проблемы самовывоза - использовать AspectJ Load-Time Weaving для ваших транзакционных прокси (или любого другого прокси-сервера, представленного AOP).

Например, с управлением транзакциями на основе аннотаций вы можете использовать режим "аспекта" следующим образом:

<tx:annotation-driven mode="aspectj" />

Обратите внимание, что режимом по умолчанию является "прокси" (т. Е. Динамические прокси JDK).

С Уважением,

Сэм

Просто еще один подход:

@EnableAsync
@SpringBootApplication
public class Application {

    @Autowired
    private AccountStatusService accountStatusService;

    @PostConstruct
    private void init() {
        accountStatusService.setSelf(accountStatusService);
    }
}

@Service
public class AccountStatusService {
    private AccountStatusService self;

    public void setSelf(AccountStatusService self) {
        this.self = self;
    }
}

с этим ваш сервис будет в прокси. Я сделал это для работы с асинхронными методами внутри себя.

Я попытался @sinuhepop решение:

@PostConstruct
private void init() {
    self = applicationContext.getBean(UserService.class);
}

Он сделал инъекцию, но сервис не был в прокси, и мои методы не работали в новом потоке. С этим подходом все работает так, как мне бы хотелось.

Приведенный выше код я не вижу циклической зависимости. Вы внедрили какой-то экземпляр Сервиса в UserService. Реализация внедренного Сервиса не обязательно должна быть другим UserService, поэтому циклическая зависимость отсутствует.

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

Похоже, что Spring создает и настраивает объект, а затем помещает его в контекст поиска компонента. Но, в случае с Java, я думаю, что он создает объект и связывает его с именем и во время конфигурации, когда объект ищется по имени, которое он нашел в контексте.

Это мое решение для малых и средних проектов. Никакого AspectJ или магии контекста приложения, он работает с синглетонами и инъекцией конструктора, и его очень легко протестировать.

@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {

    private final PersonDao _personDao;

    @Autowired
    public PersonDao(PersonDao personDao) {
        _personDao = personDao;
    }
}
Другие вопросы по тегам