Spring 条件装配有两种方式:
-
@Profile
:基于当前环境配置方式; -
@Conditional
:基于编程方式;
@Profile
先看一个例子:
@SpringBootApplication
public class ConditionBootstrap {
public static void main(String[] args) {
SpringApplication springBootApplication = new SpringApplication(ConditionBootstrap.class);
springBootApplication.setWebApplicationType(WebApplicationType.NONE);
springBootApplication.setAdditionalProfiles("dev");
ConfigurableApplicationContext context = springBootApplication.run(args);
System.out.println(context.getBeansOfType(User.class));
}
@Bean
@Profile("dev")
public User user1(){
User user = new User();
user.setName("dev");
return user;
}
@Bean
@Profile("prod")
public User user2(){
User user = new User();
user.setName("prod");
return user;
}
}
输出结果:
{user1=User{name='dev', age=null}}
同样地,也可以在 application.properties 文件里面指定 profile:
spring.profiles.active = prod
那么如果在 application.properties 中指定为 prod,在 SpringApplication 中指定为 dev 会怎么样呢,运行结果:
{user1=User{name='dev', age=null}, user2=User{name='prod', age=null}}
也就是说这两个的 profile 的配置优先级是一致的。
在刚刚的配置的基础上再尝试下,先打个包:
mvn clean package -Dmaven.test.skip=true -X
执行 jar:
java -jar /Users/dongguabai/IdeaProjects/dongguabai/spring-boot-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
运行结果:
{user1=User{name='dev', age=null}}
也就是说 --spring.profiles.active
的优先级是最高的。
@Conditional
@Profile
使用虽然很简单,但是功能很单一,而 @Conditional
基于接口编程,有着更丰富的功能,在 Spring Boot 中有大量的应用。
其实 @Profile
在后续版本也是基于 @Conditional
实现:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
@Conditional
注解里面需要传入 Condition
的实现:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition Conditions} that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
可以看到 @Conditional
注解可以标注在类上,也可以标注在方法上,如果是在方法上,比如 @Bean
这种,符合条件就会注入这个 Bean,如果标注在类上,比如 @Configuration
,就决定了这一批 Bean 是否注入。
看看 ProfileCondition
类:
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
}
实现了 Condition
接口,根据 matches
方法进行匹配。看看 Condition
接口:
@FunctionalInterface
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
* @return {@code true} if the condition matches and the component can be registered,
* or {@code false} to veto the annotated component's registration
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
是一个函数式接口。可以参照这个实现自己来一个,这里根据当前的系统操作环境决定注入哪个 Bean。 先定义一个注解:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnOSTypeCondition.class)
public @interface ConditionalOnOSType {
Type type() default Type.ANY;
enum Type {
MAC,
WINDOWS,
ANY
}
}
Condition
的实现:
public class OnOSTypeCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnOSType.class.getName());
ConditionalOnOSType.Type osType = (ConditionalOnOSType.Type)attributes.get("type");
return System.getProperty("os.name").toUpperCase().contains(osType.name());
}
}
测试:
@SpringBootApplication
public class ConditionBootstrap {
public static void main(String[] args) {
SpringApplication springBootApplication = new SpringApplication(ConditionBootstrap.class);
springBootApplication.setWebApplicationType(WebApplicationType.NONE);
//springBootApplication.setAdditionalProfiles("dev");
ConfigurableApplicationContext context = springBootApplication.run(args);
System.out.println(context.getBeansOfType(User.class));
}
@Bean
@ConditionalOnOSType(type = ConditionalOnOSType.Type.MAC)
public User user1(){
User user = new User();
user.setName("MAC");
return user;
}
@Bean
@ConditionalOnOSType(type = ConditionalOnOSType.Type.WINDOWS)
public User user2(){
User user = new User();
user.setName("WINDOWS");
return user;
}
}
输出:
{user1=User{name='MAC', age=null}}