Comment fonctionne le câblage automatique au printemps?

510

Je suis un peu confus quant au fonctionnement de l' inversion de control ( IoC) Spring.

Disons que j'ai une classe de service appelée UserServiceImplqui implémente l' UserServiceinterface.

Comment serait-ce @Autowired?

Et dans mon Controllers, comment pourrais - je instantiateun instancede ce service?

Vais-je simplement faire ce qui suit?

UserService userService = new UserServiceImpl();
Blankman
la source

Réponses:

703

Premièrement, et le plus important - tous les beans Spring sont gérés - ils "vivent" à l'intérieur d'un conteneur, appelé "contexte d'application".

Deuxièmement, chaque application a un point d'entrée vers ce contexte. Les applications Web ont un Servlet, JSF utilise un el-resolver, etc. De plus, il y a un endroit où le contexte de l'application est démarré et tous les beans - câblés automatiquement. Dans les applications Web, cela peut être un écouteur de démarrage.

Le câblage automatique se produit en plaçant une instance d'un bean dans le champ souhaité dans une instance d'un autre bean. Les deux classes doivent être des beans, c'est-à-dire qu'elles doivent être définies pour vivre dans le contexte de l'application.

Qu'est-ce que «vivre» dans le contexte de l'application? Cela signifie que le contexte instancie les objets, pas vous. C'est-à-dire - vous ne faites jamais new UserServiceImpl()- le conteneur trouve chaque point d'injection et y définit une instance.

Dans vos contrôleurs, vous avez juste les éléments suivants:

@Controller // Defines that this class is a spring bean
@RequestMapping("/users")
public class SomeController {

    // Tells the application context to inject an instance of UserService here
    @Autowired
    private UserService userService;

    @RequestMapping("/login")
    public void login(@RequestParam("username") String username,
           @RequestParam("password") String password) {

        // The UserServiceImpl is already injected and you can use it
        userService.login(username, password);

    }
}

Quelques notes:

  • Dans votre applicationContext.xmlvous devez activer la <context:component-scan>sorte que les classes sont scannés pour le @Controller, @Serviceetc. annotations.
  • Le point d'entrée pour une application Spring-MVC est le DispatcherServlet, mais il vous est caché, et donc l'interaction directe et l'amorçage du contexte d'application se produisent derrière la scène.
  • UserServiceImpldoit également être défini comme bean - en utilisant <bean id=".." class="..">ou en utilisant l' @Serviceannotation. Puisqu'il sera le seul implémenteur de UserService, il sera injecté.
  • Outre l' @Autowiredannotation, Spring peut utiliser le câblage automatique configurable en XML. Dans ce cas, tous les champs dont le nom ou le type correspond à un bean existant obtiennent automatiquement un bean injecté. En fait, c'était l'idée initiale du câblage automatique - avoir des champs injectés avec des dépendances sans aucune configuration. D'autres annotations comme @Inject, @Resourcepeuvent également être utilisées.
Bozho
la source
7
oui, UserServiceImpl est annoté avec Service, et UserService est l'interface
Bozho
17
la portée par défaut est singleton, vous n'aurez donc qu'une seule instance du bean, qui est injectée à plusieurs endroits. Si vous définissez explicitement la portée comme "prototype", alors plusieurs instances existeront, peut-être paresseuses (selon la configuration)
Bozho
2
Merci beaucoup pour votre message, cela a vraiment éclairci les choses pour moi. Concernant 'Puisqu'il sera le seul implémenteur ou UserService, il sera injecté.' - que se passe-t-il s'il y a plusieurs classes qui implémentent Userservice? Comment Spring sait-il quelle implémentation il doit utiliser?
Shishigami
7
s'il y en a un désigné comme "primaire", il l'utilise. Sinon, cela lève une exception
Bozho
3
non, userService n'est créé qu'une seule fois, il est de portée singleton
Bozho
64

Dépend si vous voulez la route des annotations ou la route de définition XML du bean.

Supposons que vous ayez défini les beans dans votre applicationContext.xml:

<beans ...>

    <bean id="userService" class="com.foo.UserServiceImpl"/>

    <bean id="fooController" class="com.foo.FooController"/>

</beans>

Le câblage automatique se produit au démarrage de l'application. Donc, dans fooController, qui, pour des raisons d'argument, veut utiliser la UserServiceImplclasse, vous l'annoteriez comme suit:

public class FooController {

    // You could also annotate the setUserService method instead of this
    @Autowired
    private UserService userService;

    // rest of class goes here
}

Lorsqu'il le verra @Autowired, Spring recherchera une classe qui correspond à la propriété dans le applicationContext, et l'injectera automatiquement. Si vous avez plus d'un UserServicebean, alors vous devrez qualifier celui qu'il doit utiliser.

