一、接口Condition、Conditional(原理)
主要提供一下方法
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
true:表示装配
false:表示不装配
1.1、Conditional
在Spring4中引入,其主要作用就是判断条件是否满足,从而决定是否初始化并向容器注册Bean
注解:Conditional() 参数是数组,数组内的都是true才装配
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { /** * All {@link Condition}s that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); }
通常配合使用。
示例:
接口:EncodingConvert
package com.lhx.spring.springboot_auto_config; public interface EncodingConvert { }
接口实现一:UTF8EncodingConvert
package com.lhx.spring.springboot_auto_config; public class UTF8EncodingConvert implements EncodingConvert { }
接口实现二:GBKEncodingConvert
package com.lhx.spring.springboot_auto_config; public class GBKEncodingConvert implements EncodingConvert { }
配置类:
package com.lhx.spring.springboot_auto_config; import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @SpringBootConfiguration public class EncodingConvertConfiguration { @Bean public EncodingConvert createGBKEncodingConvert() { return new GBKEncodingConvert(); } @Bean public EncodingConvert createUTF8EncodingConvert() { return new UTF8EncodingConvert(); } }
App:
package com.lhx.spring.springboot_auto_config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Conditional; @SpringBootApplication public class App { @Bean public Runnable createRunnable() { return () -> { System.out.println("spring boot is running"); }; } public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(App.class, args); context.getBean(Runnable.class).run(); //可以通过启动参数修改-Dfile.encoding=GBK System.out.println(System.getProperty("file.encoding")); System.out.println(context.getBeansOfType(EncodingConvert.class)); context.close(); } }
此时,会发现连个接口实现都会被装配进来。
UTF8Condition实现Condition接口
package com.lhx.spring.springboot_auto_config; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class UTF8Condition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String encoding = System.getProperty("file.encoding"); if (encoding != null) { return "utf-8".equalsIgnoreCase(encoding); } return false; } }
GBKCondition实现Condition接口
package com.lhx.spring.springboot_auto_config; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class GBKCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String encoding = System.getProperty("file.encoding"); if (encoding != null) { return "gbk".equalsIgnoreCase(encoding); } return false; } }
修改配置类
package com.lhx.spring.springboot_auto_config; import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @SpringBootConfiguration public class EncodingConvertConfiguration { @Bean @Conditional(GBKCondition.class) public EncodingConvert createGBKEncodingConvert() { return new GBKEncodingConvert(); } @Bean @Conditional(UTF8Condition.class) public EncodingConvert createUTF8EncodingConvert() { return new UTF8EncodingConvert(); } }
此时,会发现会根据file.encoding值来装配接口类。
可在启动参数增加
-Dfile.encoding=GBK
然后调试,发现装配类也变了
注意:@Conditional也可以作用在类上
二、Spring提供的Conditional自动配置
jar:spring-boot-autoconfigure中,org.springframework.boot.autoconfigure.condition;即spring-boot提供
ConditionalOnBean:当存在某个bean时候装配
ConditionalOnMissingBean:当不存在某个bean时候装配注解的bean
ConditionalOnClass:当classpath有才装配
ConditionalOnExpression:
ConditionalOnJava:JDK版本符合时候才装配
ConditionalOnNotWebApplication:不是web环境才装配
ConditionalOnWebApplication:是web环境才装配
ConditionalOnResource:资源存在才装配
ConditionalOnProperty:配置存在才装配
2.1、ConditionalOnProperty 配置存在匹配时候才配置
增加配置类
package com.lhx.spring.springboot_auto_config; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @SpringBootConfiguration public class UserConfiguration { @Bean @ConditionalOnProperty(name = "runnable.enable", havingValue = "true") public Runnable createRunnable() { return () -> { }; } }
App2编写
package com.lhx.spring.springboot_auto_config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Conditional; @SpringBootApplication public class App2 { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(App2.class, args); System.out.println(context.getBeansOfType(Runnable.class)); context.close(); } }
默认是不能装配的
可以再application.properties中添加runnable.enable=true即可装配
或者@ConditionalOnProperty(name = "runnable.enable", havingValue = "true")增加
matchIfMissing=true,表示配置没有的时候也生效
2.2、ConditionalOnClass classpath 有某个类才装配
增加或删除maven,查看效果
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.2</version> </dependency>
使用代码
@Bean @ConditionalOnClass(name="com.google.gson.Gson") public Runnable createGsonRunnable() { return () -> { }; }
2.3、ConditionalOnBean:根据容器中是否存在某个Bean进行装配
@Bean @ConditionalOnBean(name="user") public Runnable createOnBeanRunnable() { return () -> { }; }
示例一、根据配置选择不同的实现类注入
需求:根据配置选择不同的实现类注入,有个业务逻辑,有两套模式,大部分功能流程一致,但涉及某些实现不一致,标准的模板模式:004-行为型-02-模板方法模式(Template Method)
实际使用中会有,默认是一种实现,如果有某个配置,则生效。可以参看springboot自动装配实现,如Cache等
实现:https://github.com/bjlhx15/common.git spring-framework-core/spring-aop/testconditional
基础业务接口
public interface ITestConditionalService { String getBefore(); String get(); }
如果新增业务需要新增配置
public class ConstBean { public final static String atest="atest"; public final static String btest="btest"; }
抽象类实现公共方案步骤
public abstract class AbstractTestConditionalService implements ITestConditionalService { @Override public abstract String getBefore(); @Override public String get() { String before = this.getBefore(); // do something return "before:" + before; } }
第一种实现方案:也就是默认方案
@Service("aTestConditionalServiceImpl") @ConditionalOnProperty(name = "set.test",havingValue = ConstBean.atest,matchIfMissing = true) public class ATestConditionalServiceImpl extends AbstractTestConditionalService { @Override public String getBefore() { return "atest test"; } }
第二种方案
@Service("bTestConditionalServiceImpl") @ConditionalOnProperty(name = "set.test",havingValue = ConstBean.btest) public class BTestConditionalServiceImpl extends AbstractTestConditionalService { @Override public String getBefore() { return "btest test"; } }
配置 如果是a逻辑可默认不写
# 配置实现 atest、btest
set.test=atest