Spring Boot中的那些条件判断的实现方法

网友投稿 341 2023-01-08


Spring Boot中的那些条件判断的实现方法

Spring Boot中的那些Conditional

spring boot中为我们提供了丰富的Conditional来让我们得以非常方便的在项目中向容器中添加Bean。本文主要是对各个注解进行解释并辅以代码说明其用途。

所有ConditionalOnXXX的注解都可以放置在class或是method上,如果方式在class上,则会决定该class中所有的@Bean注解方法是否执行。

@Conditional

下面其他的Conditional注解均是语法糖,可以通过下面的方法自定义ConditionalOnXXX

Conditional注解定义如下,接收实现Condition接口的class数组。

public @interface Conditional {

Class<? extends Condition>[] value();

}

而Condition接口只有一个matchs方法,返回是否匹配的结果。

public interface Condition {

boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

通过操作系统进行条件判断,从而进行Bean配置。当Window时,实例化Bill的Person对象,当linux时,实例化Linus的Person对象。

//LinuxCondition,为方便起见,去掉判断代码,直接返回true了

public class LinuxCondition implements Condition {

@Override

public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

return true;

}

}

//WindowsCondition,为方便起见,去掉判断代码,直接返回false了

public class WindowsCondition implements Condition {

@Override

public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {

return false;

}

}

@Data

@ToString

@AllArgsConstructor

@NoArgsConstructor

public class Person {

private String name;

private Integer age;

}

//配置类

@Configuration

public class BeanConfig {

@Bean(name = "bill")

@Conditional({WindowsCondition.class})

public Person person1(){

return new Person("Bill Gates",62);

}

@Bean("linus")

@Conditional({LinuxCondition.class})

public Person person2(){

return new Person("Linus",48);

}

}

public class AppTest {

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);

@Test

public void test(){

String osName = applicationContext.getEnvironment().getProperty("os.name");

System.out.println("当前系统为:" + osName);

Map map = applicationContext.getBeansOfType(Person.class);

System.out.println(map);

}

}

输出的结果:

当前系统为:Mac OS X

{linus=Person(name=Linus, age=48)}

@ConditionalOnBean & @ConditionalOnMissingBean

这两个注解会对Bean容器中的Bean对象进行判断,使用的例子是配置的时候,如果发现如果没有Computer实例,则实例化一个备用电脑。

@Data

@AllArgsConstructor

@ToString

public class Computer {

private String name;

}

@Configuration

public class BeanConfig {

@Bean(name = "notebookPC")

public Computer computer1(){

return new Computer("笔记本电脑");

}

@ConditionalOnMissingBean(Computer.class)

@Bean("reservePC")

public Computer computer2(){

return new Computer("备用电脑");

}

}

public class TestApp {

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);

@Test

public void test1(){

Map map = applicationContext.getBeansOfType(Computer.class);

System.out.println(map);

}

}

修改BeanConfig,如果注释掉第一个@Bean,会实例化备用电脑,否则就不会实例化备用电脑

@ConditionalOnClass & @ConditionalOnMissingClass

这个注解会判断类路径上是否有指定的类,一开始看到的时候比较困惑,类路径上如果没有指定的class,那编译也通过不了啊...这个主要用于集成相同功能的第三方组件时用,只要类路径上有该组件的类,就进行自动配置,比如spring boot web在自动配置视图组件时,是用Velocity,还是Thymeleaf,或是freemaker时,使用的就是这种方式。

例子是两套盔甲A(光明套装)和B(暗黑套装),如果A不在则配置B。

public interface Fighter {

void fight();

}

public class FighterA implements Fighter {

@Override

public void fight() {

System.out.println("使用光明套装");

}

}

public class FighterB implements Fighter {

@Override

public void fight() {

System.out.println("使用暗黑套装");

}

}

Van是武士,使用套装进行战斗

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Van {

private Fighter fighter;

public void fight(){

fighter.fight();

}

}

VanConfigA/B实例化武士

@Configuration

@ConditionalOnClass({FighterA.class})

public class VanConfigA {

@Primary

@Bean

public Van vanA(){

return new Van(new FighterA());

}

}

@Configuration

@ConditionalOnClass({FighterB.class})

public class VanConfigB {

@Bean

public Van vanB(){

return new Van(new FighterB());

}

}

测试类,默认情况,如果套装AB都在类路径上,两套都会加载,A会设置为PRIMARY,如果在target class中将FightA.class删除,则只会加载套装B。

@SpringBootApplication

public class TestApp implements CommandLineRunner {

@Autowired

private Van van;

public static void main(String[] args) {

SpringApplication.run(TestApp.class, args);

}

@Override

public void run(String... args) throws Exception {

//do something

van.fight();

ZXqkZlA }

}

另外,尝试将两个VanConfigA/B合并,将注解ConditionalOnClass放到方法上,如果删除一个套装就会运行出错。

