zoukankan      html  css  js  c++  java
  • 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 不同的类型码会自动分发

     

  • 相关阅读:
    开启Android应用调试选项的工具XDebug的介绍
    Android Linker 与 SO 加壳技术
    PE文件格式偏移参考
    Android apk快速定位、灰色按钮克星--DroidSword
    Android系统加载Apk文件的时机和流程分析(1)--Android 4.4.4 r1的源码
    开启Android Apk调试与备份选项的Xposed模块的编写
    基于Xposed Hook实现的Android App的协议算法分析小工具-CryptoFucker
    排序学习之---选择排序
    在PHPStorm中快速插入当前日期
    排序学习之---插入排序
  • 原文地址:https://www.cnblogs.com/blogxiao/p/12203132.html
Copyright © 2011-2022 走看看