Spring MVC 源碼分析之 DispatcherServlet#processDispatchResult方法

前言:

前面的篇章我們分析了 Spring MVC 工作流程中的 HandlerMapping、HandlerAdapter 的適配過程、攔截器的工作流程,以及處理業務請求的過程,本篇我們分析一下處理完業務解析視圖的方法,也就是 DispatcherServlet#processDispatchResult 方法。

Spring MVC 知識傳送門:

詳解 Spring MVC(Spring MVC 簡介)

Spring MVC 初始化源碼分析

Spring MVC 工作流程源碼分析

Spring MVC 源碼分析之 DispatcherServlet#getHandler 方法

Spring MVC 源碼分析之 DispatcherServlet#getHandlerAdapter 方法

Spring MVC 源碼分析之 AbstractHandlerMethodAdapter#handle 方法

DispatcherServlet#processDispatchResult 方法源碼分析

DispatcherServlet#processDispatchResult 方法名直譯就是初始調度結果,其實就是解析 ModelAndView,源碼也很清晰,先是判斷是否有異常,有異常就解析異常視圖,否則就開始解析 ModelAndView 的流程。

//org.springframework.web.servlet.DispatcherServlet#processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {boolean errorView = false;//是否有異常if (exception != null) {//異常是否是模型視圖異常if (exception instanceof ModelAndViewDefiningException) {this.logger.debug("ModelAndViewDefiningException encountered", exception);//獲取異常視圖mv = ((ModelAndViewDefiningException)exception).getModelAndView();} else {//獲取異常解析器Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;//異常視圖解析mv = this.processHandlerException(request, response, handler, exception);//異常視圖不為空 賦值 errorView 為 trueerrorView = mv != null;}}//模型視圖是否為空 模型視圖是否被標識為清空if (mv != null && !mv.wasCleared()) {//解析并渲染視圖 重點關注this.render(mv, request, response);//errorView if (errorView) {//清除錯誤請求屬性WebUtils.clearErrorRequestAttributes(request);}} else if (this.logger.isTraceEnabled()) {this.logger.trace("No view rendering, null ModelAndView returned.");}//判斷是否是異步處理 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {//不是異步處理 if (mappedHandler != null) {//注冊后置攔截器mappedHandler.triggerAfterCompletion(request, response, (Exception)null);}}
}

DispatcherServlet#processHandlerException 方法源碼分析

DispatcherServlet#processHandlerException 方法的作用就是解析異常視圖,獲取所有異常解析器,遍歷解析視圖,解析到視圖就停止循環,設置視圖相關屬性返回。

//org.springframework.web.servlet.DispatcherServlet#processHandlerException
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {//刪除請求中的 HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 屬性request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);ModelAndView exMv = null;//異常解析器為空判斷if (this.handlerExceptionResolvers != null) {//迭代遍歷Iterator var6 = this.handlerExceptionResolvers.iterator();while(var6.hasNext()) {HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();//解析 exMv = resolver.resolveException(request, response, handler, ex);//解析到視圖 就跳出循環if (exMv != null) {break;}}}//視圖為 null 判斷if (exMv != null) {//視圖不為空if (exMv.isEmpty()) {//設置異常屬性request.setAttribute(EXCEPTION_ATTRIBUTE, ex);//返回return null;} else {//為空  判斷是否有視圖if (!exMv.hasView()) {//沒有視圖  獲取默認視圖名稱String defaultViewName = this.getDefaultViewName(request);//默認視圖名稱為空判斷if (defaultViewName != null) {//設置視圖名稱exMv.setViewName(defaultViewName);}}if (this.logger.isTraceEnabled()) {this.logger.trace("Using resolved error view: " + exMv, ex);} else if (this.logger.isDebugEnabled()) {this.logger.debug("Using resolved error view: " + exMv);}//設置請求相關的屬性WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName());//返回視圖return exMv;}} else {//視圖為空拋出異常throw ex;}
}

DispatcherServlet#render 方法源碼分析

DispatcherServlet#render 方法主要可以分為兩步,分別是創建視圖 View 和 解析渲染視圖 view.render。

