• 利用Spring的@ConditionXXX注解实现策略模式


    在项目开发过程中,有这样的一种常见的场景,比如根据环境的不同,发短信的服务也是不同的,但是依赖短信服务的系统调用的都是相同的接口,这样就需要针对环境来做区分,调用不同的短信通道接口。举例的这种情况,你可能会想到使用策略模式的方式来实现,当然这也是可以的。

    但是只有两种策略而且在一个环境当中,只会有一种固定策略可以生效的情况下,使用策略模式未免过于麻烦,本文尤其适合私有化项目部署,会根据私有化环境执行不同策略的情况。

    本文首发于个人博客:http://nullpointer.pw/spring-condition-annotation.html

    应用场景

    本文讲解如何使用@ConditionXXX 系列注解来更方便地实现这种策略选择的场景。

    @ConditionXXX 系列注解是 Spring 提供的一组注解,下面表格列出所有的注解以及对应的作用,常用的注解通过☆标注

    上文中的例子中,多个策略同一个环境其实只需要加载一个适合的策略即可,完全没有必要加载用不上的其他策略,在系统启动时,只取所需的策略就足矣,实现起来相较于策略模式方便不是一点点。

    常用注解

    注解 作用 常用
    @ConditionalOnProperty 当指定的属性有指定的值时进行实例化
    @ConditionalOnMissingBean 当容器里没有指定 Bean 的条件下进行实例化。
    @ConditionalOnExpression 基于 SpEL 表达式的条件判断,多条件
    @ConditionalOnBean 仅仅在当前上下文中存在某个对象时,才会实例化一个 Bean。

    以不同环境发送短信为例,进行代码实现 (当然这个例子有点强上的异味,不过用于来解释这几个主键的功用却是很合适,具体应用场景请触类旁通)。

    示例实现

    首先会定义一个用于发送短信的接口,因为会有多种环境,如开发环境、测试环境、生产环境,所以会有相应环境的实现类。

    public interface SmsService {
        Boolean send(String mobile, String content);
    }
    

    定义多环境配置类

    - application.yml
    - application-dev.yml
    - application-prod.yml
    - application-test.yml
    

    application-xxx.yml 中分别指定配置,如 application-dev.yml

    custom:
      env: dev
    

    实现每个环境的具体短信发送实现类,使用到了 @ConditionalOnProperty(name = {"custom.env"}, havingValue = "test") ,含义就是读取配置文件中 key 为 custom.env 的配置,如果值为 havingValue 属性所指定的值 test,则会实例化这个 Bean,否则不进行实例化。

    以下其他环境的同理,不再赘述。

    /**
     * 测试环境短信通道
     *
     * @author WeJan
     * @since 2020-04-28
     */
    @Service
    @ConditionalOnProperty(name = {"custom.env"}, havingValue = "test")
    public class TestSmsServiceImpl implements SmsService {
        @Override
        public Boolean send(String mobile, String content) {
            System.out.println("短信发送成功, by TestSmsServiceImpl");
            return true;
        }
    }
    
    /**
     * 开发环境短信通道
     *
     * @author WeJan
     * @since 2020-04-28
     */
    @Service
    @ConditionalOnProperty(name = {"custom.env"}, havingValue = "dev")
    public class DevSmsServiceImpl implements SmsService {
    
        @Override
        public Boolean send(String mobile, String content) {
            System.out.println("短信发送成功, by DevSmsServiceImpl");
            return true;
        }
    }
    
    /**
     * 生产环境短信通道
     *
     * @author WeJan
     * @since 2020-04-28
     */
    @Service
    @ConditionalOnProperty(name = {"custom.env"}, havingValue = "prod")
    public class ProdSmsServiceImpl implements SmsService {
    
        @Override
        public Boolean send(String mobile, String content) {
            System.out.println("短信发送成功, by ProdSmsServiceImpl");
            return true;
        }
    }
    

    正常情况下,使用 @ConditionalOnProperty 便足以满足大部分的需求,但还是有例外情况,需要设置兜底策略,可以通过 @ConditionalOnMissingBean 来实现,该注解 value 属性中对应的类若都未曾实例化,则实例化当前的 Bean。

    **
     * 默认短信通道
     *
     * @author WeJan
     * @since 2020-04-28
     */
    @Service
    @ConditionalOnMissingBean(value = {DevSmsServiceImpl.class, TestSmsServiceImpl.class, ProdSmsServiceImpl.class})
    public class DefaultSmsServiceImpl implements SmsService {
    
        @Override
        public Boolean send(String mobile, String content) {
            System.out.println("短信发送成功, by DefaultSmsServiceImpl");
            return true;
        }
    }
    

    测试一番

    为了方便测试,使用 @ActiveProfiles 注解指定当前测试使用的配置环境,

    @RunWith(SpringRunner.class)
    //@ActiveProfiles(value = "prod")
    //@ActiveProfiles(value = "dev")
    @ActiveProfiles(value = "test")
    @SpringBootTest(classes = SpringApp.class)
    public class SmsServiceTest {
        @Resource
        private SmsService smsService;
        @Test
        public void testSend() {
            smsService.send("13333333333", "Test");
        }
    }
    

    测试当激活环境为 dev 时,控制台输出,其他环境不再演示,读者可自行尝试。

    短信发送成功, by DevSmsServiceImpl
    
  • 相关阅读:
    迭代器在LinkedList上的删除
    java多线程:CopyOnWriteArrayList
    vs中代码编译通过,但还是有红色波浪线
    vs中项目属性配置
    TortoiseGit安装与配置
    DC(device context)
    weak_ptr 使用
    C++ 中shared_ptr循环引用计数问题
    for_each与lambda表达式联合使用
    new 和 make_shared 在内存上的区别
  • 原文地址:https://www.cnblogs.com/vcmq/p/12813032.html
Copyright © 2020-2023  润新知