web-dev-qa-db-fra.com

Comprendre l'utilisation de Spring @Autowired

Je lis la documentation de référence de spring 3.0.x pour comprendre l'annotation Spring Autowired:

.9.2 @Autowired et @Inject

Je ne suis pas capable de comprendre les exemples ci-dessous. Avons-nous besoin de faire quelque chose dans le XML pour que cela fonctionne?

EXEMPLE 1

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

Exemple 2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

Comment les deux classes peuvent-elles être connectées automatiquement en utilisant la même interface et en utilisant la même classe?

Exemple:

class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

Quelle méthode de conception sera appelée? Comment puis-je m'assurer que la méthode de conception de la classe Red sera appelée et non pas Blue?

287
NewQueries

TL; DR

L'annotation @Autowired vous évite de devoir effectuer le câblage vous-même dans le fichier XML (ou tout autre moyen) et trouve pour vous ce qui doit être injecté où et le fait pour vous.

Explication complète

L'annotation @Autowired vous permet de passer ailleurs aux configurations de ce qu'il faut injecter et le fait juste pour vous. En supposant que votre paquet soit com.mycompany.movies, vous devez mettre cette balise dans votre XML (fichier de contexte d'application):

<context:component-scan base-package="com.mycompany.movies" />

Cette balise fera un balayage automatique. En supposant que chaque classe devant devenir un bean est annotée avec une annotation correcte telle que @Component (pour un bean simple) ou @Controller (pour un contrôle de servlet) ou @Repository (pour DAO classes) et ces classes sont quelque part sous le paquet com.mycompany.movies, Spring les trouvera toutes et créera un bean pour chacune d'elles. Cela se fait en 2 scans des classes - la première fois, il recherche uniquement les classes devant devenir un haricot et mappe les injections nécessaires; il effectue ensuite l'injection des beans. Bien sûr, vous pouvez définir vos beans dans le fichier XML plus traditionnel ou avec une classe @ Configuration (ou toute combinaison des trois).

L'annotation @Autowired indique à Spring où une injection doit avoir lieu. Si vous le mettez sur une méthode setMovieFinder, il comprend (par le préfixe set + l'annotation @Autowired_) qu'un bean doit être injecté. Dans le second balayage, Spring recherche un bean de type MovieFinder et, s'il le trouve, il l'injecte dans cette méthode. S'il trouve deux de ces haricots, vous obtiendrez un Exception. Pour éviter la Exception, vous pouvez utiliser l'annotation @Qualifier et lui indiquer lequel des deux beans à injecter de la manière suivante:

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

Ou si vous préférez déclarer les beans dans votre XML, cela ressemblerait à ceci:

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

Dans la déclaration @Autowired, vous devez également ajouter le @Qualifier pour indiquer lequel des deux haricots de couleur à injecter:

@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

Si vous ne souhaitez pas utiliser deux annotations (le @Autowired et @Qualifier), vous pouvez utiliser @Resource pour combiner ces deux:

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

Le @Resource (vous pouvez lire quelques données supplémentaires à ce sujet dans le premier commentaire de cette réponse) vous évite d'utiliser deux annotations et vous n'en utilisez qu'une.

Je vais juste ajouter deux autres commentaires:

  1. Une bonne pratique consisterait à utiliser @Inject au lieu de @Autowired car il n’est pas spécifique à Spring et est fait partie du standard JSR-330 .
  2. Une autre bonne pratique consiste à placer le @Inject/@Autowired sur un constructeur au lieu d'une méthode. Si vous le placez sur un constructeur, vous pouvez valider que les beans injectés ne sont pas nuls et échouent rapidement lorsque vous essayez de démarrer l'application et vous évitez un NullPointerException lorsque vous devez réellement utiliser le bean.

Mise à jour : Pour compléter le tableau, j'ai créé une nouvelle question à propos de la classe @Configuration.

521
Avi

Rien dans l'exemple ne dit que les "classes implémentant la même interface". MovieCatalog est un type et CustomerPreferenceDao est un autre type. Le printemps peut facilement les distinguer.

Dans Spring 2.x, le câblage des haricots se passait principalement via des ID ou des noms de haricots. Cela est toujours pris en charge par Spring 3.x, mais vous aurez souvent une instance d’un bean avec un certain type - la plupart des services sont des singletons. Créer des noms pour ceux-là est fastidieux. Donc, Spring a commencé à prendre en charge "autowire par type".

Les exemples montrent différentes façons d’injecter des beans dans les champs, les méthodes et les constructeurs.

Le code XML contient déjà toutes les informations nécessaires à Spring, car vous devez spécifier le nom de classe qualifié complet dans chaque bean. Vous devez toutefois être un peu prudent avec les interfaces:

Ce câblage automatique échouera:

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

Puisque Java ne conserve pas les noms de paramètres dans le code d'octet, Spring ne peut plus distinguer les deux beans. Le correctif consiste à utiliser @Qualifier:

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }
18
Aaron Digulla

Oui, vous pouvez configurer le fichier xml de contexte de servlet Spring pour qu'il définisse vos beans (classes), afin qu'il puisse effectuer l'injection automatique à votre place. Cependant, notez que vous devez faire d’autres configurations pour que Spring soit opérationnel et que le meilleur moyen de le faire est de suivre un didacticiel.

Une fois que vous avez probablement configuré votre ressort, vous pouvez effectuer les opérations suivantes dans votre fichier xml de contexte de servlet de ressort pour l'exemple 1 ci-dessus (veuillez remplacer le nom du paquet de com.movies en indiquant le vrai nom du package et s’il s’agit d’une classe tierce, assurez-vous que le fichier JAR approprié se trouve sur le chemin de classe):

<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

ou si la classe MovieFinder a un constructeur avec une valeur primitive, alors vous pourriez quelque chose comme ça,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

ou si la classe MovieFinder a un constructeur qui attend une autre classe, vous pouvez faire quelque chose comme ça,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

... où ' otherBeanRef ' est un autre bean faisant référence à la classe attendue.

5
Cem Sultan