AnthonyZero's Bolg

Spring:条件装配

Spring条件装配

从 Spring Framework 3.1 开始,允许在 Bean 装配时增加前置条件判断

条件注解举例

Spring注解 场景说明 起始版本
@Profile 配置化条件装配 3.1
@Conditional 编程条件装配 4.0

配置Profile Bean

在3.1版本中,Spring引入了bean frofile的功能。要使用profile,你首先要将所有不同的bean定义整理到一个或多个profile之中,在将应用部署到每个环境时,要确保对应的profile处于激活(active)状态。

@profile(“dev”)
@profile注解应用在类级别上。他会告诉Spring这个配置类中的bean只有在dev profile激活时才会创建。如果dev profile没有激活的话,那么带有@Bean注解的方法都会被忽略掉
当你有多个profile bean时,在运行期间只会创建一个bean,这取决于激活状态的是哪个profile

Spring在确定哪个profile处于激活状态是需要依赖两个独立的属性:spring.profiles.active和spring.profile.default。他会先查找有没有active设置如果没有再去查找default设置。如果都没有设置那就不会激活profile中的bean。

条件化的bean

假设你希望一个或多个bean只有在应用的类路径下包含特定的库时才创建。或者我们希望某个bean只要当另外某个特定的bean也声明了之后才会创建。我们还可能要求只有某个特定的环境变量设置之后,才会创建某个bean。

在Spring 4引入了一个新的@Conditional注解,他可以用到带有@Bean注解的方法上。如果给定的条件计算结果为true,就会创建这个bean,否则的话,这个bean就会被忽略。

基于配置方式实现 - @Profile

计算服务,多整数求和 sum:
@Profile(“Java7”) : for 循环实现
@Profile(“Java8”) : Lambda实现

定义一个计算服务接口

/**
 * 计算服务
 */
public interface CalcService {

    public Integer sum(Integer... values);
}

JAVA7 Profile for循环计算和

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Profile("java7")
@Service
public class Java7CalcService implements CalcService{
    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java 7 for循环实现");
        int nums = 0;
        for (int i = 0; i< values.length; i++) {
            nums += values[i];
        }
        return nums;
    }
}

JAVA8 Profile Lambda Stream流计算和

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import java.util.stream.Stream;

@Profile("java8")
@Service
public class Java8CalcService implements CalcService{
    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java 8 Lambda实现");
        return Stream.of(values).reduce(0,Integer::sum);
    }
}

SpringBoo启动方式:

@SpringBootApplication(scanBasePackages = "com.pingjin.service")
public class CalcServiceBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(CalcServiceBootstrap.class)
                .web(WebApplicationType.NONE)
                .profiles("java8") //指定profile java8
                .run(args);
        CalcService calcService = context.getBean(CalcService.class);

        System.out.println("最后值为:" + calcService.sum(1,2,3,4,5,6,7,8,9,10));

        context.close();
    }
}

输出结果:

Alt text

基于编程方式实现 - @Conditional

根据系统属性条件化装配需要的Bean
自定义注解定义

import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {

    //键
    String name();
    //值
    String value();
}

OnSystemPropertyCondition实现Condition接口 实现自己条件装配判断:这里简单化将自己本机的系统user.name值用于判断

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;

public class OnSystemPropertyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
        String propertyName = String.valueOf(annotationAttributes.get("name"));
        String propertyValue = String.valueOf(annotationAttributes.get("value"));
        System.out.println("注解键值对为:" + propertyName + "->" + propertyValue);

        String systemValue = System.getProperty(propertyName);
        System.out.println("系统值:" + systemValue); //这里我的值为Anthony_One
        return systemValue.equals(propertyValue);
    }
}

SpringBoot项目启动运行:

public class SystemPropertyConditionBootstrap {

    @Bean
    @ConditionalOnSystemProperty(name = "user.name", value = "Anthony_One")
    public String helloWorld() {
        return "hello world";
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(SystemPropertyConditionBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        //条件装配不满足的时候,获取HelloWorld Bean的时候 会报异常 No bean named 'helloWorld' available
        String helloWorld = context.getBean("helloWorld", String.class); 
        System.out.println("helloWorld Bean: " + helloWorld);

        context.close();
    }

}

输出结果:

Alt text