@ConditionalOnExpress

依据表达式进行条件判断,这个作用和@ConditionalOnProperty大部分情况可以通用,表达式更灵活一点,因为可以使用SpEL。例子中会判断properties中test.enabled的值进行判断。BeanConfig分别对布尔,字符串和数字三种类型进行判断。数字尝试了很多其他的方式均不行,比如直接使用==,貌似配置的属性都会当成字符串来处理。

@Data

public class TestBean {

private String name;

}

@Configuration

@ConditionalOnExpression("#{${test.enabled:true} }")

//@ConditionalOnExpression("'zz'.equalsIgnoreCase('${test.name2}')")

//@ConditionalOnExpression("new Integer('${test.account}')==1")

public class BeanConfig {

@Bean

public TestBean testBean(){

return new TestBean("我是美猴王");

}

}

@SpringBootApplication

public class TestAppCommand implements CommandLineRunner {

@Autowired

private TestBean testBean;

public static void main(String[] args) {

SpringApplication.run(TestAppCommand.class, args);

}

@Override

public void run(String... args) throws Exception {

System.out.println(testBean.getName());

}

}

@ConditionalOnProperty

适合对单个Property进行条件判断,而上面的@ConditionalOnExpress适合面对较为复杂的情况,比如多个property的关联比较。这个例子也给了三种基本类型的条件判断,不过貌似均当成字符串就可以...

@Data

@AllArgsConstructor

@NoArgsConstructor

public class TestBean {

private String name;

}

@Configuration

@ConditionalOnProperty(prefix = "test", name="enabled", havingValue = "true",matchIfMissing = false)

//@ConditionalOnProperty(prefix = "test", name="account", havingValue = "1",matchIfMissing = false)

//@ConditionalOnProperty(prefix = "test", name="name1", havingValue = "zz",matchIfMissing = false)

public class BeanConfig {

@Bean

public TestBean testBean(){

return new TestBean("我是美猴王");

}

}

@SpringBootApplication

public class TestAppCommand implements CommandLineRunner {

@Autowired

private TestBean testBean;

public static void main(String[] args) {

SpringApplication.run(TestAppCommand.class, args);

}

@Override

public void run(String... args) throws Exception {

System.out.println(testBean.getName());

}

}

@ConditionalOnjava

可以通过java的版本进行判断。

@Data

public class TestBean {

}

@Configuration

@ConditionalOnJava(JavaVersion.EIGHT)

public class BeanConfig {

@Bean

public TestBean testBean(){

return new TestBean();

}

}

public class TestApp {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);

@Test

public void test(){

Map map = context.getBeansOfType(TestBean.class);

System.out.println(map);

}

}

@ConditionalOnResource

通过指定的资源文件是否存在进行条件判断,比如判断ehcache.properties来决定是否自动装配ehcache组件。

@Data

public class TestBean {

}

@Configuration

@ConditionalOnResource(resources = "classpath:application.yml")

public class BeanConfig {

@Bean

public TestBean testBean()http://{

return new TestBean();

}

}

public class TestApp {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);

@Test

public void test(){

Map map = context.getBeansOfType(TestBean.class);

System.out.println(map);

}

}

@ConditionalOnSingleCandidate

这个还没有想到应用场景,条件通过的条件是:1 对应的bean容器中只有一个 2.对应的bean有多个,但是已经制定了PRIMARY。例子中,BeanB装配的时候需要看BeanA的装配情况,所以BeanBConfig要排在BeanAConfig之后.可以修改BeanAConfig,将@Primary注解去掉,或者把三个@Bean注解去掉,BeanB就不会实例化了。

@Data

@AllArgsConstructor

@NoArgsConstructor

public class BeanA {

private String name;

}

@Configuration

public class BeanAConfig {

@Bean

@Primary

public BeanA bean1(){

return new BeanA("bean1");

}

@Bean(autowireCandidate = false)

public BeanA bean2(){

return new BeanA("bean2");

}

//@Bean(autowireCandidate = false)

public BeanA bean3(){

return new BeanA("bean3");

}

}

@Data

public class BeanB {

}

@Configuration

@AutoConfigureAfter(BeanAConfig.class)

@ConditionalOnSingleCandidate(BeanA.class)

public class BeanBConfig {

@Bean

public BeanB targetBean(){

return new BeanB();

}

}

public class TestApp {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanAConfig.class, BeanBConfig.class);

@Test

public void test(){

Map map = context.getBeansOfType(BeanA.class);

System.out.println(map);

Map map2 = context.getBeansOfType(BeanB.class);

System.out.println(map2);

}

}

@ConditionalOnNotWebApplication & @ConditionalOnWebApplication

判断当前环境是否是Web应用。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:自动化接口测试语言(自动化测试 语言)
下一篇:微服务网关要做权限认证吗(为什么需要微服务网关)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~