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

目录
  • spring动态获取接口的不同实现类
    • 当时想到了两种解决办法
  • 获取某接口所有实现类
    • 正文
    • 结果

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

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

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

public Object saveANews() {
	//1、获取A接口新闻列表
    //2、和缓存对比,存数据到数据库
}

public Object saveBNews() {
	//1、获取B新闻列表
    //2、和缓存对比,存数据到数据库
}

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

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

//业务接口实现方法
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);
			}
    }
}

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

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

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

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

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

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

当时想到了两种解决办法

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

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

//定义工厂类
@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可以动态获取实现类的~~~

@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

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

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

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

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;
    }
}

结果

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • SpringBoot同一接口多个实现类配置的实例详解

    SpringBoot项目中可能出现一个接口有多个实现类的情况,如果不进行配置,注入接口时编译器不知道要注入哪个实现类就会报错,因此需要进行配置.以下进行举例: 接口如下: public interface NoticeService { public String noticeUser(Long id); } 两个实现类如下: @Service public class NoticeServiceImpl1 implements NoticeService { public String not

  • spring注解识别一个接口的多个实现类方法

    1.比如有一个接口如下: public interface Filter { public String doFilter(String param); } 2.有如下接口实现类: public class TimeFilter implements Filter { @Override public String doFilter(String param) { return param.replace("aa", "bb"); } }  3.使用方式:在接口实现

  • spring boot 动态生成接口实现类的场景分析

    目录 一: 定义注解 二: 建立动态代理类 三: 注入spring容器 四: 编写拦截器 五: 新建测试类 在某些业务场景中,我们只需要业务代码中定义相应的接口或者相应的注解,并不需要实现对应的逻辑. 比如 mybatis和feign: 在 mybatis 中,我们只需要定义对应的mapper接口:在 feign 中,我们只需要定义对应业务系统中的接口即可. 那么在这种场景下,具体的业务逻辑时怎么执行的呢,其实原理都是动态代理. 我们这里不具体介绍动态代理,主要看一下它在springboot项目

  • 使用Springboot根据配置文件动态注入接口实现类

    Springboot根据配置文件动态注入接口实现类 需求 最近在做一个Springboot项目,需要面向不同需求的客户,但是为了方便管理分支,需要将不同客户的需求都写到同一套代码中,根据不同客户实例化对应的实现类. 实现 为了尽量不修改代码,少做不必要的逻辑判断,我们考虑为不同客户写不同的Service,然后根据配置参数实例化对应的Service.这样就遇到了需要根据配置文件实现不同类的需求. 针对这一需求大致有两种实现方式.但是针对我的需求,能用的只有第二种,但还是想将第一种一起总结一下. 两

  • Spring Boot中如何使用Convert接口实现类型转换器

    目录 使用Convert接口实现类型转换器 Converter接口 添加依赖 实体类 1.User类 2.Article类 配置类型转化器 1.定义全局日期转换器 2.定义全局对象转换器 3.定义全局List类型转换器 控制器 测试 Converter使用及其原理 配置文件中对Converter的引用 以字符串去空为例 我们查看Converter接口的源码 我们查看对应的成员变量: 使用Convert接口实现类型转换器 在Spring3中引入了一个Converter接口,它支持从一个Object

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

    目录 spring动态获取接口的不同实现类 当时想到了两种解决办法 获取某接口所有实现类 正文 结果 spring动态获取接口的不同实现类 最近做项目,有个需求是和外部对接,从接口获取新闻数据,把数据和缓存中的数据对比,多了的添加到数据库,少了的删掉. 接口有两个,一开始我是在业务类里写了两个方法,代码太长,简单说就是两个部分: public Object saveANews() { //1.获取A接口新闻列表 //2.和缓存对比,存数据到数据库 } public Object saveBNew

  • 分析uniapp如何动态获取接口域名

    背景 接口域名没有写死,而是动态获取.具体实现就是 通过读取一个静态json文件,来设置真实的接口域名.好处是原域名有可能被封,这样可以直接后台操作修改该配置文件即可:不然的话,h5 项目可能还好说,app 的话必须重新发版. 代码 // httpService.js 对 uni.request 的封装. 在数据请求入口处,统一先进行 域名获取,即 执行 config.requestRemoteIp 方法 import config from '@/config' import Vue from

  • springboot如何获取接口下所有实现类

    目录 springboot获取接口下所有实现类 springboot动态调用实现类 1.添加接口 2.创建实现类 3.获取实现类的相关接口 springboot获取接口下所有实现类 首先定义一个接口 public interface LoginUserService { /** * 判断手机号是否允许登录 * * @param phone 手机号 * @return 是否允许 */ boolean hasUser(String phone) throws ServiceException; /*

  • Java SpringBoot 获取接口实现类汇总

    目录 前言 一.获取接口的所有实现类 1.枚举 2.业务接口 2.1 实现类 3.ApplicationContextAware接口实现类 4.获取到所有实现类使用 前言 有时候,根据业务逻辑的需求,需要获取到某个接口的所有实现类,然后根据业务类型来执行不同的实现类方法.有点类似策略模式. 如果没有用到 Spring的话,可以使用 ServiceLoaderl类JDK自带的一个类加载器(其他框架的SPI机制也是可以实现). ServiceLoader<MyInterface> loader =

  • 详解Spring中实现接口动态的解决方法

    前言 本文主要给大家介绍的是关于Spring实现接口动态的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧. 关于这个问题是因为领导最近跟我提了一个需求,是有关于实现类Mybatis的@Select.@Insert注解的功能.其是基于interface层面,不存在任何的接口实现类.因而在实现的过程中,首先要解决的是如何动态实现接口的实例化.其次是如何将使接口根据注解实现相应的功能. 声明 解决方案是基于Mybatis源码,进行二次开发实现. 解决方法 我们先来看看Mybat

  • spring+apollo动态获取yaml格式的配置方式

    默认spring装载的都是.properties格式的配置文件,但是有时我们需要定义list或者map类型的配置,那么yaml就具有优势. 以下演示利用apollo来完成自动更新ip白名单的功能 1.重写配置工厂 public class YmlPropertySourceFactory extends DefaultPropertySourceFactory { public PropertySource<?> createPropertySource(String name, Encode

  • vue项目接口域名动态获取操作

    需求: 接口域名是从外部 .json 文件里获取的. 思路: 在开始加载项目前 进行接口域名获取,然后重置 接口域名的配置项. 实现: 1.config/index.js 文件 进行基础配置 import axios from 'axios' const config = { requestUrl: 'http://qiniu.eightyin.cn/path.json', //动态域名所在地址 baseUrl: { dev: '/api/', pro: 'http://xxx.com/' //

  • 使用Spring特性实现接口多实现类的动态调用方式

    目录 正好用到.mark一下背景 问题的提出 首先我们看一下这个方法的签名 接下来看看我们遇到的问题是什么? 实现 代码如下 验证 关于SPI 下面是JDBC驱动的接口 下面是MySQL的JDBC驱动实现的SPI配置 正好用到.mark一下背景 org.springframework.beans及org.springframework.context这两个包是Spring IoC容器的基础,其中重要的类有BeanFactory,BeanFactory是IoC容器的核心接口,其职责包括:实例化.定

  • Spring动态注册多数据源的实现方法

    最近在做SaaS应用,数据库采用了单实例多schema的架构(详见参考资料1),每个租户有一个独立的schema,同时整个数据源有一个共享的schema,因此需要解决动态增删.切换数据源的问题. 在网上搜了很多文章后,很多都是讲主从数据源配置,或都是在应用启动前已经确定好数据源配置的,甚少讲在不停机的情况如何动态加载数据源,所以写下这篇文章,以供参考. 使用到的技术 Java8 Spring + SpringMVC + MyBatis Druid连接池 Lombok (以上技术并不影响思路实现,

随机推荐