Spring MVC系列之九大核心组件

news2024/11/24 9:44:45

概述

Spring MVC是面试必问知识点其一,Spring MVC知识体系庞杂,有以下九大核心组件:

  • HandlerMapping
  • HandlerAdapter
  • HandlerExceptionResolver
  • ViewResolver
  • RequestToViewNameTranslator
  • LocaleResolver
  • ThemeResolver
  • MultipartResolver
  • FlashMapManager

另外,本文的源码学习基于Spring 6.1.5版本。

HandlerMapping

根据request找到相应的处理器Handler和Interceptors:

public interface HandlerMapping {
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

实现HandlerMapping的类,需要一并实现Order接口,order越小优先级越高,越先使用。

DispatcherServlet查找Handler是按顺序遍历所有的HandlerMapping,当找到一个HandlerMapping后立即停止查找并返回:

@Nullable
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;
}

更多分析,有待另起一文。

HandlerAdapter

HandlerAdapter也需要注册到容器里。接口有三个方法:

public interface HandlerAdapter {
	boolean supports(Object handler);
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	long getLastModified(HttpServletRequest request, Object handler);
}

handler方法使用具体Handler干活。选择使用哪个HandlerAdapter的过程在DispatcherServlet.getHandlerAdapter方法中,遍历所有的Adapter,然后检查哪个可以处理当前的Handler,找到第一个可以处理Handler的Adapter后就停止查找并将其返回。

更多分析,有待另起一文。

HandlerExceptionResolver

用于异常处理,具体来说,根据异常设置ModelAndView,之后再交给render方法进行渲染。只解析对请求做处理的过程中产生的异常,不解析渲染环节产生的异常。

接口提供一个方法,从异常解析出ModelAndView:

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

继承结构如图
在这里插入图片描述
类介绍:

  • AbstractHandlerMethodExceptionResolver:和其子类ExceptionHandlerExceptionResolver一起完成使用@ExceptionHandler注解的方法进行异常解析的功能
  • DefaultHandlerExceptionResolver:按不同类型分别对异常进行解析
  • ResponseStatusExceptionResolver:解析有@ResponseStatus注释类型的异常
  • SimpleMappingExceptionResolver:通过配置的异常类和view的对应关系来解析异常
  • HandlerExceptionResolverComposite:作为容器使用,可以封装别的Resolver

异常解析过程主要包含两部分内容:给ModelAndView设置相应内容、设置response的相关属性。当然还可能有一些辅助功能,如记录日志等,在自定义的ExceptionHandler里还可以做更多的事情。

AbstractHandlerExceptionResolver

所有直接解析异常类的父类,里面定义通用的解析流程,并使用模板模式,子类只需要覆盖相应的方法即可。核心方法是resolveException(),使用shouldApplyTo方法判断当前ExceptionResolver是否可以解析所传入处理器所抛出的异常(可以指定只能处理指定的处理器抛出的异常),如果不可以则返回null,交给下一个ExceptionResolver解析。如果可以,调用prepareResponse设置response,然后调用doResolveException实际解析异常,doResolveException是个模板方法,留给子类实现。最后调用logException方法打印日志。

shouldApplyTo方法,使用两个属性:mappedHandlers和mappedHandlerClasses,这两个属性可以在定义HandlerExceptionResolver时进行配置,用于指定可以解析处理器抛出的哪些异常,也就是如果设置这两个值中的一个,那么这个ExceptionResolver就只能解析所设置的处理器抛出的异常。mappedHandlers用于配置处理器的集合,mappedHandlerClasses用于配置处理器类型的集合。如果两个属性都没配置则将处理所有异常。

prepareResponse方法根据preventResponseCaching标示判断是否给response设置禁用缓存的属性,preventResponseCaching默认为false。

ExceptionHandlerExceptionResolver

DefaultHandlerExceptionResolver

根据异常类型的不同,使用不同的方法进行处理。核心方法doResolveException,里面是一大串if...else判断逻辑。异常类型如果是org.springframework.web.ErrorResponse接口的子类,并且是某些指定的异常类型,则返回空的ModelAndView; 其他异常类型,则调用handleErrorResponse方法。handleErrorResponse方法里给response添加header,sendError方法用于设置response的错误类型。异常类型如果不是是ErrorResponse接口的子类,同样需要判断异常类型,然后调用response.sendError()来设置错误码,并返回new ModelAndView()

