SpringMVC中的HandlerMapping和HandlerAdapter詳解
一、引言
本人在閱讀 SpringMVC 源碼過(guò)程中,一直對(duì) HandlerMapping 、 HandlerAdapter 有疑惑,一直不理解。心里知道這里用的是適配器模式,本人對(duì)適配器模式還是知曉的,但這兩個(gè)東西就是不理解。最近突然知道了一個(gè)知識(shí)點(diǎn),瞬間豁然開(kāi)朗,至于是哪個(gè)知識(shí)點(diǎn),下面慢慢說(shuō)。
下面這張圖是 SpringMVC 的工作流程圖,隨便一搜,應(yīng)該一大把,這里只做熟悉用,不會(huì)細(xì)說(shuō)。(PS:之前跳槽面試,就有一道筆試題讓我畫(huà)SpringMVC的工作流程。。。。)
對(duì)上圖做一下簡(jiǎn)單總結(jié):
1、請(qǐng)求首先進(jìn)入 DispatcherServlet , 由 DispatcherServlet 從 HandlerMappings 中匹配對(duì)應(yīng)的 Handler ,此時(shí)只是獲取到了對(duì)應(yīng)的 Handler ,然后拿著這個(gè) Handler 去尋找對(duì)應(yīng)的適配器,即: HandlerAdapter ;
2、拿到對(duì)應(yīng) HandlerAdapter 時(shí),這時(shí)候開(kāi)始調(diào)用對(duì)應(yīng)的 Handler 方法,即執(zhí)行我們的 Controller 來(lái)處理業(yè)務(wù)邏輯了, 執(zhí)行完成之后返回一個(gè) ModeAndView ;
3、 HandlerAdapter 執(zhí)行完之后,返回一個(gè) ModeAndView ,把它交給我們的視圖解析器 ViewResolver ,通過(guò)視圖名稱(chēng)查找出對(duì)應(yīng)的視圖然后返回;
4、最后,渲染視圖 返回渲染后的視圖。
二、SpringMVC中定義Controller的方式
在介紹 HandlerMapping 、 HandlerAdapter 之前,先來(lái)說(shuō)一下 SpringMVC 中定義 Handler 的方式,本人就是對(duì)這個(gè)知識(shí)點(diǎn)不熟悉,導(dǎo)致對(duì)這兩個(gè)對(duì)象一直不明白。
先說(shuō)一下最最最最……常用定義 Handler 的方式,使用 @RequestMapping 注解,下面這段代碼不用介紹吧:
@Controller public class IndexController { @RequestMapping("/index") @ResponseBody public String sayHello(){ System.out.println("hello ..."); return "hello"; } }
那大家有沒(méi)有用過(guò)下面的兩種方式來(lái)聲明一個(gè) Handler 呢??
實(shí)現(xiàn) org.springframework.web.servlet.mvc.Controller 控制器接口,此接口只有一個(gè)方法 handleRequest() ,用于請(qǐng)求的處理,返回 ModelAndView 。 這個(gè)接口從第一版 SpringMVC 就存在了,所以這個(gè)接口是非常古老的接口~~~也是 Spring MVC 最早期的實(shí)現(xiàn) Handler 的方式
// 關(guān)注一下這個(gè)包 import org.springframework.web.servlet.mvc.Controller; @Component("/home") public class HomeController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("home ..."); return null; } // 這地方考慮個(gè)問(wèn)題:怎么樣實(shí)現(xiàn)類(lèi)似@ResponseBody的功能呢? // 就是想實(shí)現(xiàn)直接向body里寫(xiě)數(shù)據(jù),而不是返回一個(gè)頁(yè)面。 // 如果想直接在處理器/控制器里使用response向客戶(hù)端寫(xiě)回?cái)?shù)據(jù), // 可以通過(guò)返回null來(lái)告訴 DispatcherServlet我們已經(jīng)寫(xiě)出響應(yīng)了, // 不需要它進(jìn)行視圖解析。像下面這樣 @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("home ..."); response.getWriter().write("home controller from body"); return null; // 返回null告訴視圖渲染 直接把body里面的內(nèi)容輸出瀏覽器即可 } }
實(shí)現(xiàn) org.springframework.web.HttpRequestHandler 接口, HttpRequestHandler 用于處理 Http requests ,其類(lèi)似于一個(gè)簡(jiǎn)單的 Servlet ,只有一個(gè) handlerRequest() 方法,其處理邏輯隨子類(lèi)的實(shí)現(xiàn)不同而不同。
// 關(guān)注一下這個(gè)包 import org.springframework.web.HttpRequestHandler; @Component("/login") public class LoginController implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("login..."); response.getWriter().write("login ..."); } }
再來(lái)看一下 servlet 的使用,是不是很相似。
@WebServlet("/myServlet") public class MyServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.service(req, resp); } }
其實(shí)上面這兩種方式第一種使用 @RequestMapping 注解一樣,都能定義為一個(gè) Handler ,攔截到對(duì)應(yīng)的請(qǐng)求,并且做出響應(yīng)。這地方就要牽扯出 HandlerMapping 了。
三、何為HandlerMapping、HandlerAdapter?
從上面的分析,我們知道, Handler 的定義有上面三種(也有可能還有其他方式,比如Servlet),這地方就要引出下面這兩個(gè) HandlerMapping:BeanNameUrlHandlerMapping 、 RequestMappingHandlerMapping ,當(dāng)然還有其他 HandlerMapping ,下面的斷點(diǎn)圖也能說(shuō)明這一點(diǎn)。
這里先說(shuō)明一下,用注解 @RequestMapping 定義的 Handler ,用的是 RequestMappingHandlerMapping ,上面的其他兩種,用的是 BeanNameUrlHandlerMapping ,靜態(tài)資源的請(qǐng)求,用的是 SimpleUrlHandlerMapping 。
這地方我們可以從 Spring 的角度考慮,Spring 容器在啟動(dòng)的時(shí)候,會(huì)去掃描所有的組件,并把它們實(shí)例化。當(dāng) Spring 容器發(fā)現(xiàn)一個(gè)方法用 @RequestMapping 注解標(biāo)注的時(shí)候,就用 RequestMappingHandlerMapping 這個(gè)類(lèi)去實(shí)例化,當(dāng)發(fā)現(xiàn)一個(gè)類(lèi)實(shí)現(xiàn)了 org.springframework.web.servlet.mvc.Controller 這個(gè)接口的時(shí)候,就用 BeanNameUrlHandlerMapping 去實(shí)例化,然后將所有請(qǐng)求放在一個(gè)Map里,用請(qǐng)求路徑(比如:/index)和對(duì)應(yīng)的 Handler 做映射處理,這樣是不是更好理解。
HandlerMapping的作用:主要是根據(jù) request 請(qǐng)求匹配/映射上能夠處理當(dāng)前 request 的 Handler .
下面來(lái)看一下如何根據(jù) request 來(lái)獲取 HandlerMapping
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
下面是對(duì) /index 請(qǐng)求的斷點(diǎn)調(diào)試圖,我們從圖中可以看出, this.handlerMappings 里面有4個(gè)類(lèi),有一個(gè)為重復(fù)的。
循環(huán)這個(gè)List,判斷這個(gè) /index 請(qǐng)求是由哪個(gè) Handler 來(lái)處理(即查找 HandlerMapping 的過(guò)程)。
通過(guò)循環(huán) HandlerMapping 來(lái)獲取 HandlerExecutionChain ,再次強(qiáng)調(diào),因?yàn)?spring 當(dāng)中存在的 Handler 有多種形式,我們處理 request 需要通過(guò) HandlerExecutionChain 來(lái)反射執(zhí)行 Handler 當(dāng)中的方法,所以不同的 Handler 需要 new 不同的 HandlerExecutionChain ,那么問(wèn)題來(lái)了 HandlerExecutionChain 不知道你的 Handler 是什么類(lèi)型(因?yàn)?HandlerExecutionChain 里只定義了一個(gè) Object handler 屬性,它不知道你的 Handler 是什么類(lèi)型的),但是 HandlerMapping 知道,所以 HandlerExecutionChain 的實(shí)例化必須依賴(lài) HandlerMapping 。
好,講到這終于明白 HandlerMapping 的干嘛的了,至于如何根據(jù) /index 去找對(duì)應(yīng)的 Handler 和 HandlerExecutionChain ,這里就不做介紹啦。
那上面幾個(gè) HandlerMapping 是怎么來(lái)的呢? Spring 容器在初始化的過(guò)程中,會(huì)調(diào)用到 initStrategies 中的 initHandlerMappings(context)、initHandlerAdapters(context); 這兩個(gè)方法。我們?cè)谠创a包的 DispatcherServlet.properties 文件下會(huì)看見(jiàn), 它定義了圖片里的這些屬性。 第一個(gè)屬性,就是我們剛看見(jiàn)的 HandlerMappings , 也就是說(shuō) HandlerMappings 是 SpringMVC 事先定義好的, Spring容器 會(huì)幫我們創(chuàng)建。至于第二個(gè)屬性,也就是 HandlerAdapter 。
介紹完 HandlerMapping 之后,下面就要來(lái)介紹 HandlerAdapter 了。
HandlerAdapter的作用:
因?yàn)?Spring MVC 中的 Handler 可以有多種實(shí)現(xiàn)形式,但是 Servlet 需要的處理方法的結(jié)構(gòu)卻是固定的,都是以 request 和 response 作為方法入?yún)?,那么如何讓固定參?shù)的 Servlet 處理方法調(diào)用靈活的 Handler 來(lái)進(jìn)行處理呢?這就需要 HandlerAdapter 來(lái)做適配。
為什么需要HandlerAdapter?
前面說(shuō)過(guò)不同的請(qǐng)求會(huì)獲取到不同的 Handler ,那么不同的 Handler 它是怎么實(shí)現(xiàn)處理不同的請(qǐng)求的呢?我的第一反應(yīng)是抽象出一個(gè)接口,定義一個(gè)公共接口,然后讓每個(gè) Handler 實(shí)現(xiàn)這個(gè)接口,我想的沒(méi)問(wèn)題吧,但 Spring 不是這么做的,為什么呢?
再次強(qiáng)調(diào): Spring MVC 的 Handler ( Controller接口,HttpRequestHandler,@RequestMapping、Servlet )有多種表現(xiàn)形式,不同的 Handler ,處理請(qǐng)求的方式是不一樣的,注解 @RequestMapping 方式使用的是用方法處理請(qǐng)求,而實(shí)現(xiàn) Controller 接口和 HttpRequestHandler 接口方式使用的是一個(gè)類(lèi),而適配器模式就能模糊掉具體的實(shí)現(xiàn),從而就能提供統(tǒng)一訪問(wèn)接口,所以這地方就要使用適配器了。
這樣做的好處有兩個(gè) (1)、處理器程序,也就是 Handler ,允許的是任意的Object,只要返回封裝好的 HandlerExecutionChain ,具體的 Handler 不用管;(2)、集成第三方請(qǐng)求處理器的時(shí)候,本處代碼也無(wú)需修改,加個(gè)適配器就行(PS:這地方可以參考文章最后的模擬程序)
HandlerMapping 的源碼也說(shuō)明了這一點(diǎn)。 HandlerMapping 接口里面只有一個(gè) getHandler() 方法,而且返回類(lèi)型是 HandlerExecutionChain ,用 HandlerExecutionChain 里面定義了一個(gè) Object 類(lèi)型的 handler 屬性,并對(duì) handler 進(jìn)行了封裝,在每個(gè)請(qǐng)求里加入了攔截器鏈。然后將這個(gè) HandlerExecutionChain 里面的 handler 傳給了 HandlerAdapter 。
這地方我們可以換個(gè)角度,就是萬(wàn)一處理請(qǐng)求的每個(gè)方法不一樣怎么辦?支持?jǐn)U展的話(huà),是不是就需要適配器模式了
說(shuō)了這么多,是不是終于知道為什么需要 HandlerAdapter 了。
在得到 Handler 之后,就是下面的這行代碼,我們來(lái)看一下 getHandlerAdapter() 方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } }
從代碼中能看到,從一個(gè) this.handlerAdapters 屬性里面遍歷了我們的適配器。這個(gè) handlerAdapters 哪來(lái)的呢? 跟上面的 this.HandlerMappings 一樣,在 SpringMVC 的配置文件里面配置的,也就是上圖中的第二個(gè)屬性。
實(shí)現(xiàn) org.springframework.web.servlet.mvc.Controller 接口形式的處理器,對(duì)應(yīng)的 HandlerMapping 是 BeanNameUrlHandlerMapping ,對(duì)應(yīng)的 HandlerAdapter 是 HttpRequestHandlerAdapter
實(shí)現(xiàn) org.springframework.web.HttpRequestHandler 接口形式的處理器,對(duì)應(yīng)的 HandlerMapping 也是 BeanNameUrlHandlerMapping ,對(duì)應(yīng)的 HandlerAdapter 也是 HttpRequestHandlerAdapter 。
最后看一下三個(gè)適配器中的 supports() 和 handle() 方法
SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter 適配 org.springframework.web.servlet.mvc.Controller 這種 Handler 。
源碼非常之簡(jiǎn)單,它是一個(gè)非常古老的適配器,幾乎已棄用狀態(tài)。
因?yàn)樗苯犹幚淼木褪窃瓷?HttpServletRequest 和 HttpServletResponse ,所以它和 Servlet容器 是強(qiáng)綁定的。
無(wú)數(shù)據(jù)自動(dòng)封裝、校驗(yàn)等一系列高級(jí)功能,所以實(shí)際應(yīng)用中此種方式很少被使用。
// 適配`org.springframework.web.servlet.mvc.Controller`這種Handler public class SimpleControllerHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof Controller); } // 最終執(zhí)行邏輯的還是Handler啊~~~~ @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); } }
HttpRequestHandlerAdapter
HttpRequestHandlerAdapter 適配 org.springframework.web.HttpRequestHandler 這種 Handler 。
它比 Controller 方式還源生。 它和上面的唯一不同是: return null 。
那是因?yàn)?HttpRequestHandler#handleRequest() 它沒(méi)有返回值,這就需要全靠開(kāi)發(fā)者自己寫(xiě) response ,而 Controller 最起碼來(lái)說(shuō)還有 Model和View 自動(dòng)渲染的能力。
public class HttpRequestHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof HttpRequestHandler); } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; }
RequestMappingHandlerAdapter
RequestMappingHandlerAdapter 主要是支持到了 org.springframework.web.method.HandlerMethod 這種 handler ,顯然這種處理器也是我們最最最最為常用的,它已經(jīng)把 HandlerMethod 的實(shí)現(xiàn)精確到了使用 @RequestMapping 注解標(biāo)注的方法。
這個(gè)類(lèi),我們要查看它的父類(lèi) AbstractHandlerMethodAdapter 。
public class AbstractHandlerMethodAdapter { // 只處理HandlerMethod 類(lèi)型的處理器。抽象方法supportsInternal默認(rèn)返回true // 是留出的鉤子可以給你自己擴(kuò)展的 @Override public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); } @Override public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 抽象方法交給子類(lèi)handleInternal去實(shí)現(xiàn) return handleInternal(request, response, (HandlerMethod) handler); } }
看完之后,再來(lái)讀一下 DispatcherServlet#doDispatch() 方法的分發(fā)流程,看看 DispatcherServlet 是如何使用 HandlerMapping和HandlerAdapter 。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... //1、根據(jù)URL(當(dāng)然不一定非得是URL)匹配到一個(gè)處理器 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { // 若匹配不到Handler處理器,就404了 noHandlerFound(processedRequest, response); return; } //2、從HandlerExecutionChain里拿出Handler(注意是Object類(lèi)型哦~ )然后找到屬于它的適配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); ... //3、執(zhí)行作用在此Handler上的所有攔截器的Pre方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //4、真正執(zhí)行handle方法(也就是你自己書(shū)寫(xiě)的邏輯方法),得到一個(gè)ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //5、視圖渲染 applyDefaultViewName(processedRequest, mv); //6、執(zhí)行攔截器的post方法(可見(jiàn)它是視圖渲染完成了才會(huì)執(zhí)行的哦~) mappedHandler.applyPostHandle(processedRequest, response, mv); ... //7、執(zhí)行攔截器的afterCompletion方法(不管拋出與否) }
從執(zhí)行步驟中可以看到: HandlerAdapter 對(duì)于執(zhí)行流程的通用性起到了非常重要的作用,它能把任何一個(gè) Handler(注意是Object類(lèi)型) 都適配成一個(gè) HandlerAdapter ,從而可以做統(tǒng)一的流程處理,這也是為何 DispatcherServlet 它能作為其它 web處理框架 的分發(fā)器的原因,因?yàn)樗鼪](méi)有耦合具體的處理器,你完全可以自己去實(shí)現(xiàn)。
四、模擬HandlerMapping/HandlerAdapter的適配器模式
如果上面的講法,你還是不懂,下面就用適配器模式模擬一下,這兩個(gè)類(lèi)的具體調(diào)用情況,應(yīng)該會(huì)一目了然。下面是依賴(lài)關(guān)系的類(lèi)圖。其中 Controller 代表的就是 HandlerMapping 。具體代碼,可以在文章最后下載。
//多種Controller實(shí)現(xiàn) public interface Controller { } // 注意這里每個(gè)實(shí)現(xiàn),都用了不同的方法名, 如果都用一樣的話(huà),就可以放到接口中了 class HttpController implements Controller { public void doHttpHandler() { System.out.println("http..."); } } class SimpleController implements Controller { public void doSimplerHandler() { System.out.println("simple..."); } } class AnnotationController implements Controller { public void doAnnotationHandler() { System.out.println("annotation..."); } }
// 定義一個(gè)Adapter接口 public interface HandlerAdapter { public boolean supports(Object handler); public void handle(Object handler); } // 多種適配器類(lèi) class SimpleHandlerAdapter implements HandlerAdapter { public void handle(Object handler) { ((SimpleController) handler).doSimplerHandler(); } public boolean supports(Object handler) { return (handler instanceof SimpleController); } } class HttpHandlerAdapter implements HandlerAdapter { public void handle(Object handler) { ((HttpController) handler).doHttpHandler(); } public boolean supports(Object handler) { return (handler instanceof HttpController); } } class AnnotationHandlerAdapter implements HandlerAdapter { public void handle(Object handler) { ((AnnotationController) handler).doAnnotationHandler(); } public boolean supports(Object handler) { return (handler instanceof AnnotationController); } }
public class DispatchServlet { public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>(); public DispatchServlet() { handlerAdapters.add(new AnnotationHandlerAdapter()); handlerAdapters.add(new HttpHandlerAdapter()); handlerAdapters.add(new SimpleHandlerAdapter()); } public void doDispatch() { // 此處模擬SpringMVC從request取handler的對(duì)象, // 適配器可以獲取到希望的Controller HttpController controller = new HttpController(); // AnnotationController controller = new AnnotationController(); //SimpleController controller = new SimpleController(); // 得到對(duì)應(yīng)適配器 HandlerAdapter adapter = getHandler(controller); // 通過(guò)適配器執(zhí)行對(duì)應(yīng)的controller對(duì)應(yīng)方法 adapter.handle(controller); } public HandlerAdapter getHandler(Controller controller) { //遍歷:根據(jù)得到的controller(handler), 返回對(duì)應(yīng)適配器 for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(controller)) { return adapter; } } return null; } public static void main(String[] args) { new DispatchServlet().doDispatch(); // http... } }
注意: Controller 接口的每個(gè)實(shí)現(xiàn)類(lèi),都用了不同的方法名, 這樣的話(huà)就需要用到適配器模式了,如果都用一樣的話(huà),就可以放到接口中了,這樣是不是可以理解 SpringMVC 中此處的 HandlerAdapter 了
五、小結(jié)
還是做個(gè)小結(jié)吧。 SpringMVC 的 Handler 有多種實(shí)現(xiàn)方式(Controller,HttpRequestHandler,Servlet等),例如繼承Controller接口的形式,基于注解@Controller控制器方式的,HttpRequestHandler方式的。
由于實(shí)現(xiàn)方式不一樣,調(diào)用方式就不確定。
- 繼承 Controller 方式所使用的HandlerMapping:BeanNameUrlHandlerMapping,
- 繼承 Controller 方式所使用的適配器:SimpleControllerHandlerAdapter 、
- 注解方式@Controller的HandlerMapping器:RequestMappingHandlerMapping
- 注解方式@Controller適配器:RequestMappingHandlerAdapter、
這是一一對(duì)應(yīng)的。
如果說(shuō)的不對(duì)的,反饋一下給我啊,謝謝……
到此這篇關(guān)于SpringMVC中的HandlerMapping和HandlerAdapter詳解的文章就介紹到這了,更多相關(guān)HandlerMapping和HandlerAdapter內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring擴(kuò)展之基于HandlerMapping實(shí)現(xiàn)接口灰度發(fā)布實(shí)例
- SpringMVC中的handlerMappings對(duì)象用法
- Spring MVC學(xué)習(xí)教程之RequestMappingHandlerMapping匹配
- SpringMVC源碼解讀之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化
- SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列request分發(fā)
- SpringMVC源碼解讀之HandlerMapping
相關(guān)文章
Java getRealPath("/")與getContextPath()區(qū)別詳細(xì)分析
這篇文章主要介紹了Java getRealPath("/")與getContextPath()區(qū)別詳細(xì)分析,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08SpringCloud URL重定向及轉(zhuǎn)發(fā)代碼實(shí)例
這篇文章主要介紹了SpringCloud URL重定向及轉(zhuǎn)發(fā)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03詳解spring boot 使用application.properties 進(jìn)行外部配置
這篇文章主要介紹了詳解spring boot 使用application.properties 進(jìn)行外部配置,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03SpringBoot整合Redis實(shí)現(xiàn)消息發(fā)布與訂閱的示例代碼
能實(shí)現(xiàn)發(fā)送與接收信息的中間介有很多,比如:RocketMQ、RabbitMQ、ActiveMQ、Kafka等,本文主要介紹了Redis的推送與訂閱功能并集成Spring Boot的實(shí)現(xiàn),感興趣的可以了解一下2022-08-08Spring事務(wù)失效的各種場(chǎng)景(13種)
本文主要介紹了Spring事務(wù)失效的各種場(chǎng)景,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Java中IO流 RandomAccessFile類(lèi)實(shí)例詳解
這篇文章主要介紹了Java中IO流 RandomAccessFile類(lèi)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05Springboot Retry組件@Recover失效問(wèn)題解決方法
在使用springboot的retry模塊時(shí),你是否出現(xiàn)過(guò)@Recover注解失效的問(wèn)題呢?不用擔(dān)心,這篇文章就來(lái)告訴你解決@Recover失效的辦法,需要的小伙伴可以參考一下2021-11-11