结构说明

springboot 设置context path springboot @condition_spring

hello-spring-boot-starter: 没什么用,引用了autoconfigure模块
hello-spring-boot-starter-autoconfigure: 主要的自定义的starter模块
hello-spring-boot-web-demo: web模块
详情可以看:入门SpringBoot自定义starter

1、@Conditional注解的说明

@Conditional是Spring4.0出现的新特性,他可以选择性的创建Bean。

判断是否满足当前条件,如果满足则创建Bean,不满足则不创建

2、思路

根据说明可以了解到,@Conditional的作用是判断是否满足当前条件,如果满足则创建Bean,不满足则不创建

我们的思路是看看web模块是否能够注入starter提供的Bean
1、如果条件满足则成功创建,通过接口能成功拿到返回值
2、如果条件满足则成功创建,通过接口不能拿到返回值

2、autoconfigure模块

根据上次的自定义starter直接创建一个Bean

3、@Conditional的使用

3.1、SpringBoot是如何实现这个功能的

这是DispatcherServlet的自动注册配置类:

@Conditional接口是说如果DispatcherServletRegistrationCondition满足条件才会去配置

springboot 设置context path springboot @condition_spring_02

看下它的继承关系图:

顶层也是一个Condition的接口,然后SpringBoot自己去封装了Condition的模块方法。

你不信可以去看看其他的,比如Redis的相关配置,也会发现都是继承SpringBootCondition。

springboot 设置context path springboot @condition_spring_03

所以我们可以得出结论:
1、实现Condition接口并重写matches方法
2、继承SpringBootCondition抽象类并重写getMatchOutcome方法

3.2、造轮子

跟着SpringBoot的思路,我们去实现第一种:实现Condition接口并重写matches方法。
如果返回值为true则@Conditional则认为符合条件

Condition的编写

public class WindowCondition implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // todo 拿配置文件的信息
        return true;
    }
}

Service的编写

public interface CmdService {

    String print(String cmd);
}
public class WindowCmdService implements CmdService {
    @Override
    public String print(String cmd) {
        return "window cmd ...";
    }
}

自动配置类

public class CmdServiceConditionAutoConfiguration {

    /**
     * 当WindowCondition方法中的matches返回true的时候,
     * WindowCmdService会被注入,否则不注入。
     */
    @Bean
    @Conditional(WindowCondition.class)
    public WindowCmdService windowComService(){
        return new WindowCmdService();
    }
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.mecuros.springboot.custom.starter.CmdServiceConditionAutoConfiguration

4、验证

在web模块引入starter并写出Controller接口

@RestController("/conditional")
public class ConditionalController {

    @Resource
    private WindowCmdService windowCmdService;

    @GetMapping("/window/{cmd}")
    public String window(@PathVariable String cmd){
        return windowCmdService.print(cmd);
    }
}

4.1、不用@Conditional的情况

不用@Conditional注解默认会创建WindowService
image.png

4.2、用@Conditional的情况

4.2.1、WindowConditional为true时

一切正常
image.png

4.2.2、WindowConditional为false时

image.png
image.png

5、总结

我们发现:
当Conditional为false,SpringBoot会报错。
当Conditional为true,接口正常。


证明:@Conditional注解需要配合两种方法使用
1、实现Condition接口并重写matches方法
2、继承SpringBootCondition抽象类并重写getMatchOutcome方法
并且在自动配置那里配合@Bean来一起使用。

6、其他知识点(待验证,后面会继续验证)

@Conditional扩展注解 作用(判断是否满足当前指定条件)
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean;
@ConditionalOnMissingBean 容器中不存在指定Bean;
@ConditionalOnExpression 满足SpEL表达式指定
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionalOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication 当前不是web环境
@ConditionalOnJndi JNDI存在指定项