Si vous procédez comme suit:

UserService service = new UserServiceImpl();

Il ne ramassera pas le @Autowiredsauf si vous le définissez vous-même.

Ben J
la source
2
Quelle est donc l'utilité de définir bean iddans applicationContext.xml. Nous devrons définir la userServicevariable avec UserServicetype. Alors pourquoi faire une entrée dans le xmlfichier.
viper
20

@Autowired est une annotation introduite dans Spring 2.5 et utilisée uniquement pour l'injection.

Par exemple:

class A {

    private int id;

    // With setter and getter method
}

class B {

    private String name;

    @Autowired // Here we are injecting instance of Class A into class B so that you can use 'a' for accessing A's instance variables and methods.
    A a;

    // With setter and getter method

    public void showDetail() {
        System.out.println("Value of id form A class" + a.getId(););
    }
}
mohit bansal
la source
10
Cela ne se compile pas et est généralement incorrect. @Autowiredne signifie pas que "vous pouvez utiliser toutes les fonctions (méthodes) et variables en Bclasse depuis la classe A". Ce qu'il fait, c'est apporter une instance de Adans des instances de B, donc vous pouvez le faire à a.getId()partir de B.
Dmitry Minkovsky
@dimadima Donc s'il fait System.out.println ("Valeur de la classe id forme A" + a.getId ()) ;, et non pas comme il l'a fait, ce sera plus correct. Veuillez répondre, car celui-ci est intuitivement clair pour moi et selon mon niveau de compréhension actuel, il explique le câblage automatique.
John Doe
annotation autowired est introduit au printemps 2,5 docs.spring.io/spring-framework/docs/2.5.x/api/org/...
SpringLearner
1
Pour une meilleure compréhension, car je suis nouveau dans ce domaine, @autowired instanciera-t-il la classe A en utilisant le constructeur par défaut? Sinon, comment les valeurs sont instanciées dans un bean ou un service si nous utilisons le câblage automatique. Je suppose que s'il appelle constructeur par défaut, pourquoi utiliser le câblage automatique en premier lieu, faites simplement A a = new A (). Précisez s'il vous plaît?
Sameer
@Sameer Grâce aux dépendances de câblage automatique, vous pouvez enregistrer beaucoup de code passe-partout dans vos tests unitaires et également les classes Controller, Service et Dao, car l'instanciation des champs l'accompagne automatiquement. Pas besoin d'appeler le constructeur.
kiltek
10

Comment @Autowiredfonctionne en interne?

Exemple:

class EnglishGreeting {
   private Greeting greeting;
   //setter and getter
}

class Greeting {
   private String message;
   //setter and getter
}

fichier .xml, il se ressemblera s'il n'utilise pas @Autowired:

<bean id="englishGreeting" class="com.bean.EnglishGreeting">
   <property name="greeting" ref="greeting"/>
</bean>

<bean id="greeting" class="com.bean.Greeting">
   <property name="message" value="Hello World"/>
</bean>

Si vous utilisez @Autowiredalors:

class EnglishGreeting {
   @Autowired //so automatically based on the name it will identify the bean and inject.
   private Greeting greeting;
   //setter and getter
}

fichier .xml, il se ressemblera s'il n'utilise pas @Autowired:

<bean id="englishGreeting" class="com.bean.EnglishGreeting"></bean>

<bean id="greeting" class="com.bean.Greeting">
   <property name="message" value="Hello World"/>
</bean>

Si vous avez encore des doutes, passez en dessous de la démo en direct

Comment fonctionne @Autowired en interne?

jeet singh parmar
la source
6

Il vous suffit d'annoter votre classe de service UserServiceImplavec une annotation:

@Service("userService")

Le conteneur à ressort prendra en charge le cycle de vie de cette classe lors de son enregistrement en tant que service.

Ensuite, dans votre contrôleur, vous pouvez le câbler automatiquement (instancier) et utiliser ses fonctionnalités:

@Autowired
UserService userService;
Jitender Chahar
la source
3

L'injection de dépendance Spring vous aide à supprimer le couplage de vos classes. Au lieu de créer un objet comme celui-ci:

UserService userService = new UserServiceImpl();

Vous l'utiliserez après avoir introduit DI:

@Autowired
private UserService userService;

Pour cela, vous devez créer un bean de votre service dans votre ServiceConfigurationfichier. Après cela, vous devez importer cette ServiceConfigurationclasse dans votre WebApplicationConfigurationclasse afin de pouvoir câbler automatiquement ce bean dans votre Controller comme ceci:

public class AccController {

    @Autowired
    private UserService userService;
} 

Vous pouvez trouver un exemple de POC basé sur la configuration java .

AbdusSalam
la source
1

Manière standard:

@RestController
public class Main {
    UserService userService;

    public Main(){
        userService = new UserServiceImpl();
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

Interface de service utilisateur:

public interface UserService {
    String print(String text);
}

Classe UserServiceImpl:

public class UserServiceImpl implements UserService {
    @Override
    public String print(String text) {
        return text + " UserServiceImpl";
    }
}

Production: Example test UserServiceImpl

C'est un excellent exemple de classes couplées serrées, un mauvais exemple de conception et il y aura des problèmes avec les tests (PowerMockito est également mauvais).

Jetons maintenant un coup d'œil à l'injection de dépendance SpringBoot, un bel exemple de couplage lâche:

L'interface reste la même,

Classe principale:

@RestController
public class Main {
    UserService userService;

    @Autowired
    public Main(UserService userService){
        this.userService = userService;
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

Classe ServiceUserImpl:

@Component
public class UserServiceImpl implements UserService {
    @Override
    public String print(String text) {
        return text + " UserServiceImpl";
    }
}

Production: Example test UserServiceImpl

et maintenant il est facile d'écrire le test:

@RunWith(MockitoJUnitRunner.class)
public class MainTest {
    @Mock
    UserService userService;

    @Test
    public void indexTest() {
        when(userService.print("Example test")).thenReturn("Example test UserServiceImpl");

        String result = new Main(userService).index();

        assertEquals(result, "Example test UserServiceImpl");
    }
}

J'ai montré une @Autowiredannotation sur le constructeur mais elle peut également être utilisée sur le setter ou sur le terrain.

Michu93
la source
0

Le concept entier d'inversion de contrôle signifie que vous êtes libre d'une corvée pour instancier des objets manuellement et fournir toutes les dépendances nécessaires. Lorsque vous annotez une classe avec une annotation appropriée (par exemple @Service), Spring instanciera automatiquement l'objet pour vous. Si vous n'êtes pas familier avec les annotations, vous pouvez également utiliser un fichier XML à la place. Cependant, ce n'est pas une mauvaise idée d'instancier les classes manuellement (avec le newmot - clé) dans les tests unitaires lorsque vous ne voulez pas charger le contexte Spring entier.

k13i
la source
0

Gardez à l'esprit que vous devez activer l' @Autowiredannotation en ajoutant un élément <context:annotation-config/>dans le fichier de configuration Spring. Cela enregistrera le AutowiredAnnotationBeanPostProcessorqui prend en charge le traitement des annotations.

Et puis, vous pouvez câbler automatiquement votre service en utilisant la méthode d'injection sur le terrain.

public class YourController{

 @Autowired
 private UserService userService; 

}

J'ai trouvé cela dans l' annotation Spring @autowired

David Pham
la source
0

Il existe 3 façons de créer une instance à l'aide de @Autowired.

1. @Autowiredsur les propriétés

L'annotation peut être utilisée directement sur les propriétés, éliminant ainsi le besoin de getters et setters:

    @Component("userService")
    public class UserService {

        public String getName() {
            return "service name";
        }
    }

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

Dans l'exemple ci-dessus, Spring recherche et injecte userServicequand UserControllerest créé.

2. @Autowiredsur les Setters

L' @Autowiredannotation peut être utilisée sur les méthodes de définition. Dans l'exemple ci-dessous, lorsque l'annotation est utilisée sur la méthode setter, la méthode setter est appelée avec l'instance de userServicewhen UserControllerest créée:

public class UserController {

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
            this.userService = userService;
    }
}

3. @Autowiredsur les constructeurs

L' @Autowiredannotation peut également être utilisée sur les constructeurs. Dans l'exemple ci-dessous, lorsque l'annotation est utilisée sur un constructeur, une instance de userServiceest injectée en tant qu'argument au constructeur lors de sa UserControllercréation:

public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService= userService;
    }
}
Mak
la source
0

En termes simples Autowiring, le câblage des liens automatiquement, vient maintenant la question de savoir qui fait cela et quel type de câblage. La réponse est: le conteneur fait cela et le type de câblage secondaire est pris en charge, les primitives doivent être effectuées manuellement.

Question: Comment le conteneur sait-il quel type de câblage?

Réponse: Nous le définissons comme byType, byName, constructeur.

Question: Existe-t-il un moyen de ne pas définir le type de câblage automatique?

Réponse: Oui, il est là en faisant une annotation, @Autowired.

Question: Mais comment le système sait-il que je dois choisir ce type de données secondaires?

Réponse: Vous fournirez ces données dans votre fichier spring.xml ou en utilisant des annotations de stéréotypes à votre classe afin que le conteneur puisse lui-même créer les objets pour vous.

Pratik Gaurav
la source