//org.springframework.web.servlet.DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {//確認語言環境 常說的國際化Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();//為響應設置 Localeresponse.setLocale(locale);//獲取視圖名稱String viewName = mv.getViewName();View view;//視圖名稱為 null 判斷if (viewName != null) {//解析視圖名稱 重點關注view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);//視圖為空判斷if (view == null) {//視圖為空 拋出異常throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");}} else {//視圖名稱為 null//從模型視圖中獲取 視圖view = mv.getView();//視圖為 null 判斷  if (view == null) {//視圖為 null  拋出異常throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");}}if (this.logger.isTraceEnabled()) {this.logger.trace("Rendering view [" + view + "] ");}try {//模型視圖的狀態判斷if (mv.getStatus() != null) {//不為空 把模型視圖的狀態設置給 responseresponse.setStatus(mv.getStatus().value());}//視圖解析渲染 重點關注view.render(mv.getModelInternal(), request, response);} catch (Exception var8) {if (this.logger.isDebugEnabled()) {this.logger.debug("Error rendering view [" + view + "]", var8);}throw var8;}
}

DispatcherServlet#resolveViewName 方法源碼分析

DispatcherServlet#resolveViewName 方法的主要作用是通過遍歷視圖解析器獲取到視圖,視圖獲取成功則停止遍歷,返回視圖。

//org.springframework.web.servlet.DispatcherServlet#resolveViewName
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {//視圖解析器為空判斷 這里的視圖解析器 就是 DispatcherServlet.properties 中的視圖解析器  InternalResourceViewResolverif (this.viewResolvers != null) {//迭代遍歷視圖解析器Iterator var5 = this.viewResolvers.iterator();while(var5.hasNext()) {//獲取視圖解析器ViewResolver viewResolver = (ViewResolver)var5.next();//通過視圖名稱和語言環境 得到視圖 重點關注View view = viewResolver.resolveViewName(viewName, locale);//為null判斷if (view != null) {//返回視圖return view;}}}return null;
}

AbstractCachingViewResolver#resolveViewName 方法源碼分析

AbstractCachingViewResolver#resolveViewName 方法主要就是創建視圖,它會先判斷是否允許使用緩存,然后去創建視圖或者說從緩存中去獲取視圖,如果允許使用緩存,最終會把創建好的視圖加入到緩存中,我們重點關注 createView 即可。

@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {//是否允許緩存if (!this.isCache()) {//不允許 直接根據 viewName 和語言環境 創建一個 View 重點關注return this.createView(viewName, locale);} else {//允許緩存//根據 viewName 和語言環境 從緩存中獲取 viewObject cacheKey = this.getCacheKey(viewName, locale);View view = (View)this.viewAccessCache.get(cacheKey);//view 為 null 判斷if (view == null) {//緩存沒有獲取到  同步鎖 保證線程安全synchronized(this.viewCreationCache) {//再次去 viewCreationCache 緩存中獲取view = (View)this.viewCreationCache.get(cacheKey);//view 為空判斷if (view == null) {//還是為空 就創建一個 view  重點關注view = this.createView(viewName, locale);//為空判斷  cacheUnresolved 默認為 trueif (view == null && this.cacheUnresolved) {//賦值為空視圖 是一個沒有任何實現的視圖view = UNRESOLVED_VIEW;}//view 不為空 加入緩存if (view != null && this.cacheFilter.filter(view, viewName, locale)) {this.viewAccessCache.put(cacheKey, view);this.viewCreationCache.put(cacheKey, view);}}}} else if (this.logger.isTraceEnabled()) {this.logger.trace(formatKey(cacheKey) + "served from cache");}return view != UNRESOLVED_VIEW ? view : null;}
}

UrlBasedViewResolver#createView 方法源碼分析

UrlBasedViewResolver#createView 方法會判斷當前請求的類型,看是請求轉發、重定向、普通請求的哪一種,不同請求類型會有不同的創建 View 的方法。

