天天看點

代碼設計與整潔總結代碼設計與整潔總結

代碼設計與整潔總結

1. 使用Spring Validation 校驗參數

  • 改造前:Controller校驗參數時,會寫非常多的校驗邏輯,且會與正常的業務代碼糅合在一起,造成閱讀代碼的不适感。簡單執行個體如下:
    • import lombok.Data;
        import lombok.ToString;
        import org.apache.commons.lang3.StringUtils;
        import org.springframework.web.bind.annotation.PostMapping;
        import org.springframework.web.bind.annotation.RequestBody;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RestController;
        
        /**
         * @author anyu
         * @date 2021/8/11 12:10
         */
        @RestController
        @RequestMapping
        public class DemoController {
        
            @PostMapping("test")
            public void test(@RequestBody Student student) {
                if (student.getAge() == null || student.getAge() <= 0 ) {
                    throw new RuntimeException("年齡參數不正确");
                }
                if (StringUtils.isNotBlank(student.getName())){
                    throw new RuntimeException("年齡參數不正确");
                }
                System.err.println(student);
            }
        }
        
        @Data
        @ToString
        class Student {
            private Integer age;
            private String name;
        }
                 
  • 改造後:使用Spring Validation 能夠在POJO類中直接進行參數校驗,使校驗代碼與業務代碼分開,職責清晰,整潔度較好,代碼示例如下:
    • import lombok.Data;
        import lombok.ToString;
        import org.springframework.web.bind.annotation.PostMapping;
        import org.springframework.web.bind.annotation.RequestBody;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RestController;
        import javax.validation.Valid;
        import javax.validation.constraints.Min;
        import javax.validation.constraints.NotBlank;
        
        /**
         * @author anyu
         * @date 2021/8/11 12:10
         */
        @RestController
        @RequestMapping
        public class DemoController {
        
            @PostMapping("test")
            public void test(@RequestBody @Valid Student student) {
                System.err.println(student);
            }
        }
        
        @Data
        @ToString
        class Student {
            @Min(value = 1, message = "年齡參數不正确")
            private Integer age;
            @NotBlank(message = "年齡參數不正确")
            private String name;
        }
                 
  • 總結:關于Spring Validation 的使用,在對象前加上 @Valid 注解,然後在請求參數内使用 @Min、@Max、@NotNull、@NotEmpty、@Email等各種注解實作對不同參數的校驗,具體用法可自行學習。