题外话:jakarta.servlet.http.HttpServletResponse有sendError和setStatus方法,这里为啥使用sendError来设置错误码StatusCode?

sendError和setStatus方法的区别是前者会返回web.xml中定义的相应错误页面,后者只是设置了status而不会返回相应错误页面。

ResponseStatusExceptionResolver

用来解析添加@ResponseStatus注解的异常,doResolveException方法调用AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);找到注解,然后调用resolveResponseStatus方法进行解析,使用注解里的value和reason字段作为参数调用response.sendError方法。

SimpleMappingExceptionResolver

需要提前配置异常类和view的对应关系然后才能使用。还是看doResolveException方法,首先调用determineViewName方法根据异常找到显示异常的逻辑视图,然后调用determineStatusCode方法判断逻辑视图是否有对应的statusCode,如果有则调用applyStatus-CodeIfPossible方法设置到response,最后调用getModelAndView将异常和解析出的viewName封装成ModelAndView并返回。

大致过程就是遍历配置文件,然后调用getDepth查找,如果返回值大于等于0则说明可以匹配,而且如果有多个匹配项则选择最优的,选择方法是判断两项内容:匹配的深度;匹配的配置项文本的长度。深度越浅越好,配置的文本越长越好。深度是指如果匹配的是异常的父类而不是异常本身,那么深度就是异常本身到被匹配的父类之间的继承层数。

可配置选项:

  • exceptionMappings:用于配置异常类(字符串类型)和viewName的对应关系,异常类可以是异常(包含包名的完整名)的一部分,还可以是异常父类的一部分
  • excludedExceptions:用于配置不处理的异常
  • defaultErrorView:用于配置当无法从exceptionMappings中解析出视图时使用的默认视图
  • statusCodes:用于配置解析出的viewName和statusCode对应关系
  • defaultStatusCode:用于配置statusCodes中没有配置相应的viewName时使用的默认statusCode
  • exceptionAttribute:用于配置异常在Model中保存的参数名,默认为exception,如果为null,异常将不保存到Model中

ViewResolver

用于将String类型的视图名(也叫逻辑视图)和Locale解析为View类型的视图。源码:

public interface ViewResolver {
	@Nullable
	View resolveViewName(String viewName, Locale locale) throws Exception;
}

根据接口定义,解析视图需要两个参数:视图名和Locale。不过一般情况下只需要根据视图名找到对应的视图,然后渲染即可。解析过程主要做两件事:解析出使用的模板和视图的类型。

类的继承结构图
在这里插入图片描述
根据上图,可将ViewResolver分为四大类:AbstractCachingViewResolver、BeanNameViewResolver、ContentNegotiatingViewResolver和ViewResolverComposite。因为逻辑视图和视图的关系一般是不变的,不需要每次都重新解析,可缓存下来。故而,spring封装一个抽象类AbstractCachingViewResolver。

解释:

  • BeanNameViewResolver:根据viewName从spring容器中查找Bean,如果查找不到或者查到后不是View类型则返回null,否则返回容器中的bean
  • ViewResolverComposite:一个封装着多个ViewResolver的容器,解析视图时遍历封装着的ViewResolver具体解析,还对成员进行必要的初始化,包括对实现ApplicationContextAware接口的ViewResolver设置ApplicationContext、给实现ServletContextAware接口的ViewResolver设置ServletContext以及对实现InitializingBean接口的ViewResolver调用afterPropertiesSet方法。真正的解析过程是resolveViewName方法,别的方法几乎都是给所包含的ViewResolver做初始化的。
  • UrlBasedViewResolver:下面详细讲解
  • ResourceBundleViewResolver:spring-webmvc-5.3版本后被废弃。需要同时使用视图名和Locale来解析视图,需要将每一个视图名和对应的视图类型配置到相应的properties文件中,默认使用classpath下的views为baseName的配置文件,如views.propertiesviews_zh_CN.properties等,baseName和文件位置都可以设置。
  • XMLViewResolver:5.3版本后被废弃
  • InternalResourceViewResolver:只JSP类型的视图,下面详细讲解
  • FreeMarkerViewResolver:只解析FreeMarker
  • GroovyMarkupViewResolver:只解析Groovy

ViewResolver的使用需要注册到Spring MVC的容器里,默认使用InternalResourceViewResolver。

ContentNegotiatingViewResolver

AbstractCachingViewResolver

