1、前言
??一般我們使用枚舉都是用來定義一些常量。比如我們需要一個表示訂單類(pc訂單、手機訂單)的常量,那我們就可以使用枚舉來實現,如下:
@AllArgsConstructor
public enum OrderTypeEnum{PC("PC", "電腦端"),PHONE("PHONE", "手機端");private String name;private String value;}
??接著再寫一下根據name查找對應枚舉的方法,枚舉就算完成了。但比如我們的業務場景是這樣的:我們有一個OrderTypeService接口及其實現類。
public interface OrderTypeService {String processPc(String order);String processPhone(String order);
}
@Service
public class OrderTypeServiceImpl implements OrderTypeService {@Overridepublic String processPc(String order) {//具體的處理pc訂單的業務邏輯System.out.println("開始處理pc訂單:" + order);return null;}@Overridepublic String processPhone(String order) {//具體的處理phone訂單的業務邏輯System.out.println("開始處理phone訂單:" + order);return null;}
}
??根據接口的方法名我們一眼就知道,processPc是用來處理PC訂單的;processPhone是用來處理PHONE訂單的。然后再實際調用這兩個方法的地方就需要我們進行if-else的判斷。這里的if-else判斷的缺點是:代碼擴展性很差,如果以后又增加了其它訂單類型,我們還需要改這里的if-else邏輯。
if (OrderTypeEnum.PC.equals(orderTypeEnum)) {orderTypeService.processPc("order");} else if (OrderTypeEnum.PHONE.equals(orderTypeEnum)) {orderTypeService.processPhone("order");}
2、思考
??針對以上if-else判斷存在的問題,那我們能不能把調用各自業務邏輯的代碼挪到枚舉里呢?肯定是可以的,本期我們就來實現一下。
2.1、通過@PostConstruct注解
在枚舉內定義BeanInjector,再通過PostConstruct注解的方法給枚舉的orderTypeService屬性注入bean。
@AllArgsConstructor
public enum OrderTypeEnum {PC("PC", "電腦端") {@Overridepublic void handle(String order) {orderTypeService.processPc(order);}},PHONE("PHONE", "手機端") {@Overridepublic void handle(String order) {orderTypeService.processPhone(order);}};private String name;private String value;protected static OrderTypeService orderTypeService;public abstract void handle(String order);@Componentpublic static class BeanInjector {@Autowiredprivate OrderTypeService orderTypeService;@PostConstructpublic void postConstruct() {OrderTypeEnum.orderTypeService = this.orderTypeService;}}
}
調用代碼:
OrderTypeEnum pcOrderType = OrderTypeEnum.PC;pcOrderType.handle("1");
輸出結果:
開始處理pc訂單:1
2.2、在枚舉內實現ApplicationContextAware回調接口,獲取到ApplicationContext,再從context里獲取我們需要的bean
@AllArgsConstructor
public enum OrderTypeEnum {PC("PC", "電腦端") {@Overridepublic void handle(String order) {OrderTypeService orderTypeService = OrderTypeEnum.ApplicationContextProvider.getApplicationContext().getBean(OrderTypeService.class);orderTypeService.processPc(order);}},PHONE("PHONE", "手機端") {@Overridepublic void handle(String order) {OrderTypeService orderTypeService = OrderTypeEnum.ApplicationContextProvider.getApplicationContext().getBean(OrderTypeService.class);orderTypeService.processPhone(order);}};private String name;private String value;public abstract void handle(String order);@Componentpublic static class ApplicationContextProvider implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {context = applicationContext;}public static ApplicationContext getApplicationContext() {return context;}}
}
調用代碼同2.1
輸出結果同2.1
2.3、還是需要實現ApplicationContextAware接口,但這次不是直接使用ApplicationContext,而是把ApplicationContext里的bean值直接注入到我們的枚舉字段中。
public enum OrderTypeEnum {PC("PC", "電腦端") {@Overridepublic void handle(String order) {orderTypeService.processPc(order);}},PHONE("PHONE", "手機端") {@Overridepublic void handle(String order) {orderTypeService.processPhone(order);}};private String name;private String value;OrderTypeEnum(String name, String value) {this.name = name;this.value = value;}public abstract void handle(String order);@AutowiredOrderTypeService orderTypeService;
}
@Component
public class OrderTypeEnumInjector implements ApplicationContextAware {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {for (OrderTypeEnum orderTypeEnum : OrderTypeEnum.values()) {applicationContext.getAutowireCapableBeanFactory().autowireBean(orderTypeEnum);}}
}
調用代碼同2.1
輸出結果同2.1
??這里重點是使用applicationContext.getAutowireCapableBeanFactory().autowireBean。它可以把枚舉中使用了@Autowired注解的字段和applicationContext中的bean根據類型和名稱做匹配,匹配到之后就會把bean注入到該字段中。從而枚舉中的orderTypeService就有值了。
3、延申
??applicationContext.getAutowireCapableBeanFactory().autowireBean能否給普通的類(沒有被spring管理)的字段注入bean呢?答案也是可以的,這里最好把這個普通對象定義成單例的,這樣我們給字段注入一次bean就可以一直使用了,否則每次new 的時候還需要重新注入bean。
public class OrderTypeManager {private OrderTypeManager(){}private static OrderTypeManager orderTypeManager = null;public synchronized static OrderTypeManager getInstance(){if(orderTypeManager == null){orderTypeManager = new OrderTypeManager();}return orderTypeManager;}@Autowiredprivate OrderTypeService orderTypeService;public void test(){orderTypeService.processPhone("2");}
}
??如上定義了一個單例的OrderTypeManager,但是OrderTypeManager沒有被spring所管理,那我們怎么把OrderTypeService這個bean注入到orderTypeService字段里呢?
@Component
public class OrderTypeEnumInjector implements ApplicationContextAware {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {for (OrderTypeEnum orderTypeEnum : OrderTypeEnum.values()) {applicationContext.getAutowireCapableBeanFactory().autowireBean(orderTypeEnum);}OrderTypeManager orderTypeManager = OrderTypeManager.getInstance();applicationContext.getAutowireCapableBeanFactory().autowireBean(orderTypeManager);}
}
調用代碼:
OrderTypeManager orderTypeManager = OrderTypeManager.getInstance();orderTypeManager.test();
輸出結果:
開始處理phone訂單:2