spring-boot下如何知足多生產環境中個性化定製功能

在項目的開發中,咱們很難作到開發一套標準的流程來解決全部客戶的需求。好比,咱們當前的計量項目,分別運行於赤峯市和河北省。雖然兩個區域處理的業務相同,可是對細節的實現要求卻不一樣。前面也學習過計量檢定軟件,其爲了解決各個定製者使用的功能需求,最後採起的方案是:將基礎項目複製多份,進而知足不一樣的客戶需求。優勢固然是有的,但比起缺點來,優勢便不值一提。缺點很明顯,總結爲一句話就是:項目變得難以維護。因此,當前讓咱們看到的就是,幾個開發人員,天天處於解決問題當中。本文將給出一種方案,來有效的規避上述問題。java

資源與環境

示例代碼:https://github.com/mengyunzhi/springBootSampleCode/tree/master/dynamic-autowiregit

開發環境:java1.8 + spring-boot:2.1.3.RELEASE

需求假設

  • 假設使用本項目的人員爲:中國人、美國人,分別能接受的語言爲中文和英文。
  • 項目運行後,能夠根據當前的訪問人員是國籍來動態顯示:你好hello
  • 有新的需求後,好比:增長德國人並顯示Hallo。增長功能時,不更改核心代碼。
  • 不使用if else

注意:若是你看完需求假設後,毫無觸動,請忽略本文如下內容github

解決方案

解決方案中,咱們涉及了兩種設計模塊,分別爲:策略模式工廠模式
策略模式:通常用於將具體的算法進行抽象及剝離。此項目中,咱們的具體算法是說你好
工廠模式:通常用於根據環境來動態的建立BEAN的狀況下。引項目中,咱們將根據不一樣國家的人,來返回不一樣的說你好這個算法。算法

先給出UML圖:
clipboard.pngspring

SpeakService

SpeakService即爲咱們供其它模塊調用的說話服務,調用其中的SayHello()來完成說你好功能。設計模式

package com.mengyunzhi.demo.dynamicautowire;

/**
 * 你好
 */
public interface SpeakService {
    void sayHello();
}

在其實現類中,咱們注入SayHelloFactory,讓其來返回正確的SayHelloService,最終調用sayHello()來完成目標。ide

package com.mengyunzhi.demo.dynamicautowire;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 你好
 */
@Service
public class SpeakServiceImpl implements SpeakService {
    private final
    SayHelloFactory sayHelloFactory;  // 說話工廠

    @Autowired
    public SpeakServiceImpl(SayHelloFactory sayHelloFactory) {
        this.sayHelloFactory = sayHelloFactory;
    }

    @Override
    public void sayHello() {
        this.sayHelloFactory.getSayHelloService().sayHello();
    }
}

SayHelloFactory

package com.mengyunzhi.demo.dynamicautowire;

/**
 * 說話工廠
 */
public interface SayHelloFactory {

    void setCountryCode(CountryCode countryCode);

    SayHelloService getSayHelloService();
}

在此,咱們增長一個CountryCode表示當前訪問者的國家。其實在獲取訪問者國家時,咱們也能夠調用其它Bean的其它來實現。spring-boot

package com.mengyunzhi.demo.dynamicautowire;

/**
 * 國家代碼
 */
public enum CountryCode {
    CHINA((byte) 0, "中國"),
    USA((byte) 1, "美國");
    private Byte code;
    private String name;

    CountryCode(Byte code, String name) {
        this.code = code;
        this.name = name;
    }

    public Byte getCode() {
        return code;
    }
    public String getName() {
        return name;
    }
}

使用enum來控制範圍,避免Factory在獲取Bean時發生異常。單元測試

package com.mengyunzhi.demo.dynamicautowire;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 說話工廠
 */
@Service
public class SayHelloFactoryImpl implements SayHelloFactory {
    /**
     * BEAN列表
     */
    private final Map<Byte, SayHelloService> servicesByCode = new HashMap<>();
    /**
     * 國家代碼
     */
    private CountryCode countryCode = CountryCode.CHINA;

    @Override
    public void setCountryCode(CountryCode countryCode) {
        this.countryCode = countryCode;
    }

    /**
     * 初始化
     *
     * @param sayHelloServices spring獲取到的因此實現了SpeakService的BEAN
     */
    @Autowired
    public void init(List<SayHelloService> sayHelloServices) {
        for (SayHelloService sayHelloService : sayHelloServices) {
            this.register(sayHelloService.getCode(), sayHelloService);
        }
    }