2. 枚舉大有用處

  • 枚舉在我們的工作中經常會遇到。它有很多用處,比如建立單例對象(最佳單例的實作方式)、對狀态值類型的映射、以及與其他的設計混合使用等等;
  1. 使用枚舉建立單例模式并使用:
    • 建立一個RespEntity類:
    • import lombok.Data;
        import java.io.Serializable;
        
        /**
         * @author anyu
         * @date 2021/7/27 10:50
         */
        @Data
        public class RespEntity<T> implements Serializable {
        
            private Integer code;
        
            private String message;
        
            private T data;
        
            public RespEntity(T data) {
                this.code = ResultCode.DefaultSuccess.getCode();
                this.message = ResultCode.DefaultSuccess.getMessage();
                this.data = data;
            }
        
            public RespEntity(ResultCode resultCode, T data) {
                this.code = resultCode.getCode();
                this.message = resultCode.getMessage();
                this.data = data;
            }
        
            public RespEntity(ResultCode resultCode) {
                this.code = resultCode.getCode();
                this.message = resultCode.getMessage();
            }
        
            public RespEntity(Integer code, String message){
                this.code = code;
                this.message = message;
            }
        
            public RespEntity(Integer code, String message, T data) {
                this.code = code;
                this.message = message;
                this.data = data;
            }
        
            public RespEntity() {
            }
        
            public static RespEntity ok() {
                return OkR.INSTANCE.getInstance();
            }
        
            public RespEntity ok(Integer code, String message) {
                return new RespEntity(code, message);
            }
        
            public RespEntity<T> ok(Integer code, String message, T data) {
                return new RespEntity<>(code, message, data);
            }
        
            public RespEntity<T> ok(T data) {
                return new RespEntity<>(ResultCode.DefaultSuccess, data);
            }
        
            public RespEntity fail() {
                return FailR.INSTANCE.getInstance();
            }
        
            public RespEntity fail(String message) {
                return new RespEntity<>(ResultCode.DefaultFail.getCode(),message, "");
            }
        
        
            public enum OkR {
                INSTANCE;
        
                private RespEntity instance;
        
                /**
                 * JVM保證這個方法絕對隻調用一次
                 */
                OkR() {
                    instance = new RespEntity<>(ResultCode.DefaultSuccess,"");
                }
        
                public RespEntity getInstance() {
                    return instance;
                }
            }
        
            public enum FailR {
                INSTANCE;
        
                private RespEntity instance;
        
                /**
                 * JVM保證這個方法絕對隻調用一次
                 */
                FailR() {
                    instance = new RespEntity<>(ResultCode.DefaultFail,"");
                }
        
                public RespEntity getInstance() {
                    return instance;
                }
            }
        }
                 
      裡面的

      OkR

      FailR

      就是使用枚舉建立的單例模式;
    • 使用RespEntity:
      /**
       * @author anyu
       * @date 2021/8/11 12:10
       */
      @RestController
      @RequestMapping
      public class DemoController {
      
          @PostMapping("test")
          public RespEntity test() {
              return RespEntity.ok();
          }
      }
                 
  2. 使用枚舉替代switch的功能:
    • 改造前:
      public static void main(String[] args) {
      	        String status = "N";
      	        String statusDesc;
      	        switch (status) {
      	            case "N":
      	                statusDesc = "New";
      	                break;
      	            case "D":
      	                statusDesc = "Inactive";
      	                break;
      	            case "O":
      	                statusDesc = "Active";
      	                break;
      	            case "S":
      	                statusDesc = "Suspended";
      	                break;
      	            case "T":
      	                statusDesc = "Terminated";
      	                break;
      	            case "C":
      	                statusDesc = "Closed";
      	                break;
      	            default:
      	                statusDesc = "狀态未知";
      	                break;
      	        }
      	        System.err.println(statusDesc);
      	    }
                 
    • 改造後:
      1. 定義枚舉類:
        /**
        	 * @author anyu
        	 * @date 2021/8/11 10:16
        	 */
        	public enum Status {
        	
        	    N("New"),
        	    D("Inactive"),
        	    O("Active"),
        	    S("Suspended"),
        	    T("Terminated"),
        	    C("Closed"),
        	    Unknown("Unknown");
        	
        	    private String value;
        	
        	    public String getValue() {
        	        return value;
        	    }
        	
        	    Status(String value) {
        	        this.value = value;
        	    }
        	
        	    public static Status forName(String status) {
        	        if (status == null) {
        	            return Status.Unknown;
        	        }
        	        String statusTrim = status.trim();
        	        for (Status pc : values()) {
        	            if (pc.toString().equalsIgnoreCase(statusTrim)) {
        	                return pc;
        	            }
        	        }
        	        return Status.Unknown;
        	    }
        	}
                   
      2. 實作前面switch功能:
        public static void main(String[] args) {
            String status = "N";
            Status sellerStatus = Status.forName(status);
            System.err.println(sellerStatus.getValue());
        }
                   
        代碼在很大程度上被簡潔了。
  3. 使用枚舉的其他可行性:
    • 我們可以去想一下枚舉具有哪些特點,然後能夠發揮它優勢的地方都能夠使用枚舉;
    • 它的優點有:
      1. 隻會執行個體化一次;性能好;
      2. 見名知義,可讀性好;
      3. 橫向擴充能力好,可以通過一個元素關聯其他元素;
      4. 減少傳遞參數錯誤,限定類型;
      5. 不可被更改;
    結合它的優點去考慮使用場景;比如它的一個優點是減少傳遞參數錯誤,我們利用這個優點可以将一些狀态值、類型值使用枚舉來表示,以避免一些參數寫錯,且可讀性更好;

3. Stream流的簡化資料處理

  • 相信同學們經常會使用集合,然後對其中的集合進行操作,比如進行過濾,進行排序,擷取第一條資料,轉換成另外一種集合,往往會寫非常多的代碼,可是當JDK 8 的Stream出世之後,你能想到的一些處理操作,往往大部分都能靠Stream流解決,你以前可能會寫很多代碼來進行處理,優雅的Stream隻需要一行語句即可完成你想要達成的效果!
  • 部落客介紹文章,内有Stream流使用的詳細說明:Java 1.8 函數式程式設計詳解
  • 簡單示例:
    public static void main(String[] args) {
        List<Integer> list = Lists.newArrayList(3, 2, 1, 5, 4, 6, 7);
        // 過濾出不等于2、不等于3 的且按照升序排序的結果
        List<Integer> result = list.stream().filter(x -> x != 2 && x != 3).sorted(Comparator.comparing(Integer::intValue)).collect(Collectors.toList());
        System.err.println(result);
    }
               
  • 列印結果如下:

    [1, 4, 5, 6, 7]

4. 使用Optional類消滅空指針異常(NullPointerException)

  • 從 Java 8 引入的一個很有趣的特性是 Optional 類。Optional 類主要解決的問題是臭名昭著的空指針異常(NullPointerException)
  • 使用Optional 前:
    @Data
    @ToString
    static class Student {
        @Min(value = 1, message = "年齡參數不正确")
        private Integer age;
        @NotBlank(message = "年齡參數不正确")
        private String name;
    }
    
    
    public static void main(String[] args) {
        Student student = null;
        System.err.println(student.getName());
    }
               
  • 使用Optional 後:
    public static void main(String[] args) {
        Student student = null;
        Optional.ofNullable(student).ifPresent(x -> System.err.println(x.getName()));
    }
               
使用Optional後,隻有當student不為Null的時候,才會進行構造Optionl類,才會去執行

列印操作

,是以它不會抛出空指針異常;更多用法,請自行學習。

5. 使用遞歸簡化代碼

  • 遞歸常用于一些有規律的或周遊的方法中;一些周遊操作每次的執行流程都是一樣的,隻是參數發生了變化,那麼我們可以使用遞歸的方式來簡化代碼開發;比如檔案周遊,每次執行的操作都是一緻的,但是我們不知道檔案的名稱是什麼,數量有多少,通過遞歸的方式就能很好的解決問題;
  • 遞歸示例檔案掃描:
    import java.io.File;
    
    /**
     * csdn: anyu
     */
    public class Demo1 {  
        public static void main(String[] args) {  
            File dir=new File("D:\\CCC");//浏覽F盤a檔案夾下的所有内容  
            listFile(dir,"");  
        }  
    public static void listFile(File dir,String spance)  
        {  
            File[] files=dir.listFiles();   //列出所有的子檔案  
            for(File file :files)  
            {  
                if(file.isFile())//如果是檔案,則輸出檔案名字  
                {  
                    System.out.println(spance+file.getName());  
                }else if(file.isDirectory())//如果是檔案夾,則輸出檔案夾的名字,并遞歸周遊該檔案夾  
                {   
                    System.out.println(spance+file.getName()); 
                    listFile(file,"|--"+spance);//遞歸周遊  
                }  
            }  
        }
    }
               
    檔案周遊我們預先不知道有哪些檔案,不能夠直接去查詢。使用遞歸既可以依次掃描,又可以滿足每次掃描要執行的過程,這種有規律的操作使用遞歸方式可以極大地簡潔代碼,秀出你的style!
  • 程式設計沒有銀彈,隻有精準下藥,方可藥到病除。在什麼場景下使用遞歸比較合适呢?我們來看看它的優缺點:
    • 優點:
    1. 代碼簡潔
    2. 易于了解
      如在樹的前/中/後序周遊中,遞歸的實作明顯比循環簡單。
    • 缺點:
    1. 時間和空間的消耗比較大
      遞歸由于是函數調用自身,而函數的調用時消耗時間和空間的,每一次函數調用,都需要在記憶體棧中配置設定空間以儲存參數,傳回值和臨時變量,而往棧中壓入和彈出資料也都需要時間,是以降低了效率。
    2. 重複計算
      遞歸中又很多計算都是重複的,遞歸的本質時把一個問題分解成兩個或多個小 問題,多個小問題存在重疊的部分,即存在重複計算,如斐波那契數列的遞歸實作。
    3. 調用棧溢出
      遞歸可能時調用棧溢出,每次調用時都會在記憶體棧中配置設定空間,而棧空間的容量是有限的,當調用的次數太多,就可能會超出棧的容量,進而造成調用棧溢出。
理清遞歸的使用方式、優缺點、使用場景,以及跟循環的差別,是合理使用遞歸的要點;