提供统一的缓存功能,当视图解析过一次就被缓存起来,直到缓存被删除前视图的解析都会自动从缓存中获取。

核心方法:

public View resolveViewName(String viewName, Locale locale) throws Exception {
	if (!isCache()) {
		return createView(viewName, locale);
	} else {
		Object cacheKey = getCacheKey(viewName, locale);
		View view = this.viewAccessCache.get(cacheKey);
		if (view == null) {
			synchronized (this.viewCreationCache) {
				view = this.viewCreationCache.get(cacheKey);
				if (view == null) {
					// Ask the subclass to create the View object.
					view = createView(viewName, locale);
					if (view == null && this.cacheUnresolved) {
						view = UNRESOLVED_VIEW;
					}
					if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
						this.viewAccessCache.put(cacheKey, view);
						this.viewCreationCache.put(cacheKey, view);
					}
				}
			}
		} else {
			// just logging
		}
		return (view != UNRESOLVED_VIEW ? view : null);
	}
}

createView内部直接调用loadView方法,而loadView是一个模板方法,留给子类实际创建视图,这也是子类解析视图的入口方法。createView之所以调用loadView而没有直接作为模板方法让子类使用是因为在loadView前可以统一做一些通用的解析,如果解析不到再交给loadView执行,这点在UrlBasedViewResolver中有具体的体现。

直接继承类有三个:ResourceBundleViewResolver、XmlViewResolver和UrlBasedViewResolver,前两个都已经在spring-webmvc-5.3版本里被废弃。

参数cacheLimit,用于设置最大缓存数的,当设置为0时不启用缓存,isCache就是判断它是否大于0。如果设置为一个大于0的数则它表示最多可以缓存视图的数量,如果往里面添加视图时超过这个数,那么最前面缓存的值将会删除。cacheLimit的默认值是1024,即最多可以缓存1024个视图。

使用两个Map做缓存,使用的最多的获取缓存是从前者获取的,而添加缓存会给两者同时添加,后者如果发现缓存数量已达到上限时会在删除自己最前面的缓存的同时也删除前者对应的缓存:

  • viewAccessCache:ConcurrentHashMap类型,内部使用细粒度锁,支持并发访问,效率非常高
  • viewCreationCache:LinkedHashMap类型,主要提供限制缓存最大数的功能,效率不如前者高。

LinkedHashMap中保存的值是有顺序的,可以自动删除最前面保存的值。具体来说,LinkedHashMap有个removeEldestEntry方法,如果这个方法返回true,Map中最前面添加的内容将被删除,它是在添加属性的put或putAll方法被调用后自动调用的:

private final Map<Object, View> viewCreationCache =
			new LinkedHashMap<>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
				@Override
				protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) {
					if (size() > getCacheLimit()) {
						viewAccessCache.remove(eldest.getKey());
						return true;
					} else {
						return false;
					}
				}
			};

UrlBasedViewResolver

UrlBasedViewResolver是所有直接将逻辑视图作为url查找模板文件的ViewResolver的基类,因为它设置了统一的查找模板的规则,所以它的子类只需要确定渲染方式也就是视图类型即可,它的每一个子类对应一种视图类型。

UrlBasedViewResolver里面重写父类的getCacheKey、createView和loadView三个方法。getCacheKey方法直接返回viewName,它用于父类AbstractCachingViewResolver中设置缓存的key,AbstractCachingViewResolver中使用的是viewName + '_' + locale,即UrlBasedViewResolver的缓存中key没有使用Locale,说明UrlBasedViewResolver不支持Locale。

createView方法,首先检查是否可以解析传入的逻辑视图,如果不可以则返回null让别的ViewResolver解析,接着分别检查是不是redirect视图或者forward视图,检查的方法是看是不是以redirect:forward:开头,如果是则返回相应视图,如果都不是则交给父类的createView,父类中又调用loadView。

其实这里是为所有UrlBasedViewResolver子类解析器统一添加了检查是否支持传入的逻辑视图和传入的逻辑视图是不是redirect或者forward视图的功能。

loadView方法共执行三句代码:

  • 使用buildView方法创建View;
  • 使用applyLifecycleMethods方法对创建的View初始化
  • 检查view对应的模板是否存在,如果存在则将初始化的视图返回,否则返回null交给下一个ViewResolver处理

