SpringMVC源码学习(七)---解析ModelAndView

2023-10-09 05:20

本文主要是介绍SpringMVC源码学习(七)---解析ModelAndView,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

解析ModelAndView

  • 一. RequestMappingHandlerAdapter类invokeHandlerMethod()方法
  • 二. invokeAndHandle() 方法
  • 三. handleReturnValue()方法
  • 四. ModelAndViewMethodReturnValueHandler类中的handleReturnValue() 方法
  • 五. RequestMappingHandlerAdapter类中的getModelAndView()方法
  • 六. DispatcherServlet类中的processDispatchResult()方法
  • 七. DispatcherServlet类中的processHandlerException()方法
  • 八. DispatcherServlet类中的render()方法
  • 九. ViewResolver和View接口
  • 十. DispatcherServlet类中的resolveViewName()方法
  • 十一. AbstractCachingViewResolver类中的resolveViewName()方法
  • 十二. UrlBasedViewResolver类中的createView()方法
  • 十三. UrlBasedViewResolver类中的loadView()方法
  • 十四. UrlBasedViewResolver类中的buildView()方法
  • 十五. UrlBasedViewResolver类中的applyLifecycleMethods()方法
  • 十六. AbstractView类中的render()方法
  • 十七. InternalResourceView类中的renderMergedOutputModel()方法

一. RequestMappingHandlerAdapter类invokeHandlerMethod()方法

在RequestMappingHandlerAdapter#invokeHandlerMethod()方法中

  1. 执行处理器方法(HandlerMethod)
  2. 封装并返回ModelAndView实例

