68 votes

Auto-injection avec ressort

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

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

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

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

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

EDITAR:
Voici le message d'erreur. L'OP l'a mentionné dans un commentaire sur l'une des réponses :

Causé par : org.springframework.beans.factory.NoSuchBeanDefinitionException : No matching bean of type [com.spring.service.Service] found for dependency : expected at least 1 bean which qualifies as autowire candidate for this dependency. Annotations de dépendance : {@org.springframework.beans.factory.annotation.Autowired(required=true)}

3 votes

Question bonus : quel est le but de l'auto-injection ici ?

0 votes

@OrkunOzen Cas d'utilisation simple : vous voulez @Transactional pour fonctionner correctement sur les invocations d'une autre méthode de la même classe depuis l'intérieur. Appeler this.myMethod() ignorerait la transaction, mais self.myMethod() devrait avoir la transaction créée. Voir Section 5.1. Pièges potentiels - Transactions et procurations .

0 votes

@Snackoverflow, une autre question bonus : comment le this.myMethod() ignorer la transaction ?

61voto

Sam Brannen Points 1763

Mise à jour : Février 2016

Autocâblage sera officiellement pris en charge dans Spring Framework 4.3. L'implémentation peut être vue dans ce Engagement GitHub .


La raison définitive pour laquelle vous ne pouvez pas vous auto-câbler est que l'implémentation de la méthode Spring DefaultListableBeanFactory.findAutowireCandidates(String, Class, DependencyDescriptor) exclut explicitement cette possibilité. Ceci 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));
    }
}

Pour info : le nom du bean (c'est-à-dire le bean qui essaie de se connecter automatiquement) est le suivant beanName . Ce haricot est en fait un candidat à l'autoguidage, mais la condition if ci-dessus renvoie faux (puisque candidateName est en fait égale à la beanName ). Ainsi, il n'est tout simplement pas possible de câbler automatiquement un haricot avec lui-même (du moins pas à partir de Spring 3.1 M1).

Quant à savoir si ce comportement est voulu ou non sur le plan sémantique, c'est une autre question ;)

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

Regards,

Sam (Core Spring Committer)

p.s. J'ai ouvert une question JIRA sur Spring pour envisager le support de l'auto-câblage par type en utilisant @Autowired. N'hésitez pas à regarder ou à voter pour cette question ici : https://jira.springsource.org/browse/SPR-8450

1 votes

Cela explique pourquoi @Autowired ne fonctionne pas mais n'explique pas comment et pourquoi @Resource fonctionne. Quelqu'un ?

2 votes

@Amit - cela explique tout !! ils excluent seulement les candidats auto-connectés et ne vérifient pas les autres comme @Resource et etc.

4 votes

Je pense que quelque chose comme cela peut être très utile dans certaines occasions, par exemple lorsque vous avez besoin qu'une méthode soit enveloppée par un proxy transactionnel (en particulier avec requires_new) qui peut être auto-invoqué. Le fait d'avoir à "séparer" une telle fonctionnalité conduit souvent à des anti-modèles et à une mauvaise conception dans les applications génériques.

41voto

sinuhepop Points 7075

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 peut obtenir le haricot à partir de ApplicationContext si est créé mais pas initialisé . @Autowired fonctionne avant l'initialisation et il ne trouve pas le même haricot. Donc, @Resource peut fonctionner après @Autowired et avant @PostConstruct .

Mais je ne sais pas, je ne fais que spéculer. Quoi qu'il en soit, bonne question.

8 votes

Cool, injecter le contexte dans votre application bean. C'est la meilleure pratique qui soit !

6 votes

@avrilfanomar : Eh bien, si une classe doit être consciente qu'il existe un proxy d'elle-même, vous brisez l'abstraction DI de toute façon, donc je pense que l'accès à votre conteneur n'est pas beaucoup plus mauvais.

1 votes

Pourquoi est-ce que ça casse DI ? Je ne vois aucun mal à s'injecter soi-même, par exemple pour utiliser la méthode transactionnelle comme il se doit.

1voto

Sam Brannen Points 1763

Au fait, la solution la plus élégante au problème de l'auto-invocation est d'utiliser le tissage en temps de charge d'AspectJ pour vos mandataires transactionnels (ou tout autre mandataire introduit par la POA que vous utilisez).

Par exemple, avec la gestion des transactions basée sur les annotations, 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 proxies dynamiques du JDK).

Regards,

Sam

0 votes

Aspectj peut-il fonctionner avec des proxies JDK ? Je suppose qu'il a besoin de CGLib, non ?

0 votes

Eh bien, aspectj traite de l'auto-invocation mais crée d'autres problèmes. De plus, la dernière fois que j'ai vérifié, il n'était pas recommandé dans les documents de SpringSource.

1voto

Vadzim Points 4460

Obtenir un proxy AOP à partir de l'objet lui-même La question suggère une autre approche bricolée avec AopContext.currentProxy() qui peuvent convenir à des cas particuliers.

0voto

johna Points 422

Pourquoi ne pas utiliser 'this' au lieu d'injecter le même objet dans lui-même ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X