buildView是一个核心方法:

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
	AbstractUrlBasedView view = instantiateView();
	// 添加前缀、后缀
	view.setUrl(getPrefix() + viewName + getSuffix());
	view.setAttributesMap(getAttributesMap());
	// 设置contentType
	String contentType = getContentType();
	if (contentType != null) {
		view.setContentType(contentType);
	}
	// 设置requestContextAttribute
	String requestContextAttribute = getRequestContextAttribute();
	if (requestContextAttribute != null) {
		view.setRequestContextAttribute(requestContextAttribute);
	}
	// 设置exposePathVariables,配置view是否可以使用@PathVariables里的参数
	Boolean exposePathVariables = getExposePathVariables();
	if (exposePathVariables != null) {
		view.setExposePathVariables(exposePathVariables);
	}
	// 设置exposeContextBeansAsAttributes,配置view是否可以使用容器里注册的Bean
	Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
	if (exposeContextBeansAsAttributes != null) {
		view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
	}
	// 设置exposedContextBeanNames,配置view可以使用容器里的哪些Bean,和上一个配置相关联
	String[] exposedContextBeanNames = getExposedContextBeanNames();
	if (exposedContextBeanNames != null) {
		view.setExposedContextBeanNames(exposedContextBeanNames);
	}
	return view;
}

getViewClass()返回其中的viewClass属性,代表View的视图类型,可以在子类通过setViewClass方法进行设置。requiredViewClass方法,用于在设置视图时判断所设置的类型是否支持,在UrlBasedViewResolver中默认返回AbstractUrlBasedView类型,requiredViewClass使用在设置视图的setViewClass方法中,

UrlBasedViewResolver的子类主要做三件事:

  • 通过重写requiredViewClass方法修改必须符合的视图类型的值;
  • 使用setViewClass方法设置所用的视图类型;
  • 给创建出来的视图设置一些属性。

InternalResourceViewResolver

在构造方法中设置viewClass,在buildView中对父类创建的View设置一些属性,requiredViewClass方法返回InternalResourceView类型。

buildView方法中给创建出来的View设置的alwaysInclude用于标示是否在可以使用forward的情况下也强制使用include,默认为false,可以在注册解析器时配置。setPreventDispatchLoop(true)用于阻止循环调用,也就是请求处理完成后又转发回原来使用的处理器的情况。

AbstractTemplateViewResolver

是所有模板类型ViewResolver的父类,主要对创建的View设置一些属性,并将requiredViewClass的返回值设置为AbstractTemplateView类型。其继承类主要就两个:FreeMarkerViewResolver和GroovyMarkupViewResolver。另外,在Spring Boot里,还有个MustacheViewResolver。

AbstractTemplateViewResolver定义5个属性:

  • exposeRequestAttributes:是否将所有RequestAttributes暴露给view使用,默认为false
  • allowRequestOverride:当RequestAttributes中存在Model中同名的参数时,是否允许使用RequestAttributes中的值将Model中的值覆盖,默认为false
  • exposeSessionAttributes:是否将所有SessionAttributes暴露给view使用,默认为false
  • allowSessionOverride:当SessionAttributes中存在Model中同名的参数时,是否允许使用RequestAttributes中的值将Model中的值覆盖,默认为false
  • exposeSpringMacroHelpers:是否将RequestContext暴露给view为spring的宏使用,默认为true

RequestToViewNameTranslator

从request获取viewName,具体来说,可在处理器返回的view为空时使用它根据request获取viewName:

public interface RequestToViewNameTranslator {
    @Nullable
    String getViewName(HttpServletRequest request) throws Exception;
}

只能配置一个,所有request到ViewName的转换规则都要在一个Translator里面全部实现。

Spring仅有一个实现类DefaultRequestToViewNameTranslator,两个核心方法:

// 从request获取lookupPath后使用transformPath对其进行处理后加上前缀后缀返回
@Override
public String getViewName(HttpServletRequest request) {
	String path = ServletRequestPathUtils.getCachedPathValue(request);
	return (this.prefix + transformPath(path) + this.suffix);
}
// 根据四个属性字段进行不同的处理
@Nullable
protected String transformPath(String lookupPath) {
	String path = lookupPath;
	if (this.stripLeadingSlash && path.startsWith(SLASH)) {
		path = path.substring(1);
	}
	if (this.stripTrailingSlash && path.endsWith(SLASH)) {
		path = path.substring(0, path.length() - 1);
	}
	if (this.stripExtension) {
		path = StringUtils.stripFilenameExtension(path);
	}
	if (!SLASH.equals(this.separator)) {
		path = StringUtils.replace(path, SLASH, this.separator);
	}
	return path;
}

