大家好!我叫弗拉迪斯拉夫·罗丹(Vladislav Rodin)。我目前正在OTUS门户上教授有关软件体系结构和高负载软件体系结构的课程。现在在OTUS中,为Spring框架上的Developer课程的新线程提供了一个开放集。考虑到课程的开始,我决定写一些版权资料,我想与您分享。
背景
Spring本身包含很多“魔术”,它们自己执行一些不明显的事情。对此的无知或误解可能会导致您在使用此框架编写应用程序的过程中可能遇到的副作用。这些显而易见的事情之一是Java Collection Framework接口的注入。在独立涉足与该主题相关的耙子之后,并听取了同事的下一个问题,我决定将其整理并以文章的形式记录我的研究结果,以期希望对已经在工作中或在Spring的初期开发中有所帮助的人提供帮助。情境
让我们看一下将与电影英雄一起使用的服务。其中将有3个:Rambo,Terminator和Gandalf。他们每个人都代表一个单独的班级,他们的父母将是共同的-英雄。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";
}
}
注射清单
假设我们要创建一个与武装分子的英雄一起使用的服务。可能您必须在其中注入此类英雄列表。没问题!创建服务和配置:@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;
}
}
一切都很好,但是检查时可以发现Gandalf在列表中!春天,看到有必要注入List,绕过了位于上下文中的bean,在其中找到了所有适合该泛型类型的bean,从其中收集了List并注入了它们,而忽略了我们的List。如何让Spring做到我们想要的?选项1。拐杖
由于问题恰恰在于试图注入Java Collection Framework的接口,因此您可以简单地在服务中(当然,在配置中)将List替换为ArrayList,以便Spring找到我们所需的类的实例。然后一切都会按照我们的预期进行。@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;
}
选项2。更正确
束缚Spring双手的另一种方法是要求他不仅将任何List注入到服务中,而且还添加具有Qualifier的具有特殊名称的bean。因此,我们将能够注入我们的bean。@Service
@Getter
public class ActionHeroesService {
@Autowired
@Qualifier("action")
List<Hero> actionHeroes;
}
注射图
如果许多人知道注入List的细微差别,那么使用Map通常情况会更糟。让我们编写一个可以处理电影主要角色的服务。地图将被注入其中,其中包含按键的电影名称以及主要角色的bean的值:@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;
}
}
在启动时,您会看到Gandalf的密钥不是LOTR,而是gandalf,从中我们可以得出结论,这不是所写电影的名字,而是豆子的名字,而对于Rimbaud和终结者来说,这只是幸运的:主要人物的名字与电影标题。实际上,当有必要注入一个Map(其键为String)并且bean的值是必需的时,Spring(如List的情况)将简单地忽略我们建议的Map,遍历上下文并收集所有合适的bean s,然后将其作为值并以其名称作为键创建一个Map。解决方法与为列表使用的解决方法类似:选项1。拐杖
用HashMap替换Map:@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;
}
}
选项2。更正确
添加限定词:@Service
@Getter
public class MainCharactersService {
@Autowired
@Qualifier("mainCharactersByFilmName")
Map<String, Hero> mainCharactersByFilmName;
}