protected View createView(String viewName, Locale locale) throws Exception {//UrlBasedViewResolver 是否可以處理if (!this.canHandle(viewName, locale)) {return null;} else {//轉發 urlString forwardUrl;//視圖名稱是否是否 redirect: 打頭if (viewName.startsWith("redirect:")) {//截取掉 redirect: 獲取真正的 urlforwardUrl = viewName.substring("redirect:".length());//創建一個重定向視圖RedirectView view = new RedirectView(forwardUrl, this.isRedirectContextRelative(), this.isRedirectHttp10Compatible());//獲取重定向 hostString[] hosts = this.getRedirectHosts();//為 null 判斷if (hosts != null) {//給視圖設置 view view.setHosts(hosts);}//應用給生命周期方法 重點關注return this.applyLifecycleMethods("redirect:", view);} else if (viewName.startsWith("forward:")) {//請求轉發 獲取轉發 urlforwardUrl = viewName.substring("forward:".length());//創建 InternalResourceViewInternalResourceView view = new InternalResourceView(forwardUrl);//應用給生命周期方法 其實就是使用 ApplicationContext 完成 view 的初始化 重點關注return this.applyLifecycleMethods("forward:", view);} else {//不是 redirect  也不是 forward  就是普通視圖 這里會調用 重點關注return super.createView(viewName, locale);}}
}

UrlBasedViewResolver#applyLifecycleMethods 方法源碼分析

UrlBasedViewResolver#applyLifecycleMethods 應用給生命周期方法,其實就是使用 ApplicationContext 完成 View 的初始化 。

//org.springframework.web.servlet.view.UrlBasedViewResolver#applyLifecycleMethods
protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {//獲取 ApplicationContextApplicationContext context = this.getApplicationContext();//為空 判斷if (context != null) {//獲取 beanFactory 完成 view 初始化Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName);//初始化完了 是否是 view 類型if (initialized instanceof View) {//返回 viewreturn (View)initialized;}}return view;
}

AbstractCachingViewResolver#createView 方法源碼分析

AbstractCachingViewResolver#createView 方法是普通請求創建 View 的方法,它調用的是父類 AbstractCachingViewResolver 的 createView 方法 ,該方法并沒有什么實際操作,接著調用了 loadView 方法,loadView 方法完成了 View 的創建及初始化、屬性見擦汗等。

//org.springframework.web.servlet.view.AbstractCachingViewResolver#createView
@Nullable
protected View createView(String viewName, Locale locale) throws Exception {return this.loadView(viewName, locale);
}//org.springframework.web.servlet.view.UrlBasedViewResolver#loadView 
protected View loadView(String viewName, Locale locale) throws Exception {//根據viewName 構建view  重點關注AbstractUrlBasedView view = this.buildView(viewName);//完成view 初始化View result = this.applyLifecycleMethods(viewName, view);//view.checkResource(locale) 檢查view屬性 默認返回 truereturn view.checkResource(locale) ? result : null;
}

AbstractCachingViewResolver#buildView 方法源碼分析

AbstractCachingViewResolver#buildView 方法獲取 View 的 Class 類型,反射創建了 View,并給 View 設置了各種屬性,是正真創建 View 的方法。

//org.springframework.web.servlet.view.UrlBasedViewResolver#buildView	
protected AbstractUrlBasedView buildView(String viewName) throws Exception {//獲取view 的classClass<?> viewClass = this.getViewClass();//斷言判斷 class 是否為 nullAssert.state(viewClass != null, "No view class");//根據 view 的 class 創建一個 viewAbstractUrlBasedView view = (AbstractUrlBasedView)BeanUtils.instantiateClass(viewClass);//根據前綴+名稱+后綴 拼接成視圖信息view.setUrl(this.getPrefix() + viewName + this.getSuffix());//view 設置 Attributeview.setAttributesMap(this.getAttributesMap());//獲取 contentTypeString contentType = this.getContentType();if (contentType != null) {//view 設置 contentTypeview.setContentType(contentType);}//獲取 RequestContextAttributeString requestContextAttribute = this.getRequestContextAttribute();if (requestContextAttribute != null) {//設置 RequestContextAttribute view.setRequestContextAttribute(requestContextAttribute);}//是否暴露 PathVariables  Request請求中的 url 屬性Boolean exposePathVariables = this.getExposePathVariables();if (exposePathVariables != null) {//是否將這些屬性暴露在視圖中view.setExposePathVariables(exposePathVariables);}//是否將 bean 暴露在視圖中Boolean exposeContextBeansAsAttributes = this.getExposeContextBeansAsAttributes();if (exposeContextBeansAsAttributes != null) {//設置到視圖中view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);}//需要暴露的 bean nameString[] exposedContextBeanNames = this.getExposedContextBeanNames();if (exposedContextBeanNames != null) {//設置到視圖中view.setExposedContextBeanNames(exposedContextBeanNames);}//返回 視圖return view;
}