四个属性:

  • stripLeadingSlash:如果最前面的字符为/是否将其去掉
  • stripTrailingSlash:如果最后一个字符为/是否将其去掉
  • stripExtension:是否需要去掉扩展名
  • separator:如果分隔符与/不同,则用分隔符替换掉/

LocaleResolver

用于从request解析出Locale:

public interface LocaleResolver {
    Locale resolveLocale(HttpServletRequest request);
    void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}

继承结构图如下
在这里插入图片描述
简介:

  • LocaleContextResolver:接口,增加获取和设置LocaleContext的能力;
  • AbstractLocaleResolver:抽象类,添加默认Locale的defaultLocale属性,及getter/setter方法;
  • AbstractLocaleContextResolver:抽象类,添加对TimeZone时区的支持
  • AcceptHeaderLocaleResolver:直接使用Header里的acceptlanguage值,不可以在程序中修改;
  • FixedLocaleResolver:解析出固定的Locale,即在创建时就设置好确定的Locale,之后无法修改;
  • SessionLocaleResolver:将Locale保存到Session中,可修改;
  • CookieLocaleResolver:将Locale保存到Cookie中,可修改。

Spring MVC中主要在两个地方用到Locale:

  • ViewResolver解析视图时
  • 使用到国际化资源或主题时

如何修改Locale呢?Spring提供org.springframework.web.servlet.i18n.LocaleChangeInterceptor,配置成拦截器即可。

ThemeResolver

解析主题,源码:

@Deprecated(since = "6.0")
public interface ThemeResolver {
    String resolveThemeName(HttpServletRequest request);
    void setThemeName(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName);
}

6.0版本已被标记为废弃@Deprecated。

MultipartResolver

public interface MultipartResolver {
    boolean isMultipart(HttpServletRequest request);
    MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
    void cleanupMultipart(MultipartHttpServletRequest request);
}

在Spring体系下目前仅有一个实现类StandardServletMultipartResolver,提供resolveMultipart方法将当前请求封装成StandardMultipartHttpServletRequest并返回;cleanupMultipart方法删除缓存。

如何封装StandardMultipartHttpServletRequest,需要看一下构造方法:

public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing) throws MultipartException {
	super(request);
	if (!lazyParsing) {
		parseRequest(request);
	}
}
private void parseRequest(HttpServletRequest request) {
	try {
		Collection<Part> parts = request.getParts();
		this.multipartParameterNames = new LinkedHashSet<>(parts.size());
		MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
		for (Part part : parts) {
			String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
			ContentDisposition disposition = ContentDisposition.parse(headerValue);
			String filename = disposition.getFilename();
			if (filename != null) {
				files.add(part.getName(), new StandardMultipartFile(part, filename));
			} else {
				this.multipartParameterNames.add(part.getName());
			}
		}
		setMultipartFiles(files);
	} catch (Throwable ex) {
		handleParseFailure(ex);
	}
}

通过request的getParts方法获取所有Part,然后使用它们创建出File并保存到对应的属性。

如果项目不使用Spring自带的StandardServletMultipartResolver,则可以考虑使用apache commons-fileupload,即使用CommonsMultipartResolver,不展开详述。

FlashMapManager

FlashMap用于在redirect中传递参数,FlashMapManager管理FlashMap:

public interface FlashMapManager {
    @Nullable
    FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
    void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}

方法retrieveAndUpdate用于恢复参数,并将恢复过的和超时的参数从保存介质中删除;saveOutputFlashMap用于将参数保存起来。默认实现org.springframework.web.servlet.support.SessionFlashMapManager,将参数保存到session中。

整个redirect参数通过FlashMap传递的过程分三步:

  • 在处理器中将需要传递的参数设置到outputFlashMap中,可以直接使用request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE)拿到outputFlashMap,将参数put进去,也可以将需要传递的参数设置到处理器的RedirectAttributes类型的参数中,当处理器处理完请求时,如果是redirect类型的返回值RequestMappingHandlerAdapter会将其设置到outputFlashMap中
  • 在RedirectView的renderMergedOutputModel方法中调用FlashMapManager.saveOutputFlashMap方法,将outputFlashMap中的参数设置到Session中
  • 请求redirect后DispatcherServlet的doServic会调用FlashMapManager的retrieveAndUpdate方法从Session中获取inputFlashMap并设置到Request的属性中备用,同时从Session中删除。

