web-dev-qa-db-fra.com

Auto injection avec Spring

J'ai essayé le code suivant avec Spring 3.x qui a échoué avec BeanNotFoundException et il devrait correspondre aux réponses à une question que j'ai déjà posée - Puis-je injecter la même classe en utilisant Spring?

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

Comme j'essayais cela avec Java 6, j'ai trouvé que le code suivant fonctionnait bien:

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

mais je ne comprends pas comment cela résout la dépendance cyclique.

MODIFIER:
Voici le message d'erreur. Le PO l'a mentionné dans un commentaire sur l'une des réponses:

Causé par: org.springframework.beans.factory.NoSuchBeanDefinitionException: aucun bean correspondant du type [com.spring.service.Service] trouvé pour la dépendance: attendu au moins 1 bean qui est considéré comme candidat autowire pour cette dépendance. Annotations de dépendance: {@ org.springframework.beans.factory.annotation.Autowired (required = true)}

52
Premraj

Mise à jour: février 2016

Auto-câblage automatique sera officiellement pris en charge dans Spring Framework 4.3. La mise en œuvre peut être vue dans ce GitHub commit .


La raison définitive pour laquelle vous ne pouvez pas vous connecter automatiquement est que la mise en oeuvre de la méthode DefaultListableBeanFactory.findAutowireCandidates(String, Class, DependencyDescriptor) de Spring exclut explicitement cette possibilité. Cela est visible dans l'extrait de code suivant de cette méthode:

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

FYI: le nom du haricot (c’est-à-dire, le haricot qui essaie de se connecter automatiquement) est beanName. Ce haricot est en fait un candidat autowire, mais la condition if ci-dessus est fausse (puisque candidateName correspond en fait à beanName). Ainsi, vous ne pouvez tout simplement pas connecter automatiquement un haricot avec lui-même (du moins pas à partir de Spring 3.1 M1).

Maintenant, quant à savoir s’il s’agit ou non d’un comportement sémantique, c’est une autre question. ;)

Je vais demander à Juergen et voir ce qu'il a à dire.

Cordialement,

Sam (Core Spring Committer)

p.s. J'ai ouvert un problème Spring JIRA pour envisager la prise en charge de l'auto-câblage automatique par type à l'aide de @Autowired. N'hésitez pas à regarder ou à voter pour ce numéro ici: https://jira.springsource.org/browse/SPR-8450

40
Sam Brannen

Ce code fonctionne aussi:

@Service
public class UserService implements Service {

    @Autowired
    private ApplicationContext applicationContext;

    private Service self;

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

Je ne sais pas pourquoi, mais il semble que Spring puisse obtenir le haricot de ApplicationContext si est créé , mais pas initialisé . @Autowired fonctionne avant l'initialisation et ne peut pas trouver le même bean. Donc, @Resource fonctionne peut-être après @Autowired et avant @PostConstruct.

Mais je ne sais pas, je ne fais que spéculer. En tout cas, bonne question.

31
sinuhepop

En passant, la solution la plus élégante au problème de l'auto-invocation consiste à utiliser AspectJ Load-Time Weaving pour vos proxies transactionnels (ou tout autre proxy introduit par AOP).

Par exemple, avec la gestion des transactions par annotation, vous pouvez utiliser le mode "aspectj" comme suit:

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

Notez que le mode par défaut est "proxy" (c'est-à-dire, les proxy dynamiques JDK).

Cordialement,

Sam

1
Sam Brannen

Obtenir le proxy AOP de l'objet lui-même question suggère une autre approche de hacky avec AopContext.currentProxy() pouvant convenir à des cas particuliers.

1
Vadzim

Étant donné le code ci-dessus, je ne vois pas de dépendance cyclique . Vous injectez une instance de Service dans UserService . L'implémentation du service injecté ne doit pas nécessairement être un autre UserService, il n'y a donc pas de dépendance cyclique.

Je ne vois pas pourquoi vous injecteriez un UserService dans UserService, mais j'espère que c'est un essai théorique ou autre.

1
Stijn Geukens

Il semble que le printemps crée et configure un objet, puis le place dans le contexte de recherche des haricots. Mais, dans le cas de Java, je pense qu’il crée l’objet et le lie au nom et au cours de la configuration lorsque l’objet est recherché par le nom qu’il trouve dans le contexte. 

0
Krishna

Juste une autre approche:

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

avec cela votre service sera en proxy. Je l'ai fait pour travailler avec des méthodes asynchrones à l'intérieur de lui-même.

J'ai essayé la solution @sinuhepop:

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

Il a fait une injection mais le service n'était pas dans le proxy et mes méthodes ne fonctionnaient pas sur un nouveau thread. Avec cette approche, ça marche comme je le voudrais.

0
user3081007