Spring & 表驱动 | LIXI.FUN
0%

Spring & 表驱动

演示导入 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 {

// 自己标明自己是 sheet1
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 {

// 自己标明自己是 sheet2
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 {

// 自己标明自己是 sheet3
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;

// Map 的内容是这个,bean 拿到了
{
"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 为了能注入
@Component
public class ExcelImportContext {

private static final Map<String, ExcelImportService>
SHEETNAME_TO_IMPORT_BEAN = new HashMap();

/**
* 使用构造方法注入提取其中的 sheetName
*/
@Autowired
public ExcelImportContext(Map<String, ExcelImportService> map) {
for (Map.Entry<String, ExcelImportService> entry : map) {
// beanName,对我来说是没用的,写在这里只是说明一下
String beanName = entry.getKey();

ExcelImportService bean = Eentry.getValue();
// 提取 sheetName
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 中没有硬编码的 sheetNamebeanName 之类的了。

附:@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 为了能注入
@Component
public class ExcelImportContext {

private static final Map<String, ExcelImportService>
SHEETNAME_TO_IMPORT_BEAN = new HashMap();

/**
* 使用构造方法注入提取其中的 sheetName
* `@Autowired` 还可以注入 List<接口> (Set<接口>, 或者 接口[])
*/
@Autowired
public ExcelImportContext(List<ExcelImportService> importServices) {
for (ExcelImportService bean : importServices) {
// 提取 sheetName
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 构建表
  • 多态调用具体实现
觉得有收获就鼓励下作者吧