类继承关系图:
在这里插入图片描述
具体实现类是SessionFlashMapManager,在Session中保存的是List<FlashMap>类型,即一个Session可保存多个FlashMap,一个FlashMap保存着一套Redirect转发所传递的参数。FlashMap继承自HashMap,并且支持设置有效期,能保存Redirect后的目标路径和通过url传递的参数,这两项内容主要用来从Session保存的多个FlashMap中查找当前请求的FlashMap。

取回FlashMap的过程:

  • 每次取回FlashMap时都会对所有保存的参数检查是否过期,而且只有在取回的时候才会检查,也就是说虽然设置的过期时间是180秒,但是如果在更长的时间内没有过取回FlashMap的操作,那么即使过期也不会被删除;
  • 使用request匹配FlashMap是在所有的FlashMap中进行的,也就是说其结果可能包含已经过期但还没有被删除的。不过这并不会造成什么影响,因为这里的FlashMap正常情况下只有一个,参数使用完后会自动删除,所以全部遍历也不会有什么问题,另外FlashMap主要用在redirect中,这个过程是不需要人工参与的,时间一般会很短,而且即使过期了也不会有什么影响。

参考

  • 看透Spring MVC:源码分析与实践

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1626986.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode 面试题 08.02——迷路的机器人

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 此题就是一个典型的图搜索题&#xff0c;一种就是广度优先搜索&#xff0c;一种就是深度优先搜索。 3. 代码实现 class Solution { public:vector<vector<int>> pathWithObstacles(vector<vecto…

Go源码--Strconv库

简介 Strconv 库是一些跨类型的转换函数集合&#xff0c;大家应该很熟悉。源码没有什么难点&#xff0c;主要是面试题有可能会出这种类型的&#xff0c;所以简单介绍下&#xff0c;主要介绍 以下两种常用转换&#xff0c;其他的没细研究&#xff0c;感兴趣的可以看看。 Strco…

[笔试训练](八)

目录 022&#xff1a;求最小公倍数 023&#xff1a;数组中的最长连续子序列 024&#xff1a;字母收集 022&#xff1a;求最小公倍数 求最小公倍数_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 求最小公倍数公式&#xff1a;lcm(a,b)a*b/gcd(a,b)&am…

网页提示语闪太快的定位问题(selenium)

selenium UI自动化时&#xff0c;提示语闪太快&#xff0c;导致无法获取元素的问题 解决办法 步骤一&#xff1a; F12---》控制台输入debugger 步骤二&#xff1a;对于需要定位的部分&#xff0c;在控制台的debugger处回车&#xff0c;可以定住页面 步骤三&#xff1a;正常定…

win11 安装qt5.14.2 、qtcreator、vs编译器 。用最小安装进行 c++开发qt界面

系统 &#xff1a;win11 一、安装vs生成工具 &#xff0c;安装编译器 下载visualstudio tools 生成工具&#xff1a; 安装编译器 和 windows sdk&#xff1a; 安装debug 调试器&#xff1a; 二、Qt5.14.2下载 下载链接: Index of /archive/qt/5.14/5.14.2 安装qt 三、配置QT/…

MVP+敏捷开发

MVP敏捷开发 1. 什么是敏捷开发&#xff1f; 敏捷开发是一种软件开发方法论&#xff0c;旨在通过迭代、自组织的团队和持续反馈&#xff0c;快速响应需求变化并交付高质量的软件。相较于传统的瀑布模型&#xff0c;敏捷开发强调灵活性、适应性和与客户的紧密合作。敏捷开发方…

Llama网络结构介绍

LLaMA现在已经是开源社区里炙手可热的模型了&#xff0c;但是原文中仅仅介绍了其和标准Transformer的差别&#xff0c;并没有一个全局的模型介绍。因此打算写篇文章&#xff0c;争取让读者不参考任何其他资料把LLaMA的模型搞懂。 结构 如图所示为LLaMA的示意图&#xff0c;由…

解决ax = Axes3D(fig2)pycharm画3d图空白不显示问题

明明代码运行正确&#xff0c;却总是显示不出来 绘制出来的也是空白 改一下代码就好了 ax Axes3D(fig2) #原来代码 ax fig2.add_axes(Axes3D(fig2)) #改后代码 修改过后就可以显示了