AbstractView#render 方法源碼分析

上文說了 DispatcherServlet#render 方法主要可以分為兩步,分別是創建視圖 View 和 解析渲染視圖 view.render,創建 View 的步驟我們已經分析完了,我們來分析一下 view.render 方法,也就是 AbstractView#render 方法,該方法會將 Model、request、response 封裝成成一個 Map 對象給后面的視圖渲染使用,同時會對下載請求做一些處理,然后就開始進行視圖渲染。

//org.springframework.web.servlet.view.AbstractView#render
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {if (this.logger.isDebugEnabled()) {this.logger.debug("View " + this.formatViewName() + ", model " + (model != null ? model : Collections.emptyMap()) + (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));}//合并成一個 Map 對象Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);//響應前處理 其實就是判斷這是否是一個下載請求  默認不是下載請求this.prepareResponse(request, response);//渲染合并數據模型 重點關注this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
}

InternalResourceView#renderMergedOutputModel 方法源碼分析

InternalResourceView#renderMergedOutputModel 方法就是進行視圖渲染了,對于不同的請求類型有不同的渲染方式。

//org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {//將模型的數據全部寫去到 request 中 就是一個 request.setAttribute 的過程this.exposeModelAsRequestAttributes(model, request);//暴露一個助手 默認空實現 可以理解為一個擴展點this.exposeHelpers(request);//確定請求的路徑String dispatcherPath = this.prepareForRendering(request, response);//獲取可以用于 include、forward  的 RequestDispatcherRequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);//為 null 判斷if (rd == null) {//為 null 拋出異常throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl() + "]: Check that the corresponding file exists within your web application archive!");} else {//判斷當前是否為include請求//include方法使原先的 Servlet 和轉發到的 Servlet 都可以輸出響應信息 即原先的 Servlet 還可以繼續輸出響應信息if (this.useInclude(request, response)) {//是 include 請求//設置 ContentTyperesponse.setContentType(this.getContentType());if (this.logger.isDebugEnabled()) {this.logger.debug("Including [" + this.getUrl() + "]");}//include 包含的意思// 調用 include()方法進行文件引入rd.include(request, response);} else {if (this.logger.isDebugEnabled()) {this.logger.debug("Forwarding to [" + this.getUrl() + "]");}//請求轉發 直接使用 forward 請求將當前請求轉發到目標文件路徑中 渲染該視圖rd.forward(request, response);}}
}

至此,視圖渲染部分的核心流程已經分析完畢,其實整個流程就兩個要點,一個是通過視圖解析器去創建 View,一個就是 View 的渲染過程,視圖渲染分析完畢,也代表著整個 Spring MVC 的工作流程分析完畢,源碼告訴了我們 Spirng MVC 是怎樣去處理一個請求的,希望可以幫助到有需要的小伙伴。

歡迎提出建議及對錯誤的地方指出糾正。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/23175.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/23175.shtml
英文地址,請注明出處:http://en.pswp.cn/web/23175.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

提高篇(二):高級繪圖技巧:在Processing中創造精美圖形

