日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Springmvc借助SimpleUrlHandlerMapping实现接口开关功能

發布時間:2025/3/8 javascript 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Springmvc借助SimpleUrlHandlerMapping实现接口开关功能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、接口開關功能

  1、可配置化,依賴配置中心

  2、接口訪問權限可控

  3、springmvc不會掃描到,即不會直接的將接口暴露出去

二、接口開關使用場景

  和業務沒什么關系,主要方便查詢系統中的一些狀態信息。比如系統的配置信息,中間件的狀態信息。這就需要寫一些特定的接口,不能對外直接暴露出去(即不能被springmvc掃描到,不能被swagger掃描到)。

三、SimpleUrlHandlerMapping官方解釋

  SimpleUrlHandlerMapping實現HandlerMapping接口以從URL映射到請求處理程序bean。
  支持映射到bean實例和映射到bean名稱;后者是非單身處理程序所必需的。
  “urlMap”屬性適合用bean引用填充處理程序映射,例如通過XML bean定義中的map元素。
  可以通過“mappings”屬性以java.util.Properties類接受的形式設置bean名稱的映射,如下所示:/welcome.html=ticketController /show.html=ticketController語法為PATH = HANDLER_BEAN_NAME。  
  如果路徑不以斜杠開頭,則前置一個。支持直接匹配(給定“/ test” - >注冊“/ test”)和“*”模式匹配(給定“/ test” - >注冊“/ t *”)。

四、接口開關實現

  就像SimpleUrlHandlerMapping javadoc中描述的那樣,其執行原理簡單理解就是根據URL尋找對應的Handler。借助這種思想,我們在Handler中再借助RequestMappingHandlerMapping和RequestMappingHandlerAdapter來幫助我們完成URL的轉發。這樣做的好處是不需要直接暴露的接口開發規則只需要稍作修改,接下來將詳細介紹一下。

  請求轉發流程如下

  

  想法是好的,如何實現這一套流程呢?首先要解決以下問題。

  1、定義的接口不能被springmvc掃描到。

  2、接口定義還是要按照@RequestMaping規則方式編寫,這樣才能減少開發量并且能被RequestMappingHandlerMapping處理。

  3、如何自動注冊url->handler到SimpleUrlHandlerMapping中去。

  對于上面需要實現的,首先要了解一些springmvc相關源碼。

  RequestMappingHandlerMapping初始化method mapping

/*** Scan beans in the ApplicationContext, detect and register handler methods.* @see #isHandler(Class)* @see #getMappingForMethod(Method, Class)* @see #handlerMethodsInitialized(Map)*/ protected void initHandlerMethods() {if (logger.isDebugEnabled()) {logger.debug("Looking for request mappings in application context: " + getApplicationContext());}String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :getApplicationContext().getBeanNamesForType(Object.class));for (String beanName : beanNames) {if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {Class<?> beanType = null;try {beanType = getApplicationContext().getType(beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);}}if (beanType != null && isHandler(beanType)) {detectHandlerMethods(beanName);}}}handlerMethodsInitialized(getHandlerMethods()); }

?  isHandler方法【判斷方法是不是一個具體handler】邏輯如下

protected boolean isHandler(Class<?> beanType) {return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }

  所以我們定義的開關接口為了不被springmvc掃描到,直接去掉類注釋上的@Controller注解和@RequestMapping注解就好了,如下。

@Component @ResponseBody public class CommonsStateController {@GetMapping("/url1")public String handleUrl1() {return null;}

