Quelques subtilités des collections d'injection au printemps

Bonjour à tous! Je m'appelle Vladislav Rodin. J'enseigne actuellement des cours sur l'architecture logicielle et l'architecture logicielle à haute charge sur le portail OTUS. Maintenant, dans OTUS, il y a un ensemble ouvert pour le nouveau fil du cours de développeur sur Spring Framework . En prévision du début du cours, j'ai décidé d'écrire un petit matériel de copyright que je veux partager avec vous.





Contexte


Le printemps contient beaucoup de «magie» en lui-même, réalisant lui-même certaines choses non évidentes. L'ignorance ou l'incompréhension de cela peut entraîner des effets secondaires que vous pouvez rencontrer lors de l'écriture de votre application à l'aide de ce cadre.

L'une de ces choses non évidentes est l'injection d'interfaces Java Collection Framework. Après être monté de façon indépendante sur un rake lié à ce sujet, et après avoir entendu les prochaines questions de mes collègues, j'ai décidé de le trier et d'enregistrer les résultats de mes recherches sous la forme d'un article dans l'espoir que cela aiderait quelqu'un déjà au travail ou pendant le développement initial de Spring.

Scénario


Examinons un service qui fonctionnera avec les héros de cinéma. Il y en aura 3: Rambo, Terminator et Gandalf. Chacun d'eux représentera une classe distincte, et leur parent sera commun - Hero.

public class Hero {
}

@Component
public class Rambo extends Hero {

    @Override
    public String toString() {
        return "Rambo";
    }
}

@Component
public class Terminator extends Hero {

    @Override
    public String toString() {
        return "Terminator";
    }
}


@Component
public class Gandalf extends Hero {
    @Override
    public String toString() {
        return "Gandalf";
    }
}

Liste d'injection


Supposons que nous voulons créer un service qui fonctionnera avec les héros des militants. Vous devrez probablement y injecter une liste de ces héros.

Aucun problème! Créez un service et une configuration:

@Service
@Getter
public class ActionHeroesService {
    @Autowired
    List<Hero> actionHeroes;
}

@Configuration
public class HeroesConfig {

    @Bean
    public List<Hero> action() {
        List<Hero> result = new ArrayList<>();
        result.add(new Terminator());
        result.add(new Rambo());
        return result;
    }
}

Tout va bien, mais lors de la vérification, il peut être constaté que Gandalf est sur la liste!

Spring, voyant qu'il était nécessaire d'injecter la List, a fait le tour des haricots situés dans le contexte, a trouvé parmi eux tous ceux qui correspondent au type générique, a collecté la List et l'a injectée, ignorant notre List.

Comment faire en sorte que Spring fasse ce que nous voulons?

Option 1. Béquille


Étant donné que le problème consiste précisément à injecter l'interface de Java Collection Framework, vous pouvez simplement remplacer List par ArrayList dans le service et, bien sûr, dans la configuration afin que Spring trouve une instance de la classe dont nous avons besoin. Ensuite, tout fonctionnera comme prévu.

@Configuration
public class HeroesConfig {

    @Bean
    public ArrayList<Hero> action() {
        ArrayList<Hero> result = new ArrayList<>();
        result.add(new Terminator());
        result.add(new Rambo());
        return result;
    }
}

@Service
@Getter
public class ActionHeroesService {
    @Autowired
    ArrayList<Hero> actionHeroes;
}

Option 2. Plus correcte


Une autre façon de lier les mains de Spring est de lui demander d'injecter dans le service non pas n'importe quelle liste, mais un bean avec un nom spécial, en ajoutant Qualifier. Ainsi, nous pourrons injecter notre bean.

@Service
@Getter
public class ActionHeroesService {
    @Autowired
    @Qualifier("action")
    List<Hero> actionHeroes;
}

Cartes d'injection


Si beaucoup de gens connaissent la nuance de l'injection de List, alors les choses sont généralement pires avec la carte.
Écrivons un service qui fonctionnera avec les personnages principaux des films. Une carte y sera injectée, contenant les noms des films par les touches, et par les valeurs des beans des personnages principaux:

@Service
@Getter
public class MainCharactersService {
    @Autowired
    Map<String, Hero> mainCharactersByFilmName;
}

@Configuration
public class HeroesConfig {

    @Bean
    public Map<String, Hero> mainCharactersByFilmName() {
        Map<String, Hero> result = new HashMap<>();

        result.put("rambo", new Rambo());
        result.put("terminator", new Terminator());
        result.put("LOTR", new Gandalf());

        return result;
    }
}

Au démarrage, vous pouvez voir que la clé de Gandalf n'est pas LOTR, mais gandalf, dont nous pouvons conclure que ce n'est pas le nom du film qui a été écrit, mais le nom du bean, alors que dans le cas de Rimbaud et du terminateur, il était juste chanceux: les noms des personnages principaux coïncident avec titres de films.

En fait, quand il est nécessaire d'injecter une Map, dont la clé est String, et la valeur des beans, Spring (comme dans le cas de la List) ignorera simplement la Map que nous avons proposée, parcourra le contexte et collectera tous les beans appropriés s, et créera une carte avec eux comme valeurs et avec leurs noms comme clés.

Les solutions de contournement sont similaires à celles qui ont fonctionné pour la liste:

Option 1. Béquille


Remplacez la carte par HashMap:

@Service
@Getter
public class MainCharactersService {
    @Autowired
    HashMap<String, Hero> mainCharactersByFilmName;
}


@Configuration
public class HeroesConfig {

    @Bean
    public HashMap<String, Hero> mainCharactersByFilmName() {
        HashMap<String, Hero> result = new HashMap<>();

        result.put("rambo", new Rambo());
        result.put("terminator", new Terminator());
        result.put("LOTR", new Gandalf());

        return result;
    }
}

Option 2. Plus correcte


Ajouter un qualificatif:

@Service
@Getter
public class MainCharactersService {
    @Autowired
    @Qualifier("mainCharactersByFilmName")
    Map<String, Hero> mainCharactersByFilmName;
}

Source: https://habr.com/ru/post/undefined/


All Articles