评论

收藏

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

编程语言 编程语言 发布于:2021-08-17 11:57 | 阅读数:344 | 评论:0

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<string, person> 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<string,computer> 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();
  }
}
另外,尝试将两个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<string,testbean> 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(){
  return new testbean();
  }
}
public class testapp {
  annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(beanconfig.class);
 
  @test
  public void test(){
  map<string,testbean> 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<string,beana> map = context.getbeansoftype(beana.class);
  system.out.println(map);
  map<string,beanb> map2 = context.getbeansoftype(beanb.class);
  system.out.println(map2);
  }
}
@conditionalonnotwebapplication & @conditionalonwebapplication
判断当前环境是否是web应用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持CodeAE代码之家
原文链接:https://segmentfault.com/a/1190000018831198

关注下面的标签,发现更多相似文章