spring注⼊bean的⼏种策略模式
上篇⽂章我们提到在有多个实现类的情况下,spring是如何选择特定的bean将其注⼊到代码⽚段中,我们讨论了按照名称注⼊和使⽤
@Qualifier 注解输⼊的两种⽅式,本篇⽂章将结合之前提到的和spring的其他注⼊⽅式⼀起进⾏讨论。
本⽂主题
我们将讨论在⼀个接⼝或者抽象类在具有多个实现类的情况下,有多少种策略能够让我们在特定的代码⽚段中注⼊想要的bean。
按照名称
定义⼀个Skill借⼝,他描述了英雄具备哪些技能,现在有两个英雄的实现类Diana和Irelia,通过component注解将其标注为bean,在容器启动的时候会将他们加载到容器中。
现在有⼀个BannerController要使⽤Diana这个bean
@RestController
public class BannerController {
@Autowired
private Skill diana;
@RequestMapping(value = "/v2/banner", method = {RequestMethod.GET})
public String test() {
diana.r();
return "Hello SpringBoot";
}
}
private Skill diana;这⾥将成员变量的名字写成Diana这个bean的名字,容器就会选择Diana这个注⼊,这就叫做按照名称注⼊。spring会给每个bean设置默认到的名字也可以⾃定义,⼤家⾃⾏查资料了解即可。
@Qualifier 注解
还是上⽂的例⼦,如果成员变量名不写成bean的名称,是其他的名字,⽐如按照常规的写法会写成
private Skill skill;这个时候就可以使⽤@Qualifier 注解,
@RestController
public class BannerController {
@Autowired
@Qualifier("diana")
private Skill skill;
@RequestMapping(value = "/v2/banner", method = {RequestMethod.GET})
public String test() {
skill.r();
return "Hello SpringBoot";
}
有选择的注⼊⼀个bean,注释掉某个bean上的@Component
如果我们确定要使⽤哪个bean,那可以把其他的注释掉
//@Component
public class Irelia implements Skill {
public Irelia() {
System.out.println("Hello, Irelia");
}
public void q(){
System.out.println("Irelia Q");
}
public void w(){
System.out.println("Irelia W");
}
public void e(){
System.out.println("Irelia E");
}
public void r(){
System.out.println("Irelia R");
}
}
让Skill的实现类只有⼀个,⾃然就不要再操⼼注⼊哪个了,就只会注⼊存在于容器当中的惟⼀的那⼀个了。
使⽤@Primary提⾼bean的优先级
还可以使⽤@Primary注解提⾼我们想让注⼊bean的优先级,那spring在扫描到相同类型的bean的时候,就会看谁的优先级⾼,谁⾼谁注⼊@Component
@Primary
public class Diana implements Skill {
private String skillName = "Diana R";
public Diana() {
System.out.println("I am Diana");
}
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println(this.skillName);
}
}
然后启动启动程序,访问路由,就会看到下⾯的输出:
说明确实是注⼊了Diana这个bean。
阶段总结
上⾯的实现⽅式看起来都能实现按照所需注⼊特定的bean,但是有个问题,当发⽣变化的时候,这⾥的变化可能是英雄名变了,新增英雄或者要替换其他英雄,都需要去⼿动改代码,有些情况下可能还不只改⼀处,还是⽐较繁琐,并不⽅便。那有没有⼀种⽅式可以通过配置⽂件的形式来指定要注⼊的bean,当变化发⽣,只需要改配置,那岂不是很⽅便很灵活很开⼼。
条件注解
条件注解顾名思义,就是按照条件决定注⼊哪⼀个bean,所以要想使⽤条件注解,就得先写⼀个条件。
spring为我们提供了特定的⽅式来实现⾃⼰的条件:@Conditional注解+Condition接⼝,spring的这种⽅
式的原理是把符合条件的bean加加载到容器中,不合的不加载,那这样在注⼊的时候就不会有多个相同类型的bean存在了,⾃然也就注⼊成功了。
定义⼀个类实现Condition接⼝
import t.annotation.Condition;
import t.annotation.ConditionContext;
import ype.AnnotatedTypeMetadata;
public class HeroCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
实现Condition接⼝必须实现它的matches⽅法,我们先来解释⼀下这个⽅法的⼊参和返回值,
ConditionContext是⼀个接⼝,定义了他⽀持那些操作
public interface ConditionContext {
BeanDefinitionRegistry getRegistry();
ConfigurableListableBeanFactory getBeanFactory();
Environment getEnvironment();
ResourceLoader getResourceLoader();
ClassLoader getClassLoader();
}
其中的getEnvironment⽅法会返回Environment对象,他⾥⾯会记录当前应⽤所有的环境信息,可以通过这个对象获取到我们的配置信息。返回值是Boolean类型的,哪个bean的条件匹配成功,就会把这个bean注⼊到代码⽚段中去。我们来具体实现⼀下。
public class HeroCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String name = Environment().getProperty("dition");
System.out.println(name);
if ("diana".equalsIgnoreCase(name)){
return true;
}else if ("irelia".equalsIgnoreCase(name)){
return true;
}
return false;
}
}
@Configuration
public class HeroConfiguration {
@Bean
@Conditional(HeroCondition.class)
public Skill diana(){
return new Diana();
}
@Bean
@Conditional(HeroCondition.class)
public Skill irelia(){
return new Irelia();
}
}
通过以上的⽅式,就可以完成⼀个简单的条件注解,但是这种需求其实spring boot早已经帮我们实现了,提供了⼀些注解可以使⽤,下篇⽂章我们看看使⽤spring boot的内置注解,如何来解决我们的问题。spring framework扩展点
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论