条件注解

@ComponentScan 包扫描机制

SpringBoot 中在一类上打上 @Component 一类的注解,就能将组件添加到容器中的原理就是依靠 @ComponentScan 包扫描机制实现的。在 SpringBoot 启动类中的 @SpringBootApplication 默认就有该注解,它默认扫描的包位置就是,启动类同级以及下面的路径。
image
如同这里是可以扫描到 BannerController, 但是如果将 BannerController 移动到启动类上级,那么启动是正常的,但是访问是报错的:
image
报错的原因就是不能扫描到上级路径下的组件,导致在容器中找不到 @RestController,但是如果在启动类上添加一个 @ComponentScan("com.ethan"),则访问正常:
image
image

原因分析:添加了这个注解后就会显示去扫描 "com.ethan" 包以及下面所有路径的组件。

策略模式的几种实现方案

  1. byname 切换bean name 保证同一接口的实现类只有一个
  2. @Qualifier("beanName")指定bean
  3. 有选择的只注入一个Bean,注释掉某个 Bean 上的@Component注解
  4. 使用@Primary注解

推荐使用第四种方式,当然如果平时就是自己开发的过程中,想使用哪种都行,但是如果是开发了一个复用的库的话,还是建议使用第四种方式,下面是四种方式的分别演示。

目前有三个英雄类,分别是 Hero1Hero2Hero3, 都实现了一个ISkill接口
ISkill

public interface ISkill {
    void r();
}

Hero1

@Component
public class Hero1 implements ISkill {

    private String name;
    private Integer age;

    public Hero1(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Hero1() {
        System.out.println("实例化Hero1对象");
    }

    @Override
    public void r() {
        System.out.println("Hero1......");
    }
}

Hero2

@Component
public class Hero2 implements ISkill {

    @Override
    public void r() {
        System.out.println("hero2......");
    }
}

Hero3

@Component
public class Hero3 implements ISkill {

    @Override
    public void r() {
        System.out.println("hero3......");
    }
}

有选择的只注入一个Bean,注释掉某个 Bean 上的@Component注解

Controller
通过BannerController进行调用的时候,如果我们需要调用 Hero1,那么我们可以先将Hero2和Hero3类上的@Component注释掉,然后在BannerController中,通过字段注入的时候,名称就定义为 private ISkill hero1;

@RestController
@RequestMapping("/v1/banner")
public class BannerController {

	@Autowired
    private ISkill hero1;
    private final ISkill hero1;

    @GetMapping("/test")
    public String test() {
        hero1.r();
        return "Hello China";
    }
}

@Qualifier("beanName")指定bean

如果Hero1、Hero2、Hero3类上的@Component注解都还存在的时候,在BannerController中注入的是,直接使用接口的名称,但是需要使用哪个英雄,就使用@Qualifier()强制指定。

@RestController
@RequestMapping("/v1/banner")
public class BannerController {

    @Autowired
    @Qualifier("hero2")
    private ISkill iSkill;

    @GetMapping("/test")
    public String test() {
        iSkill.r();
        return "Hello China";
    }
}

byname 切换bean name 保证同一接口的实现类只有一个

还有一种方式就是在BannerController中直接修改注入接口的名称,需要哪个英雄,就修改为哪个英雄

   @Autowired
    @Qualifier("hero2")
    private ISkill hero1; // hero2 hero3
	// 这类修改为什么名称,那么下面调用方法就使用修改为的名称进行调用
	...
	 hero1.r();  // hero2 hero3

使用@Primary注解

还有一种就使用@Primary注解,最推荐的也是这种方式,例如现在我们需要再去扩展一个英雄Hero,除了有现在Hero1英雄的技能外,还要有自己的扩展功能,就可以如下实现:

@Component
@Primary
public class Hero3 extends Hero1 implements ISkill {

    @Override
    public void q() {
        System.out.println("Hero3 英雄新增的技能......");
    }
}

这样前面代码中都不需要修改,调用的就是Hero3

Conditional条件注解

自定义条件注解需要使用 @Conditional + Condition 接口实现,如下:

public class Hero1Conditional implements Condition {

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String name = conditionContext.getEnvironment().getProperty("hero.conditional");
        return "gabriel".equalsIgnoreCase(name);
    }
}
@Configuration
public class HeroConfiguration {

    @Bean
    @Conditional(Hero1Conditional.class)
    public ISkill hero1() {
        return new Hero1("gabriel", 18);
    }

    @Bean
    @Conditional(Hero2Conditional.class)
    public ISkill hero2() {
        return new Hero2();
    }

    @Bean
    @Conditional(Hero3Conditional.class)
    public ISkill hero3() {
        return new Hero3();
    }
}
posted @ 2021-04-01 12:54  冷月无声の  阅读(119)  评论(0)    收藏  举报