    /**
     * 註冊Bean
     *
     * @param code         代碼
     * @param sayHelloService BEAN
     */
    private void register(Byte code, SayHelloService sayHelloService) {
        this.servicesByCode.put(code, sayHelloService);
    }

    /**
     * 獲取BEAN
     *
     * @return 對應的SayHelloService BEAN
     */
    @Override
    public SayHelloService getSayHelloService() {
        return this.servicesByCode.get(this.countryCode.getCode());
    }
}

增長Map<Byte, SayHelloService> servicesByCode來存儲對應國家的SayHelloServiceBEAN。增長getSayHelloService()來根據當前國家代碼來返回相應的Bean。學習

SayHelloService

package com.mengyunzhi.demo.dynamicautowire;

/**
 * 說話
 */
public interface SayHelloService {
    void sayHello();

    Byte getCode();
}

sayHello()方法抽離,getCode()以獲取國家代碼。

中國人你好

package com.mengyunzhi.demo.dynamicautowire;

import org.springframework.stereotype.Component;

/**
 * 中國話
 */
@Component
public class SayHelloServiceChineseImpl implements SayHelloService {
    @Override
    public void sayHello() {
        System.out.println("您好");
    }

    @Override
    public Byte getCode() {
        return CountryCode.CHINA.getCode();
    }
}

美國人Hello

package com.mengyunzhi.demo.dynamicautowire;

import org.springframework.stereotype.Component;

/**
 * 美國話
 */
@Component
public class SayHelloServiceEnglishImpl implements SayHelloService {
    @Override
    public void sayHello() {
        System.out.println("hello");
    }

    @Override
    public Byte getCode() {
        return CountryCode.USA.getCode();
    }
}

測試

package com.mengyunzhi.demo.dynamicautowire;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest
@RunWith(SpringRunner.class)
public class SpeakServiceImplTest {
    @Autowired
    SpeakService speakService;
    @Autowired
    SayHelloFactory sayHelloFactory;

    @Test
    public void sayHello() {
        // 默認說你好
        speakService.sayHello();

        // 將國家設置爲美國,再說你好
        sayHelloFactory.setCountryCode(CountryCode.USA);
        speakService.sayHello();

        // 將國家設置爲中國,再說你好
        sayHelloFactory.setCountryCode(CountryCode.CHINA);
        speakService.sayHello();
    }
}
您好
hello
您好

時序圖

clipboard.png

增長德國人

  1. 增長德國人SayHelloServiceGermanyImpl.
  2. CountryCode中,增長德國.
package com.mengyunzhi.demo.dynamicautowire;

import org.springframework.stereotype.Component;

@Component
public class SayHelloServiceGermanyImpl implements SayHelloService {
    @Override
    public void sayHello() {
        System.out.println("Hallo");
    }

    @Override
    public Byte getCode() {
        return CountryCode.GERMANY.getCode();
    }
}
package com.mengyunzhi.demo.dynamicautowire;

/**
 * 國家代碼
 */
public enum CountryCode {
    CHINA((byte) 0, "中國"),
    USA((byte) 1, "美國"),
    GERMANY((byte) 2, "德國");
    private Byte code;
    private String name;

    CountryCode(Byte code, String name) {
        this.code = code;
        this.name = name;
    }

    public Byte getCode() {
        return code;
    }

    public String getName() {
        return name;
    }
}

單元測試

@Test
    public void sayHello1() {
        // 默認說你好
        speakService.sayHello();

        // 將國家設置爲美國,再說你好
        sayHelloFactory.setCountryCode(CountryCode.USA);
        speakService.sayHello();

        // 將國家設置爲德國,再說你好
        sayHelloFactory.setCountryCode(CountryCode.GERMANY);
        speakService.sayHello();

        // 將國家設置爲中國,再說你好
        sayHelloFactory.setCountryCode(CountryCode.CHINA);
        speakService.sayHello();
    }

測試結果以下:

您好
hello
Hallo
您好

總結

在解決問題時,只全部咱們看的不夠遠,多是因爲本身站的不夠高。一樣的問題,困惑我了多日,直到近期系統的學習設計模式angular官方教程Spring 實戰後,結合近期項目變動帶來的新需求,纔在使用設計模式解決此問題上有所啓發。

欲窮千里目,更上一層樓 唐·王之渙·《登鸛雀樓》
相關文章
相關標籤/搜索