springboot——Spring下动态选择接口实现类(以多方式动态查询maxcompute为例)

前言

最近有个需求是需要通过数据源类型来动态切换数据源的操作,所有配置通过读取配置文件获取。与一般的选择实现类最大的区别是本文是动态选择,不需要通过@Qualifier注释写死实现类。看了下网上相关资料较少,且存在部分不一定正确的情况,故写此篇博客以作记录,也希望碰到此问题的小伙伴能够少走弯路。

注:本文以postgresql和maxcompute官方SDK两种查询方式查询maxcompute为例

 

正文

1.创建odps

package com.XXXX.tools.email.common.model.BO;

import com.aliyun.odps.Odps;
import com.aliyun.odps.account.Account;
import com.aliyun.odps.account.AliyunAccount;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * Description:创建maxcompute中的odps
 * 作者:gu.weidong(Marco)
 */
@Component
public class MaxComputeOdps {
    //读取配置文件中的配置,由于实际项目中还用到了mysql,故此处是secondary
    @Value("${spring.datasource.secondary.username}")
    private String accessId;

    @Value("${spring.datasource.secondary.password}")
    private String accessKey;

    private  final String endPoint = "http://service.odps.aliyun.com/api";
    private  final String project = "XXXX";//对应库名

    @Bean(name="odps")
    public Odps odps(){
        Account account = new AliyunAccount(accessId, accessKey);
        Odps odps = new Odps(account);
        odps.setEndpoint(endPoint);
        odps.setDefaultProject(project);
        return odps;
    }
}

2.数据查询接口

package com.XXXX.tools.email.service.query;
import java.util.LinkedHashMap;
import java.util.List;
/**
 * Description:查询数据接口
 * 作者:gu.weidong(Marco)
 */
public interface QueryService {
    //验证sql,避免DROP等操作
    default boolean validSql(String orgSql){
        String sql = orgSql.toUpperCase().trim();
        if(sql.startsWith("DROP")||sql.startsWith("DELETE")){
            return false;
        }
        return true;
    }

    //查询数据
    List> queryData(String sql);
}

 

3.maxcompute官方SDK的查询方式

package com.XXX.tools.email.service.query;
import com.XXX.tools.email.common.code.ErrorCode;
import com.XXX.tools.email.common.exception.RequestException;
import com.aliyun.odps.Column;
import com.aliyun.odps.Instance;
import com.aliyun.odps.Odps;
import com.aliyun.odps.OdpsException;
import com.aliyun.odps.XXXdata.RecordXXX;
import com.aliyun.odps.task.SQLTask;
import lombok.extern.slf4j.Slf4j;XXX
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
/**
 * Description:maxcompute官方SDK的查询方式
 * 作者:gu.weidong(Marco)
 */
@Service
@Slf4j
public class MaxComputeQueryService implements QueryService {
    @Autowired
    private Odps odps;

    @Override
    public List> queryData(String sql) {
        boolean b = validSql(sql);
        if (!b) {
            log.error("maxcompute的sql异常,sql语句为{}", sql);
            throw new RequestException(ErrorCode.SQL_WRONG);
        }
        Instance i;
        //将所有查询结果放置到该list中
        LinkedList> resultLinkedHashMapList = new LinkedList<>();
        try {
            i = SQLTask.run(odps, sql);
            i.waitForSuccess();
            List records = SQLTask.getResult(i);
            for (Record record : records) {
                Column[] columns = record.getColumns();
                LinkedHashMap resultLinkedHashMap = new LinkedHashMap<>();
                for (int j = 0; j < columns.length; j++) {
                    resultLinkedHashMap.put(columns[j].getName(), record.get(j).toString());
                }
                resultLinkedHashMapList.add(resultLinkedHashMap);
            }
        } catch (OdpsException e) {
            log.error("MaxCompute中sql错误,错误语句为:{}", sql, e);
            throw new RequestException(ErrorCode.SQL_WRONG);
        }
        return resultLinkedHashMapList;
    }
}

 

4.postgresql的查询方式

package com.XXX.tools.email.service.query;

import com.XXX.tools.email.common.code.ErrorCode;
import com.XXX.tools.email.common.exception.RequestException;
import com.XXX.tools.email.dao.mapper.secondary.QueryInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
/**
 * Description:postgresql方式查询maxcompute
 * 作者:gu.weidong(Marco)
 */
@Service
@Slf4j
public class PostgreQueryService implements QueryService {
    @Autowired
    private QueryInfoMapper queryInfoMapper;

    @Override
    public List> queryData(String sql) {
        boolean b = validSql(sql);
        if (!b) {
            log.error("postgre的sql异常,sql语句为{}", sql);
            throw new RequestException(ErrorCode.SQL_WRONG);
        }
        List> mapList = new LinkedList<>();
        try {
            mapList = queryInfoMapper.queryInfos(sql);
        } catch (Exception e) {
            log.error("Postgre中sql错误,错误语句为:{}", sql, e);
            throw new RequestException(ErrorCode.SQL_WRONG);
        }
        return mapList;
    }
}

5.动态切换实现类

package com.XXX.tools.email.service.query;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
 * Description:动态选择查询接口的实现类
 * 作者:gu.weidong(Marco)
 */
@Service
public class GetQueryService implements InitializingBean, ApplicationContextAware {
    private Map queryServiceImplMap = new HashMap<>();
    private ApplicationContext applicationContext;
     public QueryService createQueryService(String type){
        switch (type){
            case "postgresql":return queryServiceImplMap.get("PostgreQueryService");
            case "maxcompute":return queryServiceImplMap.get("MaxComputeQueryService");
            default:return queryServiceImplMap.get("MaxComputeQueryService");
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Map beanMap = applicationContext.getBeansOfType(QueryService.class);
        //遍历该接口的所有实现,将其放入map中
        for (QueryService serviceImpl : beanMap.values()) {
            queryServiceImplMap.put(serviceImpl.getClass().getSimpleName(), serviceImpl);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

 

6.Controller

package com.XXX.tools.email.facade;
import com.XXX.tools.email.service.query.QueryService;
import com.XXX.common.Result;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Description:
 * 作者:gu.weidong(Marco)
 */
@RestController
@Slf4j
public class JobController implements JobFacade {

    @Autowired
    private GetQueryService queryService;

    @Override
    @PostMapping("/jobs/excution")
    @ApiOperation(value = "试跑Job")
    public Result excuteJob(@RequestHeader("X-Username") String username, @RequestBody JobConfigRequestDTO jobConfigRequestDTO) {
        log.info("试跑接口,请求参数 username:" + username + ",jobConfigRequestDTO:" + JSONObject.toJSONString(jobConfigRequestDTO));
        String sql = jobConfigRequestDTO.getSql();
		//重点,获取动态的查询实现
        QueryService queryService = this.queryService.createQueryService("");
        List> mapList = queryService.queryData(sql);
        return Result.success();
    }
}

 

完毕!!!

有问题可以通过左侧公众号联系后台管理员

你可能感兴趣的:(Spring,Boot,Springboot干货系列)