在这里插入图片描述

	@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {//将Request和Response进行封装ServletWebRequest webRequest = new ServletWebRequest(request, response);try {//WebDataBinderFactory --> 工厂类,为目标对象创建一个WebDataBinder实例// 1.WebDataBinder继承了DataBinder类,为web请求提供了参数绑定服务WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);// 获取ModelFactory:// 2.ModelFactory可以协助控制器在调用方法之前初始化模型,并在调用之后更新模型ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);//  创建InvocableHandlerMethod实例,以及各个组件的配置;//  后面通过调用invokeAndHandle()方法执行处理器ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 4.尝试绑定参数、返回值解析器if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// 创建视图容器, 用于封装视图, 数据模型, 处理状态等信息ModelAndViewContainer mavContainer = new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);// 6.异步请求相关AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();if (logger.isDebugEnabled()) {logger.debug("Found concurrent result value [" + result + "]");}invocableMethod = invocableMethod.wrapConcurrentResult(result);}// 7.调用Controller中的具体方法并处理返回值 执行处理器invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}// 8.返回ModelAndView对象return getModelAndView(mavContainer, modelFactory, webRequest);}finally {// 完成请求后续处理,并将当前请求置为未激活webRequest.requestCompleted();}}

二. invokeAndHandle() 方法

在这里插入图片描述

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 1.调用Controller中的具体方法Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 2.设置返回状态码setResponseStatus(webRequest);// 3.当前请求无返回值或者返回值中包含错误,则将请求完成标识设置为true并返回if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}// 4.当前请求有返回值且无错误信息,则将请求完成标识设置为false,并继续处理当前请求mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {// 选取合适的HandlerMethodReturnValueHandler,并处理返回值this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);}throw ex;}}

三. handleReturnValue()方法

在这里插入图片描述

	@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {//根据返回值以及返回类型选择合适的返回值处理器, 对返回值进行解析HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}//解析结果handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}

四. ModelAndViewMethodReturnValueHandler类中的handleReturnValue() 方法

在这里插入图片描述

在这里插入图片描述

	@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 当前请求返回值为null,无需处理,并且要将当前请求标记已处理if (returnValue == null) {mavContainer.setRequestHandled(true);return;}// 处理引用视图ModelAndView mav = (ModelAndView) returnValue;if (mav.isReference()) {String viewName = mav.getViewName();mavContainer.setViewName(viewName);if (viewName != null && isRedirectViewName(viewName)) {mavContainer.setRedirectModelScenario(true);}}// 处理普通视图(即我们已经制定了具体的View视图,而无需通过视图解析器再次解析)else {View view = mav.getView();mavContainer.setView(view);if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {mavContainer.setRedirectModelScenario(true);}}// 设置返回状态mavContainer.setStatus(mav.getStatus());//设置数据ModelmavContainer.addAllAttributes(mav.getModel());}

五. RequestMappingHandlerAdapter类中的getModelAndView()方法

从ModelAndViewContainer容器中获取ModeAndView实例

在这里插入图片描述

	@Nullableprivate ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {// 1.更新模型modelFactory.updateModel(webRequest, mavContainer);if (mavContainer.isRequestHandled()) {return null;}// 2.获取ModelMap并创建ModelAndViewModelMap model = mavContainer.getModel();ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());// 3.处理引用类型视图和转发类型视图if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}if (model instanceof RedirectAttributes) {Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);if (request != null) {RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);}}return mav;}

六. DispatcherServlet类中的processDispatchResult()方法

解析ModelAndView

在这里插入图片描述

	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) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}//如果是自定义异常, 则获取异常处理器, 进行解析else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);//异常视图解析mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// 尝试解析视图和模型;// wasCleared:判断当前模型和视图是否已经被标识为清空,且当前视图和模型是否同时为空if (mv != null && !mv.wasCleared()) {// 解析并渲染视图render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isDebugEnabled()) {logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +"': assuming HandlerAdapter completed request handling");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {return;}// 处理注册的后置完成拦截器if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}}

processDispatchResult() 方法

  1. 如果出现异常,则解析异常视图
  2. 解析ModelAndView

七. DispatcherServlet类中的processHandlerException()方法

在这里插入图片描述

	@Nullableprotected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {ModelAndView exMv = null;if (this.handlerExceptionResolvers != null) {//遍历所有的异常解析器, 尝试对异常进行解析, 如果解析成功,跳出循焕for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {if (exMv.isEmpty()) {request.setAttribute(EXCEPTION_ATTRIBUTE, ex);return null;}//对于简单的错误模型,我们可能仍需要视图名称转换if (!exMv.hasView()) {String defaultViewName = getDefaultViewName(request);if (defaultViewName != null) {exMv.setViewName(defaultViewName);}}if (logger.isDebugEnabled()) {logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);}//设置错误请求的相关属性WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());return exMv;}throw ex;}

八. DispatcherServlet类中的render()方法

在这里插入图片描述

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {// 确定请求的区域设置并将其应用于响应。Locale locale =(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());response.setLocale(locale);View view;// 获取视图名String viewName = mv.getViewName();if (viewName != null) {view = resolveViewName(viewName, mv.getModelInternal(), locale, request);if (view == null) {throw new ServletException("Could not resolve view with name '" + mv.getViewName() +"' in servlet with name '" + getServletName() + "'");}}// 获取到视图名,再次判断当前ModelAndView对象中是否包含真正的View对象,// 因为接下来需要调用View对象的render方法else {view = mv.getView();if (view == null) {throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +"View object in servlet with name '" + getServletName() + "'");}}// Delegate to the View object for rendering.if (logger.isDebugEnabled()) {logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");}try {// 设置返回状态码if (mv.getStatus() != null) {response.setStatus(mv.getStatus().value());}// 调用View对象的render方法完成视图解析view.render(mv.getModelInternal(), request, response);}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +getServletName() + "'", ex);}throw ex;}}

ModelAndView视图解析可以分为两个重要步骤:

  1. 根据视图名称创建View实例
  2. 根据View实例渲染视图

九. ViewResolver和View接口

在这里插入图片描述

public interface ViewResolver {// 通过逻辑视图名和用户地区信息生成View对象@NullableView resolveViewName(String viewName, Locale locale) throws Exception;}

在这里插入图片描述

public interface View {String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";String PATH_VARIABLES = View.class.getName() + ".pathVariables";String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";//获取返回值的contentType@Nullabledefault String getContentType() {return null;}//通过用户提供的模型数据与视图信息渲染视图void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)throws Exception;}

十. DispatcherServlet类中的resolveViewName()方法

	//根据视图名称创建View实例@Nullableprotected View resolveViewName(String viewName, @Nullable Map<String, Object> model,Locale locale, HttpServletRequest request) throws Exception {if (this.viewResolvers != null) {for (ViewResolver viewResolver : this.viewResolvers) {//这里的视图解析器就是我们在配置文件中配置的那个View view = viewResolver.resolveViewName(viewName, locale);if (view != null) {return view;}}}return null;}

在这里插入图片描述

十一. AbstractCachingViewResolver类中的resolveViewName()方法

在这里插入图片描述

在这里插入图片描述

public View resolveViewName(String viewName, Locale locale) throws Exception {//判断缓存是否可用if (!isCache()) {//如果缓存不可用, 则直接创建视图return createView(viewName, locale);}else {//如果缓存可用, 则先尝试从缓存中获取//生成缓存KeyObject cacheKey = getCacheKey(viewName, locale);//尝试从缓存中获取视图View view = this.viewAccessCache.get(cacheKey);if (view == null) {//如果从缓存中获取视图失败, 则尝试从viewCreationCache缓存中获取synchronized (this.viewCreationCache) {//让子类创建View对象, 留给子类扩展[扩展开放,修改关闭原则]view = this.viewCreationCache.get(cacheKey);if (view == null) {// 这里cacheUnresolved指的是是否缓存默认的空视图,UNRESOLVED_VIEW是// 一个没有任何内容的Viewview = createView(viewName, locale);if (view == null && this.cacheUnresolved) {view = UNRESOLVED_VIEW;}if (view != null) {//将创建的view视图加入缓存this.viewAccessCache.put(cacheKey, view);this.viewCreationCache.put(cacheKey, view);if (logger.isTraceEnabled()) {logger.trace("Cached view [" + cacheKey + "]");}}}}}return (view != UNRESOLVED_VIEW ? view : null);}}

十二. UrlBasedViewResolver类中的createView()方法

在这里插入图片描述

	@Overrideprotected View createView(String viewName, Locale locale) throws Exception {if (!canHandle(viewName, locale)) {return null;}// 检查特殊的"redirect:"前缀 REDIRECT_URL_PREFIX = "redirect:"// 如果是以"redirect:" 开头, 说明该视图是重定向if (viewName.startsWith(REDIRECT_URL_PREFIX)) {String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());RedirectView view = new RedirectView(redirectUrl,isRedirectContextRelative(), isRedirectHttp10Compatible());String[] hosts = getRedirectHosts();if (hosts != null) {view.setHosts(hosts);}return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);}// 检查特殊的"forward:"前缀 FORWARD_URL_PREFIX = "forward:"// 如果是以"forward:" 开头, 说明该视图是请求转发if (viewName.startsWith(FORWARD_URL_PREFIX)) {String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());return new InternalResourceView(forwardUrl);}//如果是普通视图, 创建该视图视图return super.createView(viewName, locale);}

十三. UrlBasedViewResolver类中的loadView()方法

在这里插入图片描述
在这里插入图片描述

	@Overrideprotected View loadView(String viewName, Locale locale) throws Exception {// 使用逻辑视图名按照指定规则生成View对象AbstractUrlBasedView view = buildView(viewName);// 应用声明周期函数,也就是调用View对象的初始化函数和Spring用于切入bean创建的View result = applyLifecycleMethods(viewName, view);// 检查view的准确性,这里默认始终返回truereturn (view.checkResource(locale) ? result : null);}

十四. UrlBasedViewResolver类中的buildView()方法

在这里插入图片描述

	protected AbstractUrlBasedView buildView(String viewName) throws Exception {//对于InternalResourceViewResolver而言,其返回的View对象的具体类型是InternalResourceViewClass<?> viewClass = getViewClass();Assert.state(viewClass != null, "No view class");// 使用反射生成InternalResourceView对象实例AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);//根据前缀和后缀拼接视图路径信息view.setUrl(getPrefix() + viewName + getSuffix());// 设置View的contentType属性String contentType = getContentType();if (contentType != null) {view.setContentType(contentType);}// 设置contextAttribute和attributeMap等属性view.setRequestContextAttribute(getRequestContextAttribute());view.setAttributesMap(getAttributesMap());// pathVariables表示request请求url中的属性,这里主要是设置是否将这些属性暴露到视图中Boolean exposePathVariables = getExposePathVariables();if (exposePathVariables != null) {view.setExposePathVariables(exposePathVariables);}// 这里设置的是是否将Spring的bean暴露在视图中,以供给前端调用Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();if (exposeContextBeansAsAttributes != null) {view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);}// 设置需要暴露给前端页面的bean名称String[] exposedContextBeanNames = getExposedContextBeanNames();if (exposedContextBeanNames != null) {view.setExposedContextBeanNames(exposedContextBeanNames);}return view;}

十五. UrlBasedViewResolver类中的applyLifecycleMethods()方法

在这里插入图片描述

十六. AbstractView类中的render()方法

	protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {ApplicationContext context = getApplicationContext();if (context != null) {// 对生成的View对象应用初始化方法,主要包括InitializingBean.afterProperties()和一些// Processor,Aware方法Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName);if (initialized instanceof View) {return (View) initialized;}}return view;}

在这里插入图片描述

	@Overridepublic void render(@Nullable Map<String, ?> model, HttpServletRequest request,HttpServletResponse response) throws Exception {if (logger.isTraceEnabled()) {logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +" and static attributes " + this.staticAttributes);}// 合并为一个Map对象,以供给后面对视图的渲染使用Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);// 判断当前View对象的类型是否为文件下载类型,如果是文件下载类型,则设置response的prepareResponse(request, response);// 开始view视图渲染以及数据输出整理 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);}

十七. InternalResourceView类中的renderMergedOutputModel()方法

在这里插入图片描述

在这里插入图片描述

	@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// 将Model中的键值对数据全部写进RequestScope中exposeModelAsRequestAttributes(model, request);// 提供的一个hook方法,默认是空实现,用于用户进行request属性的自定义使用exposeHelpers(request);//确定请求分配器的路径String dispatcherPath = prepareForRendering(request, response);// 获取可应用于 forward/include 的RequestDispatcherRequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);if (rd == null) {throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +"]: Check that the corresponding file exists within your web application archive!");}// 判断当前是否为include请求,如果是,则调用RequestDispatcher.include()方法进行文件引入if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");}rd.include(request, response);}// 请求转发//使用forward跳转则后面的response输出则不会执行,而用include来跳转,//则include的servlet执行完后,再返回到原来的servlet执行response的输出(如果有)else {if (logger.isDebugEnabled()) {logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");}//如果当前不是include()请求,则直接使用forward请求将当前请求转发到目标文件路径中,从而渲染该视图rd.forward(request, response);}}

视图解析分析完成

这篇关于SpringMVC源码学习(七)---解析ModelAndView的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/170645

相关文章

Java中Map的五种遍历方式实现与对比

《Java中Map的五种遍历方式实现与对比》其实Map遍历藏着多种玩法,有的优雅简洁,有的性能拉满,今天咱们盘一盘这些进阶偏基础的遍历方式,告别重复又臃肿的代码,感兴趣的小伙伴可以了解下... 目录一、先搞懂:Map遍历的核心目标二、几种遍历方式的对比1. 传统EntrySet遍历(最通用)2. Lambd

Spring Boot 中 RestTemplate 的核心用法指南

《SpringBoot中RestTemplate的核心用法指南》本文详细介绍了RestTemplate的使用,包括基础用法、进阶配置技巧、实战案例以及最佳实践建议,通过一个腾讯地图路线规划的案... 目录一、环境准备二、基础用法全解析1. GET 请求的三种姿势2. POST 请求深度实践三、进阶配置技巧1

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

Spring Boot 处理带文件表单的方式汇总

《SpringBoot处理带文件表单的方式汇总》本文详细介绍了六种处理文件上传的方式,包括@RequestParam、@RequestPart、@ModelAttribute、@ModelAttr... 目录方式 1:@RequestParam接收文件后端代码前端代码特点方式 2:@RequestPart接

SpringBoot整合Zuul全过程

《SpringBoot整合Zuul全过程》Zuul网关是微服务架构中的重要组件,具备统一入口、鉴权校验、动态路由等功能,它通过配置文件进行灵活的路由和过滤器设置,支持Hystrix进行容错处理,还提供... 目录Zuul网关的作用Zuul网关的应用1、网关访问方式2、网关依赖注入3、网关启动器4、网关全局变

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso

SpringBoot的全局异常拦截实践过程

《SpringBoot的全局异常拦截实践过程》SpringBoot中使用@ControllerAdvice和@ExceptionHandler实现全局异常拦截,@RestControllerAdvic... 目录@RestControllerAdvice@ResponseStatus(...)@Except

Springboot配置文件相关语法及读取方式详解

《Springboot配置文件相关语法及读取方式详解》本文主要介绍了SpringBoot中的两种配置文件形式,即.properties文件和.yml/.yaml文件,详细讲解了这两种文件的语法和读取方... 目录配置文件的形式语法1、key-value形式2、数组形式读取方式1、通过@value注解2、通过

Java 接口定义变量的示例代码

《Java接口定义变量的示例代码》文章介绍了Java接口中的变量和方法,接口中的变量必须是publicstaticfinal的,用于定义常量,而方法默认是publicabstract的,必须由实现类... 在 Java 中,接口是一种抽象类型,用于定义类必须实现的方法。接口可以包含常量和方法,但不能包含实例