摘要:通过操作系统进行条件判断,从而进行配置。分别对布尔,字符串和数字三种类型进行判断。通过指定的资源文件是否存在进行条件判断,比如判断来决定是否自动装配组件。判断当前环境是否是应用。
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); Mapmap = applicationContext.getBeansOfType(Person.class); System.out.println(map); } }
输出的结果:
当前系统为:Mac OS X@ConditionalOnBean & @ConditionalOnMissingBean
{linus=Person(name=Linus, age=48)}
这两个注解会对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(){ Mapmap = 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@ConditionalOnResourcemap = context.getBeansOfType(TestBean.class); System.out.println(map); } }
通过指定的资源文件是否存在进行条件判断,比如判断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@ConditionalOnSingleCandidatemap = context.getBeansOfType(TestBean.class); System.out.println(map); } }
这个还没有想到应用场景,条件通过的条件是: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@ConditionalOnNotWebApplication & @ConditionalOnWebApplicationmap = context.getBeansOfType(BeanA.class); System.out.println(map); Map map2 = context.getBeansOfType(BeanB.class); System.out.println(map2); } }
判断当前环境是否是Web应用。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/77536.html
摘要:与的关系是什么是官方提出的持久化规范。它为开发人员提供了一种对象关联映射工具来管理应用中的关系数据。他的出现主要是为了简化现有的持久化开发工作和整合技术,结束现在,,等框架各自为营的局面。定义了在对数据库中的对象处理查询和事务运行时的的。 导读: 在上篇文章中对Spring MVC常用的一些注解做了简要的说明,在这篇文章中主要对Spring Data JPA 做一个简要的说明,并附有一...
摘要:学习笔记使用很容易创建一个独立运行运行内嵌容器准生产级别的基于框架的项目,使用你可以不用或者只需要很少的配置。异常消息如果这个错误是由异常引起的。错误发生时请求的路径。 Spring-Boot 1.5 学习笔记 使用Spring Boot很容易创建一个独立运行(运行jar,内嵌Servlet容器)、准生产级别的基于Spring框架的项目,使用Spring Boot你可以不用或者只需要很...
摘要:下一代服务端开发下一代服务端开发第部门快速开始第章快速开始环境准备,,快速上手实现一个第章企业级服务开发从到语言的缺点发展历程的缺点为什么是产生的背景解决了哪些问题为什么是的发展历程容器的配置地狱是什么从到下一代企业级服务开发在移动开发领域 《 Kotlin + Spring Boot : 下一代 Java 服务端开发 》 Kotlin + Spring Boot : 下一代 Java...
摘要:代码如下所示自定义业务实现恒宇少年码云消息内容是否显示消息内容,我们内的代码比较简单,根据属性参数进行返回格式化后的字符串。 在我们学习SpringBoot时都已经了解到starter是SpringBoot的核心组成部分,SpringBoot为我们提供了尽可能完善的封装,提供了一系列的自动化配置的starter插件,我们在使用spring-boot-starter-web时只需要在po...
摘要:如果还在使用以前的版本,想要使用非严格的,需要做以下配置在中引入依赖在中配置更多属性配置请参考中模块的属性介绍。这样的话很好的做到了前后端分离。 本文首发于:https://y0ngb1n.github.io/a/5... 开发环境 org.springframework.boot spring-boot-starter-parent 2.1.0.RELEASE ...
阅读 2027·2023-04-26 02:23
阅读 1770·2021-09-03 10:30
阅读 1250·2019-08-30 15:43
阅读 1081·2019-08-29 16:29
阅读 461·2019-08-29 12:28
阅读 2235·2019-08-26 12:13
阅读 2078·2019-08-26 12:01
阅读 2330·2019-08-26 11:56