spring boot 接口路由

Posted blog_xiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring boot 接口路由相关的知识,希望对你有一定的参考价值。

背景

  1. 对接饿了吗商户推送接口:配置一个回调接口,但是根据不同的类型码,进行不同的业务处理,所以需要做到根据类型分发

思路

  1. 通过switch 方式获取类型码,调用不同的处理方法:弊端 1.几十个类型码需要写几十个判断  2.扩展性很差,需要硬编码。3.多人协作管理代码混乱
  2. 做一个类似于springmvc 的dispacher 请求分发中心。 优点:1.多人协作只用写接口方法。2.插拔式代码减少耦合。3.只关注自己的业务,不需改动分发中心代码

目的

  1. 需要根据类型码自动找到对应的方法执行,返回统一的数据
  2. 多人可协作
  3. 编码简单

设计思路

  1. 通过自定义注解的方式,将所有自定义注解,在容器初始化的时候收集到容器中(类似于springmvc 的@RequestMapping 所达到的效果)。key:类型码 value :该类型对应的业务方法
  2. 不能破坏springmvc 的请求执行流程。所以需要在controller 内进行接口分发。
  3. 统一处理响应值,进行转换(自定义响应对象-------》转换-----》平台(elm 要求的响应对象))

编码

  自定义注解

  

package com.elm.AnnotationCenter;

import java.lang.annotation.*;

// 注解到对应的业务接口 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE})
public @interface ElmRouterAnnotation {
  // 类型码 String key()
default "" ; }

  初始化容器的扫描 ElmRouterAnnotation ,存放类型码与方法的映射到map容器中

 

package com.elm.AnnotationCenter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.*;

/**
 * @author coderxiao
 * @date [2020-01-16]
 * @description elm自定义注解扫描
 */
@ComponentScan
public class ElmAnntaionScan implements ApplicationListener<ContextRefreshedEvent> {

    private static Logger logger = LoggerFactory.getLogger(ElmAnntaionScan.class);


    public static  LinkedHashMap<String,Method> routerRegisterMapping=new LinkedHashMap<>();



    /**
     * 扫描注册
     * @param event
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(event.getApplicationContext().getParent() == null)
        {
      //先扫描类 Map
<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(Service.class); if(!CollectionUtils.isEmpty(beans)){ Set<Map.Entry<String, Object>> entries = beans.entrySet(); Iterator<Map.Entry<String, Object>> iterator = entries.iterator(); while(iterator.hasNext()){ Map.Entry<String, Object> map = iterator.next(); Class<?> aClass = map.getValue().getClass(); Method[] methods = aClass.getMethods(); if((methods!=null) && (methods.length>0)){ for (Method method: methods ) {
                // 再扫描方法 ElmRouterAnnotation annotation
= method.getAnnotation(ElmRouterAnnotation.class); if(annotation!=null){ String key = annotation.key(); if(StringUtils.isEmpty(key)){ logger.error("项目启动失败:类:{}--->key{}不能为空,方法名{}",aClass.getName(),key,method.getName()); int a=1/0; } Class<?>[] args= method.getParameterTypes(); if((args==null)||(args.length!=2)){ logger.error("========>>>>项目启动失败:类:{}-->方法名{}参数不能为空",aClass.getName(),method.getName()); Assert.state(false); } /* if(!"javax.servlet.http.HttpServletRequest".equals(args[0].getName())){ logger.error("========>>>>项目启动失败:类:{}-->方法名{}请求参数类型错误",aClass.getName(),method.getName()); int a=1/0; }*/ if(!"com.lws.utils.InfResultVoPro".equals(args[1].getName())){ logger.error("========>>>>项目启动失败:类:{}-->方法名{}请求参数类型错误",aClass.getName(),method.getName()); Assert.state(false); } if(routerRegisterMapping.containsKey(key)){ logger.error("========>>>>项目启动失败:类:{}-->key{}不能重复,方法名{}",aClass.getName(),method.getName()); Assert.state(false); }
                    // 存放映射到容器中 routerRegisterMapping.put(key,method); } } } } } } } }

  初始化bean(用于容器启动的时候扫描注解)

package com.config;

import com.elm.AnnotationCenter.ElmAnntaionScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;


/**
 * @author coderxiao
 * @date [2020-01-10]
 * @description 饿了吗配置文件 这里的配置文件只写应用配置,业务配置在自己的代码中加
 */
@Configuration
public class LwsElmConfig {
    @Bean
    public ElmAnntaionScan annotationScan(){return
            new ElmAnntaionScan();
    }

  
}

  分发中心

 

package com.elm.api.controller;

import com.elm.AnnotationCenter.ElmAnntaionScan;
import com.elm.api.ElmRestVo;
import com.utils.InfResultVoPro;
import com.utils.LwsBeanUtils;
import eleme.openapi.sdk.api.entity.message.OMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import red.sea.common.utils.JSON;
import red.sea.commons.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;

@RequestMapping("/elm/elmCallBack")
@RestController
public class ElmCallBackCenterController {

    private static Logger logger = LoggerFactory.getLogger(ElmCallBackCenterController.class);


  // 暴露给饿了吗调用的统一接口 OMessage 请求对象 @RequestMapping(
"/router") public Object router(@ModelAttribute OMessage message, HttpServletRequest request){ InfResultVoPro retVo=new InfResultVoPro(); LinkedHashMap<String,Method> routerMapping= ElmAnntaionScan.routerRegisterMapping; if(StringUtils.isEmpty(message.getRequestId())){ logger.info("接口存活检测-------"); return ElmRestVo.retData(retVo); } logger.info("请求原始数据:"+JSON.toJSONString(message)); retVo.setOriginData(message); String type=String.valueOf(message.getType()); if(routerMapping.containsKey(type)){
      // 分发中心 Method method
=routerMapping.get(type); try { logger.info("开始进入elm请求分发{}",type);
        //这里着重说明一下,类需要被实例化,才能被反射调用 BeanUtils.getBean(beanName|class) 获取spring 托管的bean
       
method.invoke(BeanUtils.getBean(method.getDeclaringClass()),message.getMessage(),retVo); }
catch (Exception e) { logger.error("elm路由分发异常",e); e.printStackTrace(); retVo.setError(""); } }
    //这是统一数据转换
return ElmRestVo.retData(retVo); } }

 

 需要扫描的业务方法

package com.elm.test.service.impl;

import com.elm.AnnotationCenter.ElmRouterAnnotation;
import com.utils.InfResultVoPro;
import org.springframework.stereotype.Service;

/**
 * 这是是service 注解才会被扫描到
 */
@Service
public class ElmTest {

    /**
     * 将注解和方法进行映射
     * @param msg
     * @param retVo
     */
    @ElmRouterAnnotation(key = "1")
    public void myTest(String msg, InfResultVoPro retVo){
        System.out.println("elm----------->MyTest");
    }

}

 

当访问 http://{domain}:{port}/elm/elmCallBack//router 

传入OMessage 不同的类型码会自动分发

 

 

以上是关于spring boot 接口路由的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot:thymeleaf 没有正确渲染片段

spring-boot restful put方式提交表单

解决spring-boot启动中碰到的问题:Cannot determine embedded database driver class for database type NONE(转)(代码片段

springboot 根据目录结构生成路由前缀

一张图,理顺 Spring Boot应用在启动阶段执行代码的几种方式

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段