演示导入 Excel 文件不同 Sheet ,以 Spring 中的 Service 注入 Dao 为例,
常规
if/else 或者 switch 判断以后对数据操作,好点儿每个不同条件抽象出一个方法,逻辑也会清晰些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Slf4j @Service public class ExcelImportServiceImpl implements ExcelImportService {
@Autowired private Sheet1Dao sheet1Dao; @Autowired private Sheet2Dao sheet2Dao; @Autowired private Sheet3Dao sheet3Dao; @Overide public void importData(SheetData sheetData) { String sheetName = sheetData.getSheetName(); if ("sheet1".equals(sheetName)) { Data data = sheetData.get(); sheet1Dao.insert(data); } else if ("sheet2".equals(sheetName)) { Data data = sheetData.get(); sheet2Dao.insert(data); } else if ("sheet3".equals(sheetName)) { Data data = sheetData.get(); sheet3Dao.insert(data); } } }
|
表驱动
要构建表,上面的问题就在于 @Autowired 注入的几个 Dao 了,这在 枚举 & 表驱动 中是不曾出现的问题
要状态对应上不同的动作,构建相应的表(Map),任务就完成了。
首先拆分不同的 Service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| public interface ExcelImportService { void importData(SheetData sheetData); String getSheetName(); }
@Getter @Service("sheet1ImportService") public class Sheet1ImportService implements ExcelImportService { private String sheetName = "sheet1"; @Autowired private Sheet1Dao sheet1Dao; @Overide public void importData(SheetData sheetData) { Data data = sheetData.get(); sheet1Dao.insert(data); } }
@Getter @Service("sheet2ImportService") public class Sheet2ImportService implements ExcelImportService { private String sheetName = "sheet2"; @Autowired private Sheet2Dao sheet2Dao; @Overide public void importData(SheetData sheetData) { Data data = sheetData.get(); sheet2Dao.insert(data); } }
@Getter @Service("sheet3ImportService") public class Sheet3ImportService implements ExcelImportService { private String sheetName = "sheet3"; @Autowired private Sheet3Dao sheet3Dao; @Overide public void importData(SheetData sheetData) { Data data = sheetData.get(); sheet3Dao.insert(data); } }
|
现在就把三个 sheet 的导入拆分开了,它们都实现了 ExcelImportService,它们拥有不同的 sheetName 作为自我标识
Map 的 k/v 似乎也就出来了, key (sheetName),value (具体的 sheetXImportService 的 bean)
怎么拿到 bean 呢?直接从 context 里 getBean() ? 可以,但还是要硬编码 sheetXImportService 的 beanName,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class ExcelImportContext { private static final Map<String, ExcelImportService> SHEETNAME_TO_IMPORT_BEAN = new HashMap(); static { SHEETNAME_TO_IMPORT_BEAN.put("sheet1", Context.getBean("sheet1ImportService")); SHEETNAME_TO_IMPORT_BEAN.put("sheet2", Context.getBean("sheet2ImportService")); SHEETNAME_TO_IMPORT_BEAN.put("sheet3", Context.getBean("sheet3ImportService")); } public static void importData(SheetData sheetData) { String sheetName = sheetData.getSheetName(); Optional<ExcelImportService> importServiceOpt = Optional.ofNullable(SHEETNAME_TO_IMPORT_BEAN.get(sheetName)); if (importServiceOpt.isPresent()) { importServiceOpt.get().importData(sheetData.getData()); } } }
|
上面的代码 ExcelImportContext 中需要硬编码 beanName 不优雅,易出错。
终极进化版本
主要原理 @Autowired 可以注入一个 interface 的所有实现:
1 2 3 4 5 6 7 8 9
| @Autowried private Map<String, ExcelImportContext> beanNameToExcelImportBean;
{ "sheet1ImportService": 名为 shee1ImportService 的 bean, "sheet2ImportService": 名为 shee2ImportService 的 bean, "sheet3ImportService": 名为 shee3ImportService 的 bean }
|
但是这个 key 不是我需要的咋个办?还记得在 ExcelImportService 中提供了 getSheetName() 方法么,让各个实现返回其对应的 sheetName,那么就可以在注入的时候把它提取出来了。
各个实现中的 getSheeName() 用 lombok 的 @Getter 注解生成了的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| @Component public class ExcelImportContext { private static final Map<String, ExcelImportService> SHEETNAME_TO_IMPORT_BEAN = new HashMap();
@Autowired public ExcelImportContext(Map<String, ExcelImportService> map) { for (Map.Entry<String, ExcelImportService> entry : map) { String beanName = entry.getKey(); ExcelImportService bean = Eentry.getValue(); String sheetName = bean.getSheetName(); SHEETNAME_TO_IMPORT_BEAN.put(sheetName, bean); } } public void importData(SheetData sheetData) { String sheetName = sheetData.getSheetName();
Optional<ExcelImportService> importServiceOpt = Optional.ofNullable(SHEETNAME_TO_IMPORT_BEAN.get(sheetName)); if (importServiceOpt.isPresent()) { importServiceOpt.get().importData(sheetData.getData()); } } }
|
现在差不多了, ExcelImportContext 中没有硬编码的 sheetName 和 beanName 之类的了。
附:@Autowired 还可以注入 List<接口> (Set<接口>, 或者 接口[])
在这个例子中,使用 List<> (Set<>, 接口[]) 的方式更合适
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Component public class ExcelImportContext { private static final Map<String, ExcelImportService> SHEETNAME_TO_IMPORT_BEAN = new HashMap();
@Autowired public ExcelImportContext(List<ExcelImportService> importServices) { for (ExcelImportService bean : importServices) { String sheetName = bean.getSheetName(); SHEETNAME_TO_IMPORT_BEAN.put(sheetName, bean); } } public void importData(SheetData sheetData) { } String sheetName = sheetData.getSheetName();
Optional<ExcelImportService> importServiceOpt = Optional.ofNullable(SHEETNAME_TO_IMPORT_BEAN.get(sheetName)); if (importServiceOpt.isPresent()) { importServiceOpt.get().importData(sheetData.getData()); } }
|
总结
- 拆分实现,实现中带有自己的标识(sheetName),提供得到标识的方法
@Autowried 构造注入接口的所有实现,提取想要的信息作为 key,bean 作为 value 构建表
- 多态调用具体实现