【Jenkins】持续集成与交付 (三):有关报错解决(Jenkins (2.387.3) or higher required)

【Jenkins】持续集成与交付 (三):有关报错解决Jenkins (2.387.3) or higher required 一、Jenkins主页报错二、安装Jenkins插件报错三、解决过程(解压替换jenkins.war)四、重新访问登录💖The Begin💖点点关注,收藏不迷路💖 一、Jenkins主页报错 New version of J…

【nodejs状态库mobx之computed规则】

The above example nicely demonstrates the benefits of a computed value, it acts as a caching point. Even though we change the amount, and this will trigger the total to recompute, it won’t trigger the autorun, as total will detect its output hasn’t been …

一分钟了解期权合约转仓交易的流程

期权合约转仓交易的流程 期权合约转仓交易是指在期权交易过程中&#xff0c;交易者平掉手中现有的仓位&#xff0c;并选择更高或更低的行权价格&#xff0c;进行买入或卖出开仓的交易方法。转仓交易具有多个优势&#xff0c;包括降低风险、锁定利润、提高资金使用效率以及增加…

关于Github默认分支main和master以及如何在git init时指定默认分支

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

内置对象部分

一&#xff0c;内置对象 二&#xff0c;math对象 不是构造函数&#xff0c;不需要new来调用&#xff0c;而是直接使用里面的属性和方法即可 1.随机方法random 返回一个随机的小数 [0,1&#xff09; 2.日起格式化 返回的月份会小一&#xff0c;记得加一 周一返回1&#xff…

什么是储能电站的一次设备与二次设备?

随着国家政策导向和扶持&#xff0c;储能电站的建设&#xff0c;在各地均稳步推进&#xff0c;储能电站的设备主要分一次设备和二次设备两种&#xff0c;下面分别介绍这两方面内容&#xff1a; 储能电站一次设备 一次设备是储能电站的电路基础设施&#xff0c;包含变压器、主…

提升你的C编程技能:使用cURL下载Kwai视频

概述 本文将介绍如何利用C语言以及cURL库来实现Kwai视频的下载。cURL作为一个功能强大的网络传输工具&#xff0c;能够在C语言环境下轻松地实现数据的传输。我们还将探讨如何运用代理IP技术&#xff0c;提升爬虫的匿名性和效率&#xff0c;以适应Kwai视频平台的发展趋势。 正…

代码随想录算法训练营day40

题目&#xff1a;343. 整数拆分、96.不同的二叉搜索树 参考链接&#xff1a;代码随想录 343. 整数拆分 思路&#xff1a;五部曲来走。dp数组&#xff0c;dp[i]用于记录拆i得到的最大乘积和&#xff0c;我们要求的也就是dp[n]&#xff1b;递推公式&#xff0c;我们想拆分i&am…

简单把玩下SpringAI

Hello大家好&#xff0c;今天写一些不烧脑的文章&#xff0c;我们来体验一下Spring的新框架Spring AI&#xff0c;只是简单玩玩不深入&#x1f60f; Spring AI 简介 Spring AI是为了简化人工智能相关应用程序的开发Spring AI的诞生&#xff0c;灵感来自于LangChain等项目 Oll…

江苏开放大学2024年春《会计基础 050266》第二次任务:第二次过程性考核参考答案

电大搜题 多的用不完的题库&#xff0c;支持文字、图片搜题&#xff0c;包含国家开放大学、广东开放大学、超星等等多个平台题库&#xff0c;考试作业必备神器。 公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#…

Windows常见问题(技巧)总结

目录 问题Windows中更改快捷方式图标windows 中网速很慢,如何解决?因为Authenticated Users从其父系继承承权限&#xff0c;你无法删除此对象.Windows下的照片软件在哪个文件夹下?如何批量更改文件名?Windows 电脑pagefile.sys是什么文件,可以删除吗?U盘中打开文件时提示&a…

平衡小车的控制算法--结合自动控制原理学习

单纯的去看自控原理&#xff0c;很多概念有点抽象&#xff0c;最好找些应用去理解相关的概念&#xff0c;就找了实验室的一个平衡小车作为应用&#xff0c;不过主要根据小车去跑matlab去验证一些控制算法。结合台湾国立交通大学林沛群的自控线上课的总结 一、自控原理重要概念 …