提高篇(二):高級繪圖技巧:在Processing中創造精美圖形 引言 Processing不僅是一種編程語言,更是一個用于創意編程的強大工具。掌握高級繪圖技巧,可以讓你在藝術創作中如虎添翼。在本篇文章中,我們將探索貝塞爾曲線、樣條曲線、自定義形狀、圖形變換等高級繪圖技術,幫助…

tkinter+火山引擎+python實現語音識別聊天機器人

想要做一款能通過語音識別來聊天的智能機器人,首先需要能通過麥克風錄制語音進行識別轉換成文字,將文字發送給機器人得到聊天結果,并能將返回的文字轉換成語音進行合成,之后再通過本地播放語音實現語音交互。 架構: 實現步驟 一、本地錄音 本地錄音可以通過pyAudio庫實…

2024-06-05-記一次cnvd滲透

前言&#xff1a;挖src挖郁悶了&#xff0c;閑來無事選擇挖一個cnvd來練練手&#xff0c;本次的漏洞都沒啥難度&#xff0c;企查查資產過了5000萬 說一下cnvd證書的下放標準 對于中危及中危以上通用型漏洞&#xff08;CVSS2.0基準評分超過4.0分&#xff09;&#xff0c;以及涉…

Scaling vision Transformer 論文理解

Scaling vision Transformer 論文理解 1. 摘要2. 一些主要結論小結2.1 few shot transfer learning2.2 Pareto-front3. 討論3.1 Limitations3.2 社會作用4. 文章結論參考資料1. 摘要 Attention-based neural networks such as the Vision Transformer (ViT) have recently att…

valgrind-內存泄漏定位工具

1、前言 valgirnd 是一套開放源代碼的動態調試工具集合。能夠檢測內存管理錯誤&#xff0c;線程BUG等。valgirnd是由內核以及基于內核的其他調試工具組成。內核類似于一個框架&#xff0c;它模擬了一個cpu的環境&#xff0c;并提供服務給其他工具使用。而其他工具則類似于插件…

紅酒:紅酒保存中的光照與避免陽光直射

在紅酒保存中&#xff0c;光照是一個常常被忽視的因素。光照對紅酒的影響是不可小覷的&#xff0c;因為陽光中的紫外線會加速紅酒的氧化&#xff0c;導致其口感和品質的下降。因此&#xff0c;在保存云倉酒莊雷盛紅酒時&#xff0c;應特別注意避免陽光直射。 陽光直射對紅酒的影…

企業代碼簽名證書1300元

隨著手機和電腦等設備的普及&#xff0c;越來越多的開發者進入軟件行業&#xff0c;為了軟件的安全性、完整性和可信度&#xff0c;開發者往往會使用由正規CA認證機構頒發的代碼簽名證書對軟件代碼進行數字簽名&#xff0c;來標識軟件的來源和軟件開發者的真實身份。今天就隨SS…

AGI時代下,計算機專業出身的程序員該何去何從?

Easy&#xff1a;單獨開個貼說一下吧。 如果你計算機、軟件工程、通信、電子、網絡等相關專業本科畢業&#xff0c;也考慮前后端/測試崗&#xff0c;戳 技術大廠&#xff0c;6險1金&#xff0c;待遇不錯。 提前說明&#xff0c;一切以古代思維對AGI時代的推演&#xff0c;都是可…

博物館文物庫房管理軟件

博物館作為文化遺產的守護者和傳承者&#xff0c;承載著人類智慧與文明的結晶。在博物館的背后&#xff0c;一個龐大而嚴密的管理系統支撐著文物的保護與展示。而其中&#xff0c;文物庫房管理軟件的使用&#xff0c;無疑是一項重要的管理工具。 文物庫房管理軟件的功能具有多樣…

【CentOS 7】挑戰探索:在CentOS 7上實現Python 3.9的完美部署指南

【CentOS 7】挑戰探索&#xff1a;在CentOS 7上實現Python 3.9的完美部署指南 大家好 我是寸鐵&#x1f44a; 總結了一篇【CentOS 7】挑戰探索&#xff1a;在CentOS 7上實現Python 3.9的完美部署指南詳細步驟? 喜歡的小伙伴可以點點關注 &#x1f49d; 前言 此篇教程只適用于p…