6. 泛型

  • 泛型有三種使用方式,分别為:泛型類、泛型接口、泛型方法
  • 在編譯之後程式會采取去泛型化的措施。也就是說Java中的泛型,隻在編譯階段有效。在編譯過程中,正确檢驗泛型結果後,會将泛型的相關資訊擦出,并且在對象進入和離開方法的邊界處添加類型檢查和類型轉換的方法。也就是說,泛型資訊不會進入到運作時階段。
    對此總結成一句話:泛型類型在邏輯上看以看成是多個不同的類型,實際上都是相同的基本類型。
  • 最常用的泛型方式:
    • 使用泛型類來自定義傳回對象,參見我們

      章節2

      的建立RespEntity對象
    • 建立一個xml解析工具類:
      import java.io.IOException;
      	import java.io.StringWriter;
      	
      	import com.fasterxml.jackson.databind.DeserializationFeature;
      	import com.fasterxml.jackson.databind.MapperFeature;
      	import com.fasterxml.jackson.databind.SerializationFeature;
      	import com.fasterxml.jackson.dataformat.xml.XmlMapper;
      	
      	import javax.xml.bind.JAXBContext;
      	import javax.xml.bind.Marshaller;
      	
      	public class XmlUtils {
      	    private static XmlMapper xmlMapper = new XmlMapper();
      	
      	    private XmlUtils() {
      	        throw new IllegalStateException("Utility class");
      	    }
      	
      	    static {
      	        // 忽略空Bean轉json的錯誤
      	        xmlMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
      	        // 忽略 在json字元串中存在,但是在java對象中不存在對應屬性的情況。防止錯誤
      	        xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
      	        // 大小寫脫敏
      	        xmlMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
      	    }
      	    public static <T> T string2Obj(String xmlSrc, Class<T> clazz) {
      	        if (null == xmlSrc || clazz == null) {
      	            return null;
      	        }
      	        try {
      	            return xmlMapper.readValue(xmlSrc, clazz);
      	        } catch (IOException e) {
      	            throw new RuntimeException(e);
      	        }
      	    }
      	
      	    public static String obj2String(Object o) {
      	        try {
      	            JAXBContext context = JAXBContext.newInstance(o.getClass());
      	            Marshaller marshaller = context.createMarshaller();
      	            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
      	            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
      	            StringWriter writer = new StringWriter();
      	            marshaller.marshal(o, writer);
      	            return writer.toString();
      	        } catch (Exception e) {
      	            throw new RuntimeException(e);
      	        }
      	    }
      	}
                 
      string2Obj()這個方法使用了泛型,調用此方法會傳回指定類型的對象;
  • 泛型的優缺點:
    1. 泛型簡單易用
    2. 類型安全
      泛型的主要目标是實作java的類型安全。 泛型可以使編譯器知道一個對象的限定類型是什麼,這樣編譯器就可以在一個高的程度上驗證這個類型
    3. 消除了強制類型轉換 使得代碼可讀性好,減少了很多出錯的機會
    4. 簡潔,提高重用率。
      泛型的好處是在編譯的時候檢查類型安全,并且所有的強制轉換都是自動和隐式的,提高代碼的重用率。
    我們可以根據泛型的特點去思考它的應用場景;

