Spring Cloud ApiController

在Spring Cloud项目开发中,一般的目录结构


截屏2022-08-09 13.50.44.png

各个模块是做什么的就不用多说了,主要是web模块,比如我们实现一个登陆功能,首先要在Web模块下面新建一个LoginController,然后再后写接口层和实现层,然后我们需要查询用户又要新建一个UserController,新增加一个功能就要新建一个Controller,那么我不想建这么多的Controller,我想用一个Controller来统一调用接口可不可以,答案是可以。这就是我们接下来要讲的ApiController

ApiController

作用,不用每个功能都写一个Controller,只要写在一个公共的模块里面,其他模块引入就可以了

优点:因为是统一的Controller入口,所以可以做一些拦截,鉴权操作

下面只提供一些核心代码和思路供大家参考

@RestController
@RequestMapping({"/api"})
public class ApiController implements ApplicationContextAware {
    private static final Logger log = LoggerFactory.getLogger(ApiController.class);
    private ApplicationContext applicationContext;
    private static Map<String, Object> serviceCache = new HashMap();

    public ApiController() {
    }

    @CrossOrigin
    @RequestMapping(
            value = {"/{serviceName}/{method}"},
            method = {RequestMethod.POST}
    )

    /**
     * Api入口
     */
    public Object post(@PathVariable("serviceName") String serviceName, @PathVariable("method") String method, @RequestBody String params, HttpServletRequest request, HttpServletResponse response) {
        long startTime = System.currentTimeMillis();
        ServletThreadLocal.put(request, response);
        Object object = this.doService(serviceName, method, params);
        long executeTime = System.currentTimeMillis() - startTime;
        log.info("调用服务[{}.{}]执行时间:{}ms", new Object[]{serviceName, method, executeTime});
        return object;
    }

    /**
     * doService
     *
     * @param serviceName
     * @param method
     * @param params
     * @return
     */
    private Object doService(String serviceName, String method, String params) {
        Object serviceBean = this.getServiceBean(serviceName);
        Result commonResult = null;
        if (serviceBean == null) {
            commonResult = Result.fail(ResultCodeEnum.SERVICE_NOT_FOUND.getDesc());
            log.info("调用服务[{}.{}]响应结果:{}", new Object[]{serviceName, method, JSONObject.toJSONString(commonResult.toString())});
            return commonResult;
        } else {
            Method currentMethod = this.getInvokeMethod(serviceBean, method);
            //方法不存在
            if (currentMethod == null) {
                commonResult = Result.fail(ResultCodeEnum.METHOD_NOT_FOUND.getDesc());
                log.info("调用服务[{}.{}]响应结果:{}", new Object[]{serviceName, method, JSONObject.toJSONString(commonResult.toString())});
                return commonResult;
            } else {
                Type[] paramTypes = currentMethod.getGenericParameterTypes();
                if (paramTypes.length != 0 && paramTypes.length <= 1) {
                    CallTypeEnum callType = CallTypeEnum.REAL;
                    BaseParam baseParam = null;

                    try {
                        //获取入参
                        Object inputParam = JSONObject.parseObject(params, paramTypes[0]);
                        baseParam = (BaseParam) inputParam;
                        log.info("调用服务[{}.{}]请求参数:{}", new Object[]{serviceName, method, JSONObject.toJSONString(inputParam)});

                        callType = baseParam.getCallType();
                        //判断是真实调用还是mock调用,mock调用自己实现
                        if (callType == CallTypeEnum.REAL) {
                            //获取返回结果
                            Object callResult = currentMethod.invoke(serviceBean, inputParam);
                            //判断返回结果类型
                            if (!(callResult instanceof Result)) {
                                throw new RuntimeException("接口返回类型必须为[com.xxx.base.bean.Result]类型");
                            }
                            commonResult = (Result) callResult;
                            log.info("调用服务[{}.{}]响应结果:{}", new Object[]{serviceName, method, JSONObject.toJSONString(commonResult)});
                            return commonResult;
                        }
                    } catch (Exception var17) {
                        commonResult = Result.buildFail(ResultCodeEnum.FAIL.getDesc());
                        log.error("服务[{}.{}]处理失败", new Object[]{serviceName, method, var17});
                    }
                } else {
                    log.info("调用服务[{}.{}]响应结果:{}", new Object[]{serviceName, method, "服务定义形参个数只能为一个"});
                    return Result.buildFail("服务定义形参个数只能为一个");
                }
            }
        }
        return commonResult;
    }

    /**
     * 获取ServiceBean
     *
     * @param serviceName
     * @return
     */
    private Object getServiceBean(String serviceName) {
        Object serviceBean = serviceCache.get(serviceName);
        try {
            if (serviceBean == null) {
                if (serviceName.contains(".")) {
                    Class apiClass = null;

                    try {
                        apiClass = Class.forName(serviceName);
                        serviceBean = this.applicationContext.getBean(apiClass);
                    } catch (ClassNotFoundException var5) {
                        log.error("获取服务[" + serviceName + "]出错", var5);
                    } catch (Exception var6) {
                        log.error("获取服务[" + serviceName + "]出错", var6);
                    }
                } else {
                    serviceBean = this.applicationContext.getBean(serviceName);
                }
            }
        } catch (Exception var7) {
            log.error("获取服务[" + serviceName + "]出错", var7);
        }

        if (serviceBean != null) {
            serviceCache.put(serviceName, serviceBean);
        }
        return serviceBean;
    }

    /**
     * 获取方法
     *
     * @param serviceBean
     * @param method
     * @return
     */
    private Method getInvokeMethod(Object serviceBean, String method) {
        Method currentMethod = null;
        Method[] methods = serviceBean.getClass().getDeclaredMethods();
        Method[] var5 = methods;
        int var6 = methods.length;

        for (int var7 = 0; var7 < var6; ++var7) {
            Method method1 = var5[var7];
            if (method1.getName().equals(method)) {
                currentMethod = method1;
            }
        }

        return currentMethod;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
public class ServletThreadLocal {
    private static ThreadLocal<HttpServletRequest> requestThreadLocal = new ThreadLocal();
    private static ThreadLocal<HttpServletResponse> responseThreadLocal = new ThreadLocal();

    public ServletThreadLocal() {
    }

    public static HttpServletRequest getRequest() {
        return (HttpServletRequest)requestThreadLocal.get();
    }

    public static HttpServletResponse getResponse() {
        return (HttpServletResponse)responseThreadLocal.get();
    }

    public static void putRequest(HttpServletRequest request) {
        requestThreadLocal.set(request);
    }

    public static void putResponse(HttpServletResponse response) {
        responseThreadLocal.set(response);
    }

    public static void put(HttpServletRequest request, HttpServletResponse response) {
        putRequest(request);
        putResponse(response);
    }
}

Api模块

public interface OrderServiceFacade {
    Result<OrderResult> getOrderById(OrderParam param);
}

Service模块

@Service("orderServiceFacade")
public class OrderServiceFacadeImpl implements OrderServiceFacade {

   @Resource
   OrderMainMapper orderMainMapper;

   @Override
   public Result<OrderResult> getOrderById(OrderParam param){
}

这样就不需要我们在写Controller了

接口调用

http://localhost:9091/api/orderServiceFacade/getOrderById
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272