Spring Boot中的那些条件判断的实现方法
SpringBoot中的那些Conditional
springboot中为我们提供了丰富的Conditional来让我们得以非常方便的在项目中向容器中添加Bean。本文主要是对各个注解进行解释并辅以代码说明其用途。
所有ConditionalOnXXX的注解都可以放置在class或是method上,如果方式在class上,则会决定该class中所有的@Bean注解方法是否执行。
@Conditional
下面其他的Conditional注解均是语法糖,可以通过下面的方法自定义ConditionalOnXXX
Conditional注解定义如下,接收实现Condition接口的class数组。
public@interfaceConditional{ Class[]value(); }
而Condition接口只有一个matchs方法,返回是否匹配的结果。
publicinterfaceCondition{ booleanmatches(ConditionContextcontext,AnnotatedTypeMetadatametadata); }
通过操作系统进行条件判断,从而进行Bean配置。当Window时,实例化Bill的Person对象,当Linux时,实例化Linus的Person对象。
//LinuxCondition,为方便起见,去掉判断代码,直接返回true了 publicclassLinuxConditionimplementsCondition{ @Override publicbooleanmatches(ConditionContextconditionContext,AnnotatedTypeMetadataannotatedTypeMetadata){ returntrue; } }
//WindowsCondition,为方便起见,去掉判断代码,直接返回false了 publicclassWindowsConditionimplementsCondition{ @Override publicbooleanmatches(ConditionContextconditionContext,AnnotatedTypeMetadatametadata){ returnfalse; } } @Data
@ToString @AllArgsConstructor @NoArgsConstructor publicclassPerson{ privateStringname; privateIntegerage; }
//配置类 @Configuration publicclassBeanConfig{ @Bean(name="bill") @Conditional({WindowsCondition.class}) publicPersonperson1(){ returnnewPerson("BillGates",62); } @Bean("linus") @Conditional({LinuxCondition.class}) publicPersonperson2(){ returnnewPerson("Linus",48); } }
publicclassAppTest{ AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigApplicationContext(BeanConfig.class); @Test publicvoidtest(){ StringosName=applicationContext.getEnvironment().getProperty("os.name"); System.out.println("当前系统为:"+osName); Mapmap=applicationContext.getBeansOfType(Person.class); System.out.println(map); } }
输出的结果:
当前系统为:MacOSX
{linus=Person(name=Linus,age=48)}
@ConditionalOnBean&@ConditionalOnMissingBean
这两个注解会对Bean容器中的Bean对象进行判断,使用的例子是配置的时候,如果发现如果没有Computer实例,则实例化一个备用电脑。
@Data @AllArgsConstructor @ToString publicclassComputer{ privateStringname; }
@Configuration publicclassBeanConfig{ @Bean(name="notebookPC") publicComputercomputer1(){ returnnewComputer("笔记本电脑"); } @ConditionalOnMissingBean(Computer.class) @Bean("reservePC") publicComputercomputer2(){ returnnewComputer("备用电脑"); } }
publicclassTestApp{ AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigApplicationContext(BeanConfig.class); @Test publicvoidtest1(){ Mapmap=applicationContext.getBeansOfType(Computer.class); System.out.println(map); } }
修改BeanConfig,如果注释掉第一个@Bean,会实例化备用电脑,否则就不会实例化备用电脑
@ConditionalOnClass&@ConditionalOnMissingClass
这个注解会判断类路径上是否有指定的类,一开始看到的时候比较困惑,类路径上如果没有指定的class,那编译也通过不了啊...这个主要用于集成相同功能的第三方组件时用,只要类路径上有该组件的类,就进行自动配置,比如springbootweb在自动配置视图组件时,是用Velocity,还是Thymeleaf,或是freemaker时,使用的就是这种方式。
例子是两套盔甲A(光明套装)和B(暗黑套装),如果A不在则配置B。
publicinterfaceFighter{ voidfight(); } publicclassFighterAimplementsFighter{ @Override publicvoidfight(){ System.out.println("使用光明套装"); } } publicclassFighterBimplementsFighter{ @Override publicvoidfight(){ System.out.println("使用暗黑套装"); } }
Van是武士,使用套装进行战斗
@Data @AllArgsConstructor @NoArgsConstructor publicclassVan{ privateFighterfighter; publicvoidfight(){ fighter.fight(); } }
VanConfigA/B实例化武士
@Configuration @ConditionalOnClass({FighterA.class}) publicclassVanConfigA{ @Primary @Bean publicVanvanA(){ returnnewVan(newFighterA()); } } @Configuration @ConditionalOnClass({FighterB.class}) publicclassVanConfigB{ @Bean publicVanvanB(){ returnnewVan(newFighterB()); } }
测试类,默认情况,如果套装AB都在类路径上,两套都会加载,A会设置为PRIMARY,如果在targetclass中将FightA.class删除,则只会加载套装B。
@SpringBootApplication publicclassTestAppimplementsCommandLineRunner{ @Autowired privateVanvan; publicstaticvoidmain(String[]args){ SpringApplication.run(TestApp.class,args); } @Override publicvoidrun(String...args)throwsException{ //dosomething van.fight(); } }
另外,尝试将两个VanConfigA/B合并,将注解ConditionalOnClass放到方法上,如果删除一个套装就会运行出错。
@ConditionalOnExpress
依据表达式进行条件判断,这个作用和@ConditionalOnProperty大部分情况可以通用,表达式更灵活一点,因为可以使用SpEL。例子中会判断properties中test.enabled的值进行判断。BeanConfig分别对布尔,字符串和数字三种类型进行判断。数字尝试了很多其他的方式均不行,比如直接使用==,貌似配置的属性都会当成字符串来处理。
@Data publicclassTestBean{ privateStringname; }
@Configuration @ConditionalOnExpression("#{${test.enabled:true}}") //@ConditionalOnExpression("'zz'.equalsIgnoreCase('${test.name2}')") //@ConditionalOnExpression("newInteger('${test.account}')==1") publicclassBeanConfig{ @Bean publicTestBeantestBean(){ returnnewTestBean("我是美猴王"); } }
@SpringBootApplication publicclassTestAppCommandimplementsCommandLineRunner{ @Autowired privateTestBeantestBean; publicstaticvoidmain(String[]args){ SpringApplication.run(TestAppCommand.class,args); } @Override publicvoidrun(String...args)throwsException{ System.out.println(testBean.getName()); } }
@ConditionalOnProperty
适合对单个Property进行条件判断,而上面的@ConditionalOnExpress适合面对较为复杂的情况,比如多个property的关联比较。这个例子也给了三种基本类型的条件判断,不过貌似均当成字符串就可以...
@Data @AllArgsConstructor @NoArgsConstructor publicclassTestBean{ privateStringname; }
@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) publicclassBeanConfig{ @Bean publicTestBeantestBean(){ returnnewTestBean("我是美猴王"); } }
@SpringBootApplication publicclassTestAppCommandimplementsCommandLineRunner{ @Autowired privateTestBeantestBean; publicstaticvoidmain(String[]args){ SpringApplication.run(TestAppCommand.class,args); } @Override publicvoidrun(String...args)throwsException{ System.out.println(testBean.getName()); } }
@ConditionalOnJava
可以通过java的版本进行判断。
@Data publicclassTestBean{ }
@Configuration @ConditionalOnJava(JavaVersion.EIGHT) publicclassBeanConfig{ @Bean publicTestBeantestBean(){ returnnewTestBean(); } }
publicclassTestApp{ AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(BeanConfig.class); @Test publicvoidtest(){ Mapmap=context.getBeansOfType(TestBean.class); System.out.println(map); } }
@ConditionalOnResource
通过指定的资源文件是否存在进行条件判断,比如判断ehcache.properties来决定是否自动装配ehcache组件。
@Data publicclassTestBean{ }
@Configuration @ConditionalOnResource(resources="classpath:application.yml") publicclassBeanConfig{ @Bean publicTestBeantestBean(){ returnnewTestBean(); } }
publicclassTestApp{ AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(BeanConfig.class); @Test publicvoidtest(){ Mapmap=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 publicclassBeanA{ privateStringname; }
@Configuration publicclassBeanAConfig{ @Bean @Primary publicBeanAbean1(){ returnnewBeanA("bean1"); } @Bean(autowireCandidate=false) publicBeanAbean2(){ returnnewBeanA("bean2"); } //@Bean(autowireCandidate=false) publicBeanAbean3(){ returnnewBeanA("bean3"); } }
@Data publicclassBeanB{ }
@Configuration @AutoConfigureAfter(BeanAConfig.class) @ConditionalOnSingleCandidate(BeanA.class) publicclassBeanBConfig{ @Bean publicBeanBtargetBean(){ returnnewBeanB(); } }
publicclassTestApp{ AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(BeanAConfig.class,BeanBConfig.class); @Test publicvoidtest(){ Mapmap=context.getBeansOfType(BeanA.class); System.out.println(map); Map map2=context.getBeansOfType(BeanB.class); System.out.println(map2); } }
@ConditionalOnNotWebApplication&@ConditionalOnWebApplication
判断当前环境是否是Web应用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。