SpringBoot学习笔记---自动装配续2之条件装配

上一篇文章我们讲了模块装配,接下来我们讲述一下条件装配

什么是条件装配?

Bean装配的前置判断

有哪些例子?

@Profile(配置化条件装配) @Conditional(编程条件装配)

有哪些实现方式?

注解方式,接口方式

@Profile在Spring3.0时代出现的,使用方式就是在类上面加上@Profile(XXX),如果我们在SpringBoot启动的时候使用了.profiles("XXX"),那么这个类就会被装配进来。在4.0时代,发生了变化,Profile有了@Conditional这个注解。

上面讲的很抽象,我们还是用具体的例子来帮助大家理解一下:

首先看看@Profile的使用:

我们假设有这么一个场景,声明一个接口CalculateService,这个接口中只有一个方法sum(),如下:

public interface CalculateService {

    /**
     * 从多个整数 sum 求和
     * @param values 多个整数
     * @return sum 累加值
     */
    Integer sum(Integer... values);
}

如果profile为java8时,我们采用lambda表达式来实现这个接口,并调用这个实现类的sum方法求和,如果profile为java7是,我们采用传统的方式来实现这个接口,并调用这个实现类的sum方法求和。

我们先看看Java7CalculateService的实现

@Profile("Java7")
@Service
public class Java7CalculateService implements CalculateService {

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

}

这个实现类有个注解@Profile("java7"),表示当我们的spring应用启动的时候指明了profile是java7才会生效。

我们再看看Java8CalculateService的实现

@Profile("Java8")
@Service
public class Java8CalculateService implements CalculateService {

    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java 8 Lambda 实现");
        int sum = Stream.of(values).reduce(0, Integer::sum);
        return sum;
    }

}

达到的效果和上面很像。对lambda不熟悉的同学可能比较疑惑Integer::sum还有reduce是什么意思,我还是解答一下:

Stream.of是把传入的数组转换成流,reduce是对这个流进行相关计算,这个函数接受两个参数,一个初始值identity和一个accumulator函数,啥意思呢,就是以下这个意思:

* <pre>{@code
     *     T result = identity;
     *     for (T element : this stream)
     *         result = accumulator.apply(result, element)
     *     return result;
     * }</pre>

这里的accumulator函数式Integer类在java8中提供的一个新的函数,而且是一个二元函数哦,具体实现看下面: 

    /**
     * Adds two integers together as per the + operator.
     *
     * @param a the first operand
     * @param b the second operand
     * @return the sum of {@code a} and {@code b}
     * @see java.util.function.BinaryOperator
     * @since 1.8
     */
    public static int sum(int a, int b) {
        return a + b;
    }

回归正题,我们定义好这两个实现类后,那应该怎么去使用呢?

@SpringBootApplication(scanBasePackages = "com.imooc.diveinspringboot.service")
public class CalculateServiceBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(CalculateServiceBootstrap.class)
                .web(WebApplicationType.NONE)
                .profiles("Java8")
                .run(args);

        // CalculateService Bean 是否存在
        CalculateService calculateService = context.getBean(CalculateService.class);

        System.out.println("calculateService.sum(1...10) : " +
                calculateService.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

        // 关闭上下文
        context.close();
    }
}

重点看下里面.profiles这个方法,还有注解的scanBasePackages,这个就保证了应用获得的究竟是哪个实现类的Bean。好了Profile的原理和使用方式我们讲到这里,接下来我们讲一讲Condition:

在SprintBoot的spring-boot-autoconfigure.data.elasticsearch这个jar包里面,可以看到很多的装配类,比如ElasticsearchAutoConfiguration 这个类,这个类上面使用了@ConditionalOnProperty(prefix = "spring.data.elasticsearch", name = "cluster-nodes", matchIfMissing = false),这个ConditionalOnProperty包含了@Conditional(OnPropertyCondition.class),这个OnPropertyCondition里面就是对@ConditionalOnProperty里面的内容进行解析,看是否能匹配,如果匹配成功了,才进行下一步装配操作。那么我们可以用自己的方式来模拟实现一下这个ConditionalOnProperty,然后真正的理解它。

我们这里实现这样一个场景,如果我的系统环境变量的用户名字和注解上用户提供的名字一样,那么我们就装配一个String类型的Bean,否则不装配。

首先定义一个引导类:

public class ConditionalOnSystemPropertyBootstrap {

    @Bean
    @ConditionalOnSystemProperty(name = "user.name", value = "天帝")
    public String helloWorld() {
        return "Hello,World 光贤";
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(ConditionalOnSystemPropertyBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        // 通过名称和类型获取 helloWorld Bean
        String helloWorld = context.getBean("helloWorld", String.class);

        System.out.println("helloWorld Bean : " + helloWorld);

        // 关闭上下文
        context.close();
    }
}

我们看到helloWorld方法上面有一个 @ConditionalOnSystemProperty(name = "user.name", value = "天帝"),这个注解的作用是为了说如果系统环境变量中的user.name是天帝,那么久装配一个String类型的Bean。

我们看一下我们的ConditionalOnSystemProperty的实现:

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

    /**
     * Java 系统属性名称
     * @return
     */
    String name();

    /**
     * Java 系统属性值
     * @return
     */
    String value();
}

这里最关键的还是它上面的@Conditional(OnSystemPropertyCondition.class),好了,我们直接进入到OnSystemPropertyCondition看看它到底做了什么:

public class OnSystemPropertyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());

        String propertyName = String.valueOf(attributes.get("name"));

        String propertyValue = String.valueOf(attributes.get("value"));

        String javaPropertyValue = System.getProperty(propertyName);
        System.out.println("-------"+javaPropertyValue+"-----------------");
        return propertyValue.equals(javaPropertyValue);
    }
}

这里的OnSystemPropertyCondition必须要实现Condition这个接口的matches方法,这个方法可以获取到我们注解中设置的值,然后与系统环境变量中的值比对,如果比对一样,那么返回true,表示我们的Conditional的条件成立,这个String类型的bean就可以装配到我们的容器中去了。这就是为什么我们在SpringBoot中的application.propertis里面写了一些配置项后,会发现应用给我们自动装配一些Bean,其中的原理就是这样的。

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值