Самовсасывание с помощью Spring

Я попробовал следующий код с 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;
}

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

EDIT:
Вот сообщение об ошибке. ОП упомянул об этом в комментарии к одному из ответов:

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

Ответ 1

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

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


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

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

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

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

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

Привет,

Сэм (Core Spring Committer)

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

Ответ 2

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

@Service
public class UserService implements Service {

    @Autowired
    private ApplicationContext applicationContext;

    private Service self;

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Привет,

Сэм

Ответ 6

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

Ответ 7

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

@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);
}

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

Ответ 8

Почему бы не использовать 'this' вместо того, чтобы вводить один и тот же объект в себя?