写点什么

spring 注入 bean 的几种策略模式

用户头像
测试轩
关注
发布于: 2020 年 05 月 15 日
spring注入bean的几种策略模式

上篇文章Spring IOC的核心机制:实例化与注入我们提到在有多个实现类的情况下,spring 是如何选择特定的 bean 将其注入到代码片段中,我们讨论了按照名称注入和使用 @Qualifier 注解输入的两种方式,本篇文章将结合之前提到的和 spring 的其他注入方式一起进行讨论。


本文主题


我们将讨论在一个接口或者抽象类在具有多个实现类的情况下,有多少种策略能够让我们在特定的代码片段中注入想要的 bean。


按照名称


定义一个 Skill 借口,他描述了英雄具备哪些技能,现在有两个英雄的实现类 Diana 和 Irelia,通过 component 注解将其标注为 bean,在容器启动的时候会将他们加载到容器中。


现在有一个 BannerController 要使用 Diana 这个 bean


@RestControllerpublic 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 注解,


@RestControllerpublic 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,那可以把其他的注释掉


//@Componentpublic 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@Primarypublic 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 org.springframework.context.annotation.Condition;import org.springframework.context.annotation.ConditionContext;import org.springframework.core.type.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 = context.getEnvironment().getProperty("hero.condition");        System.out.println(name);        if ("diana".equalsIgnoreCase(name)){            return true;        }else if ("irelia".equalsIgnoreCase(name)){            return true;        }        return false;    }}
复制代码


@Configurationpublic 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 的内置注解,如何来解决我们的问题。




博客地址:https://www.immortalp.com/


发布于: 2020 年 05 月 15 日阅读数: 109
用户头像

测试轩

关注

一名测试工程师,没了 2018.06.29 加入

还是一名测试工程师,没了

评论

发布
暂无评论
spring注入bean的几种策略模式