  @GetMapping("/url2")public String handleUrl2() {return null;} }

  按照如上的定義,url? -> handler(/message/state/* ->?CommonsStateController )形式已經出來了,但是還缺少父類路徑 /message/state/ 以及 如何讓RequestMappingHandlerMapping識別CommonsStateController這個handler 中的所有子handler。

  抽象Handler以及自定義RequestMappingHandlerMapping

import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; import java.util.Objects;/*** @author hujunzheng* @create 2018-08-10 12:53**/ public abstract class BaseController extends AbstractController implements InitializingBean {private RequestMappingHandlerMapping handlerMapping = new BaseRequestMappingHandlerMapping();@Autowiredprivate RequestMappingHandlerAdapter handlerAdapter;@Overrideprotected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerExecutionChain mappedHandler = handlerMapping.getHandler(request);return handlerAdapter.handle(request, response, mappedHandler.getHandler());}@Overridepublic void afterPropertiesSet() {handlerMapping.afterPropertiesSet();}private class BaseRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
     //初始化子handler mapping@Override
protected void initHandlerMethods() {detectHandlerMethods(BaseController.this);}
//合并父路徑和子handler路徑@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {RequestMappingInfo info = super.getMappingForMethod(method, handlerType);if (!Objects.isNull(info) && StringUtils.isNotBlank(getBasePath())) {info = RequestMappingInfo.paths(getBasePath()).build().combine(info);}return info;}}//開關接口定義父路徑public abstract String getBasePath(); }

  所有開關接口handler都繼承這個BaseController?抽象類,在對象初始時創建所有的子handler mapping。SimpleUrlHandlerMapping最終會調用開關接口的handleRequestInternal方法,方法內部通過RequestMappingHandlerMapping和RequestMappingHandlerAdapter 將請求轉發到具體的子handler。

@Component @ResponseBody public class CommonsStateController extends BaseController {@GetMapping("/url1")public String handleUrl1() {return null;}@GetMapping("/url2")public String handleUrl2() {return null;} }

?

  自動注冊url-handler到SimpleUrlHandlerMapping

import org.apache.commons.lang3.StringUtils; import org.springframework.util.CollectionUtils; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;import java.util.HashMap; import java.util.List; import java.util.Map;/*** @author hujunzheng* @create 2018-08-10 13:57**/ public class EnhanceSimpleUrlHandlerMapping extends SimpleUrlHandlerMapping {public EnhanceSimpleUrlHandlerMapping(List<BaseController> controllers) {if (CollectionUtils.isEmpty(controllers)) {//NOSONARreturn;}Map<String, BaseController> urlMappings = new HashMap<>();controllers.forEach(controller -> {String basePath = controller.getBasePath();if (StringUtils.isNotBlank(basePath)) {if (!basePath.endsWith("/*")) {basePath = basePath + "/*";}urlMappings.put(basePath, controller);}});this.setUrlMap(urlMappings);} }

  獲取BaseController父路徑,末尾加上‘/*’,然后將url -> handler關系注冊到SimpleUrlHandlerMapping的urlMap中去。這樣只要請求路徑是 父路徑/*的模式都會被SimpleUrlHandlerMapping處理并轉發給對應的handler(BaseController),然后在轉發給具體的子handler。

  接口開關邏輯

import com.cmos.wmhopenapi.service.config.LimitConstants; import com.google.common.collect.Lists; import org.apache.commons.lang3.StringUtils; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.stream.Collectors;/*** @author hujunzheng* @create 2018-08-10 15:17**/ public class UrlHandlerInterceptor extends HandlerInterceptorAdapter {private SimpleUrlHandlerMapping mapping;private LimitConstants limitConstants;public UrlHandlerInterceptor(SimpleUrlHandlerMapping mapping, LimitConstants limitConstants) {this.mapping = mapping;this.limitConstants = limitConstants;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String lookupUrl = mapping.getUrlPathHelper().getLookupPathForRequest(request);String urllimits = limitConstants.getUrllimits();if (StringUtils.isNotBlank(urllimits)) {for (String urllimit : Lists.newArrayList(urllimits.split(",")).stream().map(value -> value.trim()).collect(Collectors.toList())) {if (mapping.getPathMatcher().match(urllimit, lookupUrl)) {return false;}}}return true;} }

  基本思路就是通過 UrlPathHelper獲取到request的lookupUrl(例如 /message/state/url1) ,然后獲取到配置中心配置的patter path(例如message/state/*),最后通過 AntPathMatcher進行二者之間的匹配,如果成功則禁止接口訪問。

?五、接口開關配置

@Bean public SimpleUrlHandlerMapping simpleUrlHandlerMapping(ObjectProvider<List<BaseController>> controllers, LimitConstants limitConstants) {SimpleUrlHandlerMapping mapping = new EnhanceSimpleUrlHandlerMapping(controllers.getIfAvailable());mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);mapping.setInterceptors(new UrlHandlerInterceptor(mapping, limitConstants));return mapping; }

  創建自定義的SimpleUrlHandlerMapping,然后將類型為BaseController所有handler以構造參數的形式傳給SimpleUrlHandlerMapping,并設置接口開關邏輯攔截器。

  至此,接口開關能力已經實現完畢。再也不用在擔心接口會直接暴露出去了,可以通過配置隨時更改接口的訪問權限。

轉載于:https://www.cnblogs.com/hujunzheng/p/9902475.html

總結

以上是生活随笔為你收集整理的Springmvc借助SimpleUrlHandlerMapping实现接口开关功能的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。