7. 使用ThreadLocal簡潔代碼

  • 多線程通路同一個共享變量的時候容易出現并發問題,特别是多個線程對一個變量進行寫入的時候,為了保證線程安全,一般使用者在通路共享變量的時候需要進行額外的同步措施才能保證線程安全性。ThreadLocal是除了加鎖這種同步方式之外的一種保證一種規避多線程通路出現線程不安全的方法,當我們在建立一個變量後,如果每個線程對其進行通路的時候通路的都是線程自己的變量這樣就不會存線上程不安全問題。
  • ThreadLocal是JDK包提供的,它提供線程本地變量,如果建立一樂ThreadLocal變量,那麼通路這個變量的每個線程都會有這個變量的一個副本,在實際多線程操作的時候,操作的是自己本地記憶體中的變量,進而規避了線程安全問題,如下圖所示:
    • 代碼設計與整潔總結代碼設計與整潔總結
  • 場景模拟:我們寫了一套通用代碼,對接了一家公司的CRM系統,但是臨時要求再增加一個賬号,不同資料流向同系統的兩個賬号中;(每個賬号資料獨立,對接代碼一樣,通過賬号獨立來将兩種資料隔離)使用ThreadLocal的話,可以在不改變代碼邏輯的情況下,增加一個判斷:如果是A平台資料,則使用A賬号;如果是B平台資料則使用B平台的AppKey、AppSecret;而後續的通用接口直接從ThreadLocal中讀取賬号資訊,就可以将資料傳送到對應的賬号平台;
  • 代碼設計與整潔總結代碼設計與整潔總結
    它就像一個中間者,平台不直接從左側拿取賬号,而直接從ThreadLocal中擷取,這樣能夠易于擴充、解耦合、且極大的減少了重複代碼,提高了程式設計效率
  • ThreadLocal 相關知識:
    • 工具類:
      import org.apache.commons.collections4.MapUtils;
      import org.apache.commons.lang3.StringUtils;
      
      import java.util.HashMap;
      import java.util.Map;
      import java.util.Optional;
      import java.util.concurrent.ConcurrentHashMap;
      import java.util.stream.Collectors;
      
      
       /**
       * @author 暗餘
       * @date 2021/7/17 15:44
       */
      @SuppressWarnings("unused")
      public final class ThreadLocalUtils {
      
          private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL =
                  ThreadLocal.withInitial(() -> new ConcurrentHashMap<>(16));
      
          /**
           * 擷取到ThreadLocal中值
           *
           * @return ThreadLocal存儲的是Map
           */
          public static Map<String, Object> getThreadLocal() {
              return THREAD_LOCAL.get();
          }
      
          /**
           * 從ThreadLocal中的Map擷取值
           *
           * @param key Map中的key
           * @param <T> Map中的value的類型
           * @return Map中的value值 可能為空
           */
          public static <T> T get(String key) {
              return get(key, null);
          }
      
          /**
           * 從ThreadLocal中的Map擷取值
           *
           * @param key          Map中的key
           * @param defaultValue Map中的value的為null 是 的預設值
           * @param <T>          Map中的value的類型
           * @return Map中的value值 可能為空
           */
          @SuppressWarnings("unchecked")
          public static <T> T get(String key, T defaultValue) {
              Map<String, Object> map = THREAD_LOCAL.get();
              if (MapUtils.isEmpty(map)) {
                  return null;
              }
              return (T) Optional.ofNullable(map.get(key)).orElse(defaultValue);
          }
      
          /**
           * ThreadLocal中的Map設定值
           *
           * @param key   Map中的key
           * @param value Map中的value
           */
          public static void set(String key, Object value) {
              Map<String, Object> map = THREAD_LOCAL.get();
              map.put(key, value);
          }
      
          /**
           * ThreadLocal中的Map 添加Map
           *
           * @param keyValueMap 參數map
           */
          public static void set(Map<String, Object> keyValueMap) {
              Map<String, Object> map = THREAD_LOCAL.get();
              map.putAll(keyValueMap);
          }
      
          /**
           * 删除ThreadLocal中的Map 中的value
           *
           * @param key Map中的key
           */
          public static void delete(String key) {
              Map<String, Object> map = THREAD_LOCAL.get();
              if (MapUtils.isEmpty(map)) {
                  return;
              }
              map.remove(key);
          }
      
          /**
           * 删除ThreadLocal中的Map
           */
          public static void remove() {
              THREAD_LOCAL.remove();
          }
      
          /**
           * 從ThreadLocal中的Map擷取值 根據可key的字首
           *
           * @param prefix key 的字首
           * @param <T>    Map中的value的類型
           * @return 符合條件的Map
           */
          @SuppressWarnings("unchecked")
          public static <T> Map<String, T> fetchVarsByPrefix(String prefix) {
              Map<String, T> vars = new HashMap<>(16);
              if (StringUtils.isBlank(prefix)) {
                  return vars;
              }
              Map<String, Object> map = THREAD_LOCAL.get();
              if (MapUtils.isEmpty(map)) {
                  return vars;
              }
              return map.entrySet().stream().filter(test -> test.getKey().startsWith(prefix))
                      .collect(Collectors.toMap(Map.Entry::getKey, time -> (T) time.getValue()));
          }
      
          /**
           * 删除ThreadLocal中的Map 中的Value  按 Map中的Key的字首
           *
           * @param prefix Map中的Key的字首
           */
          public static void deleteVarsByPrefix(String prefix) {
              if (StringUtils.isBlank(prefix)) {
                  return;
              }
              Map<String, Object> map = THREAD_LOCAL.get();
              if (MapUtils.isEmpty(map)) {
                  return;
              }
              map.keySet().stream().filter(o -> o.startsWith(prefix)).collect(Collectors.toSet()).forEach(map::remove);
          }
      }
                 
    • 存入值:

      ThreadLocalUtils.set($key, $value);

    • 取值:

      ThreadLocalUtils.get($key)

    • 删除值:

      ThreadLocalUtils.remove();

    一定要記得用完後要remove() 否則會發生記憶體洩漏;

8. 結合工廠模式、Spring特性、枚舉 優化代碼

  • 特性說明:
    • 工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬于建立型模式,它提供了一種建立對象的最佳方式。在工廠模式中,我們在建立對象時不會對用戶端暴露建立邏輯,并且是通過使用一個共同的接口來指向新建立的對象。
    • Spring : 在Spring中,bean可以被定義為兩種模式:prototype(多例)和singleton(單例)
      • singleton(單例):隻有一個共享的執行個體存在,所有對這個bean的請求都會傳回這個唯一的執行個體。
      • prototype(多例):對這個bean的每次請求都會建立一個新的bean執行個體,類似于new。
      Spring bean 預設是單例模式,我們在使用過程中也是用的它的單列模式
    • 枚舉,枚舉的好處上面已有講解,可以往上翻。
    • 既然它們都有這麼多好處,我們在寫代碼中能否結合使用呢?将他們的優點結合起來,作為一種更上一層的設計模闆?當然有!請往下看
  • 實戰:
    • 需求如下:我有一個流程需求,在第一步我要做xxx事情,第二步我要做xxx事情,第三步我要做xxx事情,如何設計來讓我們既達到要求又能擁有很好的代碼設計呢?
    • 需求分析:
      1. 首先我們可以定義一個枚舉類,這個枚舉類中定義了這個流程的所有步驟;
      2. 其次我們可以定義一個工廠類,這個工廠類可以為我們執行個體化一個處理目前步驟的需求,通過傳入枚舉類,然後根據目前流程來對應建立相應的需求
      3. 最後我們可以為每一個流程步驟建立一個處理類,這個類能夠執行目前流程步驟需要做的事情;
      前兩步我們利用了枚舉和工廠能夠建立對應的處理類。第三步我們做任務處理的時候,就可以結合Spring來進行,如果沒有引進Spring 會導緻類膨脹,每個線程處理一次請求就要執行個體化一個類,顯然性能消耗太大。我們利用Spring的預設單例可以重複使用一個類,且與其他被Spring 管理的工具類如Mybatis、MQ 等無縫銜接,更加易于使用。
    • 代碼示範:
      • 枚舉:
        /**
         * @author 暗餘
         * @date 2021/7/27 15:38
         */
        public enum ProcessStage {
        
        /**
         * 流程1
         */
        ProcessOne("serviceOne"),
        
        /**
         * 流程2
         */
        ProcessTwo("serviceTwo"),
        
        /**
         * 流程3
         */
        ProcessThree("serviceThree"),
        
        /**
         * 流程4
         */
        ProcessFour("serviceFour"),
        
        /**
         * 流程5
         */
        AccountCreate("serviceFive");
        
        	private String serviceName;
        
        	ProcessStage(String serviceName) {
        		this.serviceName = serviceName;
        	}
        
        	public String getServiceName() {
        		return serviceName;
        	}
        }
                   
      • 工廠類:
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Component;
        
        import java.util.Map;
        import java.util.concurrent.ConcurrentHashMap;
        
        /**
         * @author 暗餘
         * @date 2021-08-10
         */
        @Component
        public class ServiceImplFactory {
        
            @Autowired
            private Map<String, IDemoService> serviceMap = new ConcurrentHashMap<>(ProcessStage.values().length);
        
            public IDemoService getService(ProcessStage stage) {
                return serviceMap.get(stage.getServiceName());
            }
        
        	/**
        	 * 在别處就可以類似這樣直接先從工廠類中get出Spring的執行個體,然後調用它的process方法即可。
        	 */
        	public void test(){
        		Object process = getService(ProcessStage.ProcessOne).process();
        		System.err.println(process.toString());
        	}
        }
                   
      • 處理類接口
        public interface IDemoService {
        
            public String process();
        
        }
                   
      • 處理實作類1
        import org.springframework.stereotype.Service;
        /**
         * @author 暗餘
         * @date 2021/9/23 11:07
         */
        @Service("serviceOne")
        public class DemoServiceOne implements IDemoService{
        
            @Override
            public String process() {
                return "serviceOne";
            }
        }
                   
      • 處理實作類2
        import org.springframework.stereotype.Service;
        
        /**
         * @author 暗餘
         * @date 2021/9/23 11:07
         */
        @Service("serviceTwo")
        public class DemoServiceTwo implements IDemoService {
            @Override
            public String process() {
                return "serviceTwo";
            }
        }