【java前端課堂】05_類的多態

類的多態是面向對象編程的三大特性之一&#xff0c;與封裝和繼承并列。多態性允許我們將父對象設置為與其子對象相等的技術&#xff0c;讓我們可以把一個子對象當作父對象對待。在Java中&#xff0c;多態主要體現在以下三個方面&#xff1a; 方法的重載&#xff08;Overloadin…

【Mybatis】源碼分析-高級應用

1、Mybatis配置文件深入理解 1.2、動態SQL語句 Mybatis 的映射?件中&#xff0c;前?我們的 SQL 都是?較簡單的&#xff0c;有些時候業務邏輯復雜時&#xff0c;我們的 SQL是動態變化的&#xff0c;此時在前?的學習中我們的 SQL 就不能滿?要求了。 1.2.1、條件判斷 我們根…

技巧:合并ZIP分卷壓縮包

如果ZIP壓縮文件文件體積過大&#xff0c;大家可能會選擇“分卷壓縮”來壓縮ZIP文件&#xff0c;那么&#xff0c;如何合并zip分卷壓縮包呢&#xff1f;今天我們分享兩個ZIP分卷壓縮包合并的方法給大家。 方法一&#xff1a; 我們可以將分卷壓縮包&#xff0c;通過解壓的方式…

E10:系統彈窗提示

效果– window.WeFormSDK.showMessage("這是一個E10的提示", 3, 2); const onClickCreate () > console.log("create"); const onClickSave () > console.log("save"); const onClickCancel () > dialogComponent?.destroy(); co…

Java四舍五入保留小數

這里介紹兩種方法&#xff1a; package Book.jj.hh;import java.text.DecimalFormat; //使用DecimalFormat類 public class Demo1 {public static void main(String[] args) {double num 123.52631;DecimalFormat a new DecimalFormat("#.00"); //小數點后有幾個0…

SpringCloud Gateway基礎入門與使用實踐總結

官網文檔&#xff1a;點擊查看官網文檔 Cloud全家桶中有個很重要的組件就是網關&#xff0c;在1.x版本中都是采用的Zuul網關。但在2.x版本中&#xff0c;zuul的升級一直跳票&#xff0c;SpringCloud最后自己研發了一個網關替代Zuul&#xff0c;那就是SpringCloud Gateway一句話…

抖音賬號永久封號后強制注銷釋放實名!一分鐘教程方法公開

目前方法是可行的&#xff0c;不知道能保持多久&#xff01; 下載舊版本抖音&#xff1a;下載抖音6.8版本或5.8版本的老版本應用。 使用封禁手機號登錄&#xff1a;使用已被永久封禁的手機號登錄舊版本的抖音應用。 賬號注銷操作&#xff1a; 在設置中找到賬號與安全的選項。…

從零開始發布你的第一個npm插件包并在多項目中使用

引言 在開源的世界里&#xff0c;每個人都有機會成為貢獻者&#xff0c;甚至是創新的引領者。您是否有過這樣的想法&#xff1a;開發一個解決特定問題的小工具&#xff0c;讓她成為其他開發者手中的利器&#xff1f;今天&#xff0c;我們就來一場實戰訓練&#xff0c;學習如何將…

【ubuntu】增加samba服務和文件夾

發現ai -server的ubuntu機器無法git clone 下來github的文件所以 使用samba 連接到linux的文件夾proj然后在我的windows上git clone 即可。安裝samba Creating config file /etc/samba/smb.conf with new version Setting up libcephfs2 (17.2.7-0ubuntu0.22.04.1) ... Setting…

漏洞挖掘 | 驗證碼繞過

還是老規矩&#xff0c;開局一個登錄框&#xff0c;中途漏洞全靠舔&#xff0c;先來研究一下這個登錄窗口 很好&#xff0c;發現有驗證碼登錄&#xff0c;先測試測試能不能并發 看來沒有&#xff0c;只成功發送了兩條&#xff0c;再看看驗證碼是不是4位 很好&#xff0c;是4位。…