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 不同的類型碼會自動分發

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM