服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 使用spring动态获取接口的不同实现类

使用spring动态获取接口的不同实现类

2022-07-31 14:08CarinaYang Java教程

这篇文章主要介绍了使用spring动态获取接口的不同实现类,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

spring动态获取接口的不同实现类

最近做项目,有个需求是和外部对接,从接口获取新闻数据,把数据和缓存中的数据对比,多了的添加到数据库,少了的删掉。

接口有两个,一开始我是在业务类里写了两个方法,代码太长,简单说就是两个部分:

?
1
2
3
4
5
6
7
8
9
public Object saveANews() {
    //1、获取A接口新闻列表
    //2、和缓存对比,存数据到数据库
}
 
public Object saveBNews() {
    //1、获取B新闻列表
    //2、和缓存对比,存数据到数据库
}

写完后我发现,因为操作的是数据库的同一张表,2的部分代码完全一模一样,只有1的部分不同,而1的部分其实就只有一行代码。。。

这必须得复用啊,而且是一个业务,也没必要分别用两个方法,于是我改成了这样:

?
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
62
63
64
65
66
67
68
69
70
//业务接口实现方法
public Object saveNews(NewsUtilService service) {
    //1、获取接口新闻列表
    List<NewsVO> list = service.queryNews();
    //2、和缓存对比,存数据到数据库
}
 
//定义新闻数据接口
public interface NewsUtilService {
    List<NewsVO> queryNews();
}
 
//接口的两个实现类
@Service
public class ANewsDataServiceImpl implements NewsUtilService {
    @Autowired
    private NewsDataMapper newsDataMapper;
 
    @Override
    public List<NewsVO> queryNews(){
        //对接数据
    }
}
 
@Service
public class BNewsDataServiceImpl implements NewsUtilService {
    @Override
    public List<NewsVO> queryNews(){
        //对接数据
    }
}
 
//定义工厂类
@Service
public class NewsUtilServiceFactory {  
    /**
     *
     * Method Name:  getNewsUtilService
     * @param source
     * @return
     */
    public NewsUtilService getNewsUtilService(String source){
        switch(source){
            case "a":
                return new ANewsDataServiceImpl();
            case "b":
                return new BNewsDataServiceImpl();
            default:
                return null;
        }
    }
}
 
//控制层调用
@RestController
public class NewsDataController {
    @Resource
    private NewsDataService newsDataService;
    
    @Resource
    private NewsUtilServiceFactory factory;
 
    public Object getNewsData(){
        String[] sources = {"a","b"};
            for (int i = 0; i < sources.length; i++) {
                NewsUtilService newsUtilService = factory.getNewsUtilService(sources[i]);
                newsDataService.saveNews(newsUtilService);
            }
    }
}

本来以为这就大工告成了,谁知道运行后控制台居然报错了:

使用spring动态获取接口的不同实现类

经过一步步调试,总算发现了是什么问题:

使用spring动态获取接口的不同实现类

其中一个实现类中注入的Mapper没有实例化,是null。

一开始我还以为是构造函数调用和注入的顺序问题,查了半天才明白不是,问题在这里:

使用spring动态获取接口的不同实现类

使用new关键字实例化的对象不是被spring创建的,不归spring管,所以A类实现类中Mapper注入的注解根本不生效!

但是因为业务需要,那个mapper又需要用到,怎么办呢?

当时想到了两种解决办法

1、在接口的方法参数里加入mapper,把mapper作为参数传进去,但这实在太奇怪了,先不说B类实现类根本用不到mapper,而且一个接口定义出来后根本不管它的实现类吧,因为实现类的问题去改接口,,,似乎有点非呀。

于是决定用第二种,修改工厂类变成如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义工厂类
@Service
public class NewsUtilServiceFactory {  
    @Autowired
    private ANewsDataServiceImpl aNewsDataServiceImpl;
    @Autowired
    private BNewsDataServiceImpl bNewsDataServiceImpl;
    public NewsUtilService getNewsUtilService(String source){
        switch(source){
            case "a":
                return aNewsDataServiceImpl;
            case "b":
                return bNewsDataServiceImpl;
            default:
                return null;
        }
    }
}

代码写出来自己都无语了,先把所有的实现类都实例化出来,在根据输入返回。这不是工厂模式,是商店模式吧。。。但是当时也想不到其他办法,就先这么写了,但一直觉得肯定有其他解决方案,直到今天有空,去查了一下,才发现自己都多low。。。

其实spring可以动态获取实现类的~~~

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class NewsUtilServiceFactory {  
    @Autowired
    private ApplicationContext applicationContext; 
    public NewsUtilService getNewsUtilService(String source){
        switch(source){
            case "web":
                return applicationContext.getBean(WebNewsDataServiceImpl.class);
            case "oa":
                return applicationContext.getBean(OANewDataServiceImpl.class);
            default:
                return null;
        }
    }
}

这才是正确写法有木有!

总算弄出来了,赶紧记录下来先~

获取某接口所有实现类

在springboot项目中,为了方便,我们可能需要获取某一个接口下面的所有实现类,根据名称进行匹配使用。

正文

1、ServiceLocator.java

?
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
package com.yang.config;
import com.yang.workOrder.service.IRootService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
 * explain:获取应用上下文并获取相应的接口实现类
 *
 * @author yang
 * @date 2021/1/5
 */
@Component
public class ServiceLocator implements ApplicationContextAware {
    /**
     * 用于保存接口实现类名及对应的类
     */
    private Map<String, IRootService> map;
    /**
     * 获取应用上下文并获取相应的接口实现类
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //根据接口类型返回相应的所有bean
        map = applicationContext.getBeansOfType(IRootService.class);
    }
    /**
     * 获取所有实现集合
     * @return
     */
    public Map<String, IRootService> getMap() {
        return map;
    }
    /**
     * 获取对应服务
     * @param key
     * @return
     */
    public IRootService getService(String key) {
        return map.get(key);
    }
}

2、IRootService.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.yang.workOrder.service;
import com.alibaba.fastjson.JSONObject;
import com.yang.workOrder.entity.WorkOrder;
/**
 * explain:基础流程操作服务接口
 *
 * @author yang
 * @date 2021/1/5
 */
public interface IRootService {
    /**
     * 开始流程
     * @param workOrder
     * @return
     */
    boolean startProcess(WorkOrder workOrder);
}

3、RootA001ServiceImpl.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.yang.workOrder.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.yang.workOrder.entity.WorkOrder;
import com.yang.workOrder.service.IRootService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
 * explain:A_001流程审批实现类
 *
 * @author yang
 * @date 2021/1/5
 */
@Service("A_001")
public class RootA001ServiceImpl implements IRootService {
    private static final Logger LOGGER = LoggerFactory.getLogger(RootA001ServiceImpl.class);
    @Override
    public boolean startProcess(WorkOrder workOrder) {
        return false;
    }
}

4、RootA002ServiceImpl.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.yang.workOrder.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.yang.workOrder.entity.WorkOrder;
import com.yang.workOrder.service.IRootService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
 * explain:A_002流程审批实现类
 *
 * @author yang
 * @date 2021/1/5
 */
@Service("A_002")
public class RootA002ServiceImpl implements IRootService {
    private static final Logger LOGGER = LoggerFactory.getLogger(RootA002ServiceImpl.class);
    @Override
    public boolean startProcess(WorkOrder workOrder) {
        return false;
    }
}

结果

使用spring动态获取接口的不同实现类

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/CarinaYang/article/details/81134603

延伸 · 阅读

精彩推荐