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 不同的类型码会自动分发

 

原文地址:https://www.cnblogs.com/blogxiao/p/12203132.html

时间: 2024-10-28 20:52:38

spring boot 接口路由的相关文章

spring boot接口 支持https

1.拥有证书,可自己生成测试用javatool生成 keytool -keystore [keyname].jks -genkey -alias tomcat -keyalg RSA 接下来输入相关信息即可 2.把证书添加到项目中/src/main/resources/目录下 3.增加配置 server.port= 8443 server.ssl.key-store= classpath:mykeys.jks server.ssl.key-store-password= yourpassword

Spring Boot 接口 返回json格式数据

@ResponseBody //必须添加此注解 @RequestMapping("/emPower") public String emPowers(@RequestBody Object user) { System.out.println(user); return "{'msg':'OK','success':200'}"; } 原文地址:https://www.cnblogs.com/xy888/p/9045288.html

Spring Boot Hello World (restful接口)例子

Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 spring boot 连接Mysql spring boot配置druid连接池连接mysql spring boot集成mybatis(1) spring boot集成mybatis(2) – 使用pagehelper实现分页 spring boot集成mybatis(3) – mybatis ge

spring boot 开发环境搭建(Eclipse)

Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 spring boot 连接Mysql spring boot配置druid连接池连接mysql spring boot集成mybatis(1) spring boot集成mybatis(2) – 使用pagehelper实现分页 spring boot集成mybatis(3) – mybatis ge

spring boot redis 缓存(cache)集成

Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 spring boot 连接Mysql spring boot配置druid连接池连接mysql spring boot集成mybatis(1) spring boot集成mybatis(2) – 使用pagehelper实现分页 spring boot集成mybatis(3) – mybatis ge

spring boot 环境配置(profile)切换

Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 spring boot 连接Mysql spring boot配置druid连接池连接mysql spring boot集成mybatis(1) spring boot集成mybatis(2) – 使用pagehelper实现分页 spring boot集成mybatis(3) – mybatis ge

Spring Boot 介绍

Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 spring boot 连接Mysql spring boot配置druid连接池连接mysql spring boot集成mybatis(1) spring boot集成mybatis(2) – 使用pagehelper实现分页 spring boot集成mybatis(3) – mybatis ge

15 个优秀开源的 Spring Boot 学习项目

Spring Boot 算是目前 Java 领域最火的技术栈了,松哥年初出版的 <Spring Boot + Vue 全栈开发实战>迄今为止已经加印了 8 次,Spring Boot 的受欢迎程度可见一斑.经常有人问松哥有没有推荐的 Spring Boot 学习资料?当然有!买松哥书就对了,哈哈. 有需要书籍<Spring Boot+Vue全栈开发实战>PDF版的同学,可以在公众号:Java知己,发送:全栈开发实战,获取该书籍. 除了书呢?当然就是开源项目了,今天松哥整理了几个优质

七个开源的 Spring Boot 前后端分离项目,一定要收藏!

前后端分离已经在慢慢走进各公司的技术栈,根据松哥了解到的消息,不少公司都已经切换到这个技术栈上面了.即使贵司目前没有切换到这个技术栈上面,松哥也非常建议大家学习一下前后端分离开发,以免在公司干了两三年,SSH 框架用的滚瓜烂熟,出来却发现自己依然没有任何优势! 其实前后端分离本身并不难,后段提供接口,前端做数据展示,关键是这种思想.很多人做惯了前后端不分的开发,在做前后端分离的时候,很容易带进来一些前后端不分时候的开发思路,结果做出来的产品不伦不类,因此松哥这里给大家整理了几个开源的前后端分离项