Самовсасывание с помощью 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).
Привет,
Сэм
Ответ 5
Получить прокси AOP из самого объекта вопрос предполагает альтернативный хакерский подход с AopContext.currentProxy()
, который может быть подходящим для особых случаев.
Ответ 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' вместо того, чтобы вводить один и тот же объект в себя?