SpringMVC源码-处理器适配器HandlerAdapter

因为定义controller的方式有三种,每种不同的方式调用的方法不同,尤其是注解修饰的 方法名是自定义的 因此需要通过适配器模式来调用方法执行

initStrategies进行适配器的初始化
在这里插入图片描述
在这里插入图片描述

处理器适配器一共有如下四种:

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\org.springframework.web.servlet.function.support.HandlerFunctionAdapter

HandlerAdapter对应着四种适配器

接口在请求执行获取到适配器的代码执行逻辑是在这:

getHandlerAdapter:1378, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1103, DispatcherServlet (org.springframework.web.servlet)
doService:1006, DispatcherServlet (org.springframework.web.servlet)
processRequest:1085, FrameworkServlet (org.springframework.web.servlet)
doPost:971, FrameworkServlet (org.springframework.web.servlet)
service:681, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

在这里插入图片描述
handlerAdapters取值

	/*** 处理器适配器,用于执行处理器** List of HandlerAdapters used by this servlet. */@Nullableprivate List<HandlerAdapter> handlerAdapters;

在这里插入图片描述
HttpRequestHandlerAdapter

/**适配器使用普通的{@link org.springframework.web。HttpRequestHandler}接口与通用的{@link org.springframework.web.servlet.DispatcherServlet}。支持实现{@link LastModified}接口的处理程序。<p>这是一个SPI类,不直接由应用程序代码使用。public class  HttpRequestHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {// 判断是 HttpRequestHandler 类型return (handler instanceof HttpRequestHandler);}@Override@Nullablepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {// HttpRequestHandler 类型的调用((HttpRequestHandler) handler).handleRequest(request, response);return null;}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {// 处理器实现了 LastModified 接口的情况下if (handler instanceof LastModified) {return ((LastModified) handler).getLastModified(request);}return -1L;}}

SimpleControllerHandlerAdapter


public class SimpleControllerHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {// 判断是 Controller 类型return (handler instanceof Controller);}@Override@Nullablepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {// Controller 类型的调用return ((Controller) handler).handleRequest(request, response);}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {// 处理器实现了 LastModified 接口的情况下if (handler instanceof LastModified) {return ((LastModified) handler).getLastModified(request);}return -1L;}}

RequestMappingHandlerAdapter


public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {/*** MethodFilter that matches {@link InitBinder @InitBinder} methods.*/public static final MethodFilter INIT_BINDER_METHODS = method ->AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);/*** MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.*/public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));@Nullableprivate List<HandlerMethodArgumentResolver> customArgumentResolvers;// 用于给处理器方法和注释了@ModelAttribute的方法设置参数@Nullableprivate HandlerMethodArgumentResolverComposite argumentResolvers;// 用于给注释了@initBinder的方法设置参数@Nullableprivate HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;@Nullableprivate List<HandlerMethodReturnValueHandler> customReturnValueHandlers;// 用于将处理器的返回值处理成ModelAndView的类型@Nullableprivate HandlerMethodReturnValueHandlerComposite returnValueHandlers;@Nullableprivate List<ModelAndViewResolver> modelAndViewResolvers;private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();private List<HttpMessageConverter<?>> messageConverters;// 用于保存实现了ResponseBodyAdvice接口,可以修改返回的ResponseBody的类private final List<Object> requestResponseBodyAdvice = new ArrayList<>();@Nullableprivate WebBindingInitializer webBindingInitializer;private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");@Nullableprivate Long asyncRequestTimeout;private CallableProcessingInterceptor[] callableInterceptors = new CallableProcessingInterceptor[0];private DeferredResultProcessingInterceptor[] deferredResultInterceptors = new DeferredResultProcessingInterceptor[0];private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();private boolean ignoreDefaultModelOnRedirect = false;private int cacheSecondsForSessionAttributeHandlers = 0;/*** 是否对相同 Session 加锁*/private boolean synchronizeOnSession = false;private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();@Nullableprivate ConfigurableBeanFactory beanFactory;// ========== 缓存 ==========private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);// 用于缓存@controllerAdvice注释的类里面注释了@InitBinder的方法private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);// 用于缓存@ControllerADvice注释的类里注释了@ModelAttribute的方法private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();public RequestMappingHandlerAdapter() {// 初始化 messageConvertersthis.messageConverters = new ArrayList<>(4);this.messageConverters.add(new ByteArrayHttpMessageConverter());this.messageConverters.add(new StringHttpMessageConverter());try {this.messageConverters.add(new SourceHttpMessageConverter<>());}catch (Error err) {// Ignore when no TransformerFactory implementation is available}this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());}/*** Provide resolvers for custom argument types. Custom resolvers are ordered* after built-in ones. To override the built-in support for argument* resolution use {@link #setArgumentResolvers} instead.*/public void setCustomArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {this.customArgumentResolvers = argumentResolvers;}/*** Return the custom argument resolvers, or {@code null}.*/@Nullablepublic List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {return this.customArgumentResolvers;}/*** Configure the complete list of supported argument types thus overriding* the resolvers that would otherwise be configured by default.*/public void setArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {if (argumentResolvers == null) {this.argumentResolvers = null;}else {this.argumentResolvers = new HandlerMethodArgumentResolverComposite();this.argumentResolvers.addResolvers(argumentResolvers);}}/*** Return the configured argument resolvers, or possibly {@code null} if* not initialized yet via {@link #afterPropertiesSet()}.*/@Nullablepublic List<HandlerMethodArgumentResolver> getArgumentResolvers() {return (this.argumentResolvers != null ? this.argumentResolvers.getResolvers() : null);}/*** Configure the supported argument types in {@code @InitBinder} methods.*/public void setInitBinderArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {if (argumentResolvers == null) {this.initBinderArgumentResolvers = null;}else {this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite();this.initBinderArgumentResolvers.addResolvers(argumentResolvers);}}/*** Return the argument resolvers for {@code @InitBinder} methods, or possibly* {@code null} if not initialized yet via {@link #afterPropertiesSet()}.*/@Nullablepublic List<HandlerMethodArgumentResolver> getInitBinderArgumentResolvers() {return (this.initBinderArgumentResolvers != null ? this.initBinderArgumentResolvers.getResolvers() : null);}/*** Provide handlers for custom return value types. Custom handlers are* ordered after built-in ones. To override the built-in support for* return value handling use {@link #setReturnValueHandlers}.*/public void setCustomReturnValueHandlers(@Nullable List<HandlerMethodReturnValueHandler> returnValueHandlers) {this.customReturnValueHandlers = returnValueHandlers;}/*** Return the custom return value handlers, or {@code null}.*/@Nullablepublic List<HandlerMethodReturnValueHandler> getCustomReturnValueHandlers() {return this.customReturnValueHandlers;}/*** Configure the complete list of supported return value types thus* overriding handlers that would otherwise be configured by default.*/public void setReturnValueHandlers(@Nullable List<HandlerMethodReturnValueHandler> returnValueHandlers) {if (returnValueHandlers == null) {this.returnValueHandlers = null;}else {this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();this.returnValueHandlers.addHandlers(returnValueHandlers);}}/*** Return the configured handlers, or possibly {@code null} if not* initialized yet via {@link #afterPropertiesSet()}.*/@Nullablepublic List<HandlerMethodReturnValueHandler> getReturnValueHandlers() {return (this.returnValueHandlers != null ? this.returnValueHandlers.getHandlers() : null);}/*** Provide custom {@link ModelAndViewResolver ModelAndViewResolvers}.* <p><strong>Note:</strong> This method is available for backwards* compatibility only. However, it is recommended to re-write a* {@code ModelAndViewResolver} as {@link HandlerMethodReturnValueHandler}.* An adapter between the two interfaces is not possible since the* {@link HandlerMethodReturnValueHandler#supportsReturnType} method* cannot be implemented. Hence {@code ModelAndViewResolver}s are limited* to always being invoked at the end after all other return value* handlers have been given a chance.* <p>A {@code HandlerMethodReturnValueHandler} provides better access to* the return type and controller method information and can be ordered* freely relative to other return value handlers.*/public void setModelAndViewResolvers(@Nullable List<ModelAndViewResolver> modelAndViewResolvers) {this.modelAndViewResolvers = modelAndViewResolvers;}/*** Return the configured {@link ModelAndViewResolver ModelAndViewResolvers}, or {@code null}.*/@Nullablepublic List<ModelAndViewResolver> getModelAndViewResolvers() {return this.modelAndViewResolvers;}/*** Set the {@link ContentNegotiationManager} to use to determine requested media types.* If not set, the default constructor is used.*/public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {this.contentNegotiationManager = contentNegotiationManager;}/*** Provide the converters to use in argument resolvers and return value* handlers that support reading and/or writing to the body of the* request and response.*/public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {this.messageConverters = messageConverters;}/*** Return the configured message body converters.*/public List<HttpMessageConverter<?>> getMessageConverters() {return this.messageConverters;}/*** Add one or more {@code RequestBodyAdvice} instances to intercept the* request before it is read and converted for {@code @RequestBody} and* {@code HttpEntity} method arguments.*/public void setRequestBodyAdvice(@Nullable List<RequestBodyAdvice> requestBodyAdvice) {if (requestBodyAdvice != null) {this.requestResponseBodyAdvice.addAll(requestBodyAdvice);}}/*** Add one or more {@code ResponseBodyAdvice} instances to intercept the* response before {@code @ResponseBody} or {@code ResponseEntity} return* values are written to the response body.*/public void setResponseBodyAdvice(@Nullable List<ResponseBodyAdvice<?>> responseBodyAdvice) {if (responseBodyAdvice != null) {this.requestResponseBodyAdvice.addAll(responseBodyAdvice);}}/*** Provide a WebBindingInitializer with "global" initialization to apply* to every DataBinder instance.*/public void setWebBindingInitializer(@Nullable WebBindingInitializer webBindingInitializer) {this.webBindingInitializer = webBindingInitializer;}/*** Return the configured WebBindingInitializer, or {@code null} if none.*/@Nullablepublic WebBindingInitializer getWebBindingInitializer() {return this.webBindingInitializer;}/*** Set the default {@link AsyncTaskExecutor} to use when a controller method* return a {@link Callable}. Controller methods can override this default on* a per-request basis by returning an {@link WebAsyncTask}.* <p>By default a {@link SimpleAsyncTaskExecutor} instance is used.* It's recommended to change that default in production as the simple executor* does not re-use threads.*/public void setTaskExecutor(AsyncTaskExecutor taskExecutor) {this.taskExecutor = taskExecutor;}/*** Specify the amount of time, in milliseconds, before concurrent handling* should time out. In Servlet 3, the timeout begins after the main request* processing thread has exited and ends when the request is dispatched again* for further processing of the concurrently produced result.* <p>If this value is not set, the default timeout of the underlying* implementation is used.* @param timeout the timeout value in milliseconds*/public void setAsyncRequestTimeout(long timeout) {this.asyncRequestTimeout = timeout;}/*** Configure {@code CallableProcessingInterceptor}'s to register on async requests.* @param interceptors the interceptors to register*/public void setCallableInterceptors(List<CallableProcessingInterceptor> interceptors) {this.callableInterceptors = interceptors.toArray(new CallableProcessingInterceptor[0]);}/*** Configure {@code DeferredResultProcessingInterceptor}'s to register on async requests.* @param interceptors the interceptors to register*/public void setDeferredResultInterceptors(List<DeferredResultProcessingInterceptor> interceptors) {this.deferredResultInterceptors = interceptors.toArray(new DeferredResultProcessingInterceptor[0]);}/*** Configure the registry for reactive library types to be supported as* return values from controller methods.* @since 5.0.5*/public void setReactiveAdapterRegistry(ReactiveAdapterRegistry reactiveAdapterRegistry) {this.reactiveAdapterRegistry = reactiveAdapterRegistry;}/*** Return the configured reactive type registry of adapters.* @since 5.0*/public ReactiveAdapterRegistry getReactiveAdapterRegistry() {return this.reactiveAdapterRegistry;}/*** By default the content of the "default" model is used both during* rendering and redirect scenarios. Alternatively a controller method* can declare a {@link RedirectAttributes} argument and use it to provide* attributes for a redirect.* <p>Setting this flag to {@code true} guarantees the "default" model is* never used in a redirect scenario even if a RedirectAttributes argument* is not declared. Setting it to {@code false} means the "default" model* may be used in a redirect if the controller method doesn't declare a* RedirectAttributes argument.* <p>The default setting is {@code false} but new applications should* consider setting it to {@code true}.* @see RedirectAttributes*/public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;}/*** Specify the strategy to store session attributes with. The default is* {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},* storing session attributes in the HttpSession with the same attribute* name as in the model.*/public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {this.sessionAttributeStore = sessionAttributeStore;}/*** Cache content produced by {@code @SessionAttributes} annotated handlers* for the given number of seconds.* <p>Possible values are:* <ul>* <li>-1: no generation of cache-related headers</li>* <li>0 (default value): "Cache-Control: no-store" will prevent caching</li>* <li>1 or higher: "Cache-Control: max-age=seconds" will ask to cache content;* not advised when dealing with session attributes</li>* </ul>* <p>In contrast to the "cacheSeconds" property which will apply to all general* handlers (but not to {@code @SessionAttributes} annotated handlers),* this setting will apply to {@code @SessionAttributes} handlers only.* @see #setCacheSeconds* @see org.springframework.web.bind.annotation.SessionAttributes*/public void setCacheSecondsForSessionAttributeHandlers(int cacheSecondsForSessionAttributeHandlers) {this.cacheSecondsForSessionAttributeHandlers = cacheSecondsForSessionAttributeHandlers;}/*** Set if controller execution should be synchronized on the session,* to serialize parallel invocations from the same client.* <p>More specifically, the execution of the {@code handleRequestInternal}* method will get synchronized if this flag is "true". The best available* session mutex will be used for the synchronization; ideally, this will* be a mutex exposed by HttpSessionMutexListener.* <p>The session mutex is guaranteed to be the same object during* the entire lifetime of the session, available under the key defined* by the {@code SESSION_MUTEX_ATTRIBUTE} constant. It serves as a* safe reference to synchronize on for locking on the current session.* <p>In many cases, the HttpSession reference itself is a safe mutex* as well, since it will always be the same object reference for the* same active logical session. However, this is not guaranteed across* different servlet containers; the only 100% safe way is a session mutex.* @see org.springframework.web.util.HttpSessionMutexListener* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)*/public void setSynchronizeOnSession(boolean synchronizeOnSession) {this.synchronizeOnSession = synchronizeOnSession;}/*** Set the ParameterNameDiscoverer to use for resolving method parameter names if needed* (e.g. for default attribute names).* <p>Default is a {@link org.springframework.core.DefaultParameterNameDiscoverer}.*/public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {this.parameterNameDiscoverer = parameterNameDiscoverer;}/*** A {@link ConfigurableBeanFactory} is expected for resolving expressions* in method argument default values.*/@Overridepublic void setBeanFactory(BeanFactory beanFactory) {if (beanFactory instanceof ConfigurableBeanFactory) {this.beanFactory = (ConfigurableBeanFactory) beanFactory;}}/*** Return the owning factory of this bean instance, or {@code null} if none.*/@Nullableprotected ConfigurableBeanFactory getBeanFactory() {return this.beanFactory;}@Overridepublic void afterPropertiesSet() {//// Do this first, it may add ResponseBody advice beans// 初始化注释了@ControllerAdvice的类的相关属性initControllerAdviceCache();// 初始化 argumentResolvers 属性if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();//26个this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}// 初始化 initBinderArgumentResolvers 属性if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}// 初始化 returnValueHandlers 属性if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}private void initControllerAdviceCache() {// 判断当前应用程序上下文是否为空,如果为空,直接返回if (getApplicationContext() == null) {return;}// 扫描@ControllerAdvice注解的Bean,生成对应的ControllerAdviceBean对象,并将进行排序List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();// 遍历ControllerAdviceBean数组for (ControllerAdviceBean adviceBean : adviceBeans) {Class<?> beanType = adviceBean.getBeanType();if (beanType == null) {throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);}// 扫描有`@ModelAttribute`,无`@RequestMapping`注解的方法,添加到`modelAttributeAdviceCache`属性中// 该类方法用于在执行方法前修改 Model 对象Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);if (!attrMethods.isEmpty()) {this.modelAttributeAdviceCache.put(adviceBean, attrMethods);}// 扫描有`@InitBinder`注解的方法,添加到`initBinderAdviceCache`属性中// 该类方法用于在执行方法前初始化数据绑定器Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);if (!binderMethods.isEmpty()) {this.initBinderAdviceCache.put(adviceBean, binderMethods);}// 如果是RequestBodyAdvice或ResponseBodyAdvice的子类,添加到requestResponseBodyAdviceBeans中if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {requestResponseBodyAdviceBeans.add(adviceBean);}}// 将requestResponseBodyAdviceBeans添加到this.requestResponseBodyAdvice属性中if (!requestResponseBodyAdviceBeans.isEmpty()) {this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);}// 打印日志if (logger.isDebugEnabled()) {int modelSize = this.modelAttributeAdviceCache.size();int binderSize = this.initBinderAdviceCache.size();int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {logger.debug("ControllerAdvice beans: none");}else {logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");}}}// Count all advice, including explicit registrations..private int getBodyAdviceCount(Class<?> adviceType) {List<Object> advice = this.requestResponseBodyAdvice;return RequestBodyAdvice.class.isAssignableFrom(adviceType) ?RequestResponseBodyAdviceChain.getAdviceByType(advice, RequestBodyAdvice.class).size() :RequestResponseBodyAdviceChain.getAdviceByType(advice, ResponseBodyAdvice.class).size();}/*** Return the list of argument resolvers to use including built-in resolvers* and custom resolvers provided via {@link #setCustomArgumentResolvers}.*/private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);// Annotation-based argument resolution// 添加按注解解析参数的解析器resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolution// 添加按类型解析参数的解析器resolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());// Custom arguments// 添加自定义参数解析器,主要用于解析自定义类型if (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-all// 最后两个解析器可以解析所有类型的参数resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;}/*** Return the list of argument resolvers to use for {@code @InitBinder}* methods including built-in and custom resolvers.*/private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);// Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());// Custom argumentsif (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));return resolvers;}/*** Return the list of return value handlers to use including built-in and* custom handlers provided via {@link #setReturnValueHandlers}.*/private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);// Single-purpose return value typeshandlers.add(new ModelAndViewMethodReturnValueHandler());handlers.add(new ModelMethodProcessor());handlers.add(new ViewMethodReturnValueHandler());handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));handlers.add(new StreamingResponseBodyReturnValueHandler());handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));handlers.add(new HttpHeadersReturnValueHandler());handlers.add(new CallableMethodReturnValueHandler());handlers.add(new DeferredResultMethodReturnValueHandler());handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));// Annotation-based return value typeshandlers.add(new ModelAttributeMethodProcessor(false));handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));// Multi-purpose return value typeshandlers.add(new ViewNameMethodReturnValueHandler());handlers.add(new MapMethodProcessor());// Custom return value typesif (getCustomReturnValueHandlers() != null) {handlers.addAll(getCustomReturnValueHandlers());}// Catch-allif (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));}else {handlers.add(new ModelAttributeMethodProcessor(true));}return handlers;}/*** Always return {@code true} since any method argument and return value* type will be processed in some way. A method argument not recognized* by any HandlerMethodArgumentResolver is interpreted as a request parameter* if it is a simple type, or as a model attribute otherwise. A return value* not recognized by any HandlerMethodReturnValueHandler will be interpreted* as a model attribute.*/@Overrideprotected boolean supportsInternal(HandlerMethod handlerMethod) {return true;}@Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;//视图 数据信息// 校验请求(HttpMethod 和 Session 的校验)checkRequest(request);// Execute invokeHandlerMethod in synchronized block if required.// 如果synchronizeOnSession为true,则对session进行同步,否则不同步if (this.synchronizeOnSession) {// 同步相同 Session 的逻辑,默认情况falseHttpSession session = request.getSession(false);if (session != null) {// 获取Session的锁对象Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {mav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No HttpSession available -> no mutex necessarymav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No synchronization on session demanded at all... 很关键的一个方法啊啊啊啊mav = invokeHandlerMethod(request, response, handlerMethod);}// 响应不包含'Cache-Control'头if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav;}/*** This implementation always returns -1. An {@code @RequestMapping} method can* calculate the lastModified value, call {@link WebRequest#checkNotModified(long)},* and return {@code null} if the result of that call is {@code true}.*/@Overrideprotected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {return -1;}/*** Return the {@link SessionAttributesHandler} instance for the given handler type* (never {@code null}).*/private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {return this.sessionAttributesHandlerCache.computeIfAbsent(handlerMethod.getBeanType(),type -> new SessionAttributesHandler(type, this.sessionAttributeStore));}/*** Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}* if view resolution is required.* @since 4.2* @see #createInvocableHandlerMethod(HandlerMethod)*/@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {// 使用request和response创建ServletWebRequest对象ServletWebRequest webRequest = new ServletWebRequest(request, response);try {// 创建WebDataBinderFactory对象,此对象用来创建WebDataBinder对象,进行参数绑定,// 实现参数跟String之间的类型转换,ArgumentResolver在进行参数解析的过程中会用到WebDataBinderWebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);// 创建ModelFactory对象,此对象主要用来处理model,主要是两个功能,1是在处理器具体处理之前对model进行初始化,2是在处理完请求后对model参数进行更新ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);// 创建ServletInvocableHandlerMethod对象,并设置其相关属性,实际的请求处理就是通过此对象来完成的,参数绑定、处理请求以及返回值处理都在里边完成ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 设置参数处理器if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}// 设置返回值处理器if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}// 设置参数绑定工厂对象invocableMethod.setDataBinderFactory(binderFactory);// 设置参数名称发现器invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// 创建ModelAndViewContainer对象,用于保存model和View对象ModelAndViewContainer mavContainer = new ModelAndViewContainer();// 将flashmap中的数据设置到model中 FlashMap只适用于重定向跳转传参mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));// 使用modelFactory将sessionAttributes和注释了@ModelAttribute的方法的参数设置到model中modelFactory.initModel(webRequest, mavContainer, invocableMethod);// 根据配置对ignoreDefaultModelOnRedirect进行设置mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);// 创建AsyncWebRequest异步请求对象AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);// 创建WebAsyncManager异步请求管理器对象WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);// 如果当前异步请求已经处理并得到结果,则将返回的结果放到mavContainer对象中,然后将invocable对象进行包装转换,转成需要的执行对象然后开始执行if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);return "Resume with async result [" + formatted + "]";});// 转换具体的invocable执行对象invocableMethod = invocableMethod.wrapConcurrentResult(result);}// 执行调用invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}// 处理完请求后的后置处理,此处一共做了三件事,// 1、调用ModelFactory的updateModel方法更新model,包括设置SessionAttribute和给Model设置BinderResult// 2、根据mavContainer创建了ModelAndView// 3、如果mavContainer里的model是RedirectAttributes类型,则将其设置到FlashMapreturn getModelAndView(mavContainer, modelFactory, webRequest);}finally {// 标记请求完成webRequest.requestCompleted();}}/*** Create a {@link ServletInvocableHandlerMethod} from the given {@link HandlerMethod} definition.* @param handlerMethod the {@link HandlerMethod} definition* @return the corresponding {@link ServletInvocableHandlerMethod} (or custom subclass thereof)* @since 4.2*/protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {return new ServletInvocableHandlerMethod(handlerMethod);}private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {// 获取sessionAttributesHandlerSessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);// 获取处理器类的类型Class<?> handlerType = handlerMethod.getBeanType();// 获取处理器类中注释了@modelAttribute而且没有注释@RequestMapping的类型,第一个获取后添加到缓存,以后直接从缓存中获取Set<Method> methods = this.modelAttributeCache.get(handlerType);if (methods == null) {methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);this.modelAttributeCache.put(handlerType, methods);}// 注释了@ModelAttribute的方法List<InvocableHandlerMethod> attrMethods = new ArrayList<>();// Global methods first// 先添加全局的@ModelAttribute方法,后添加当前处理器定义的@ModelAttribute方法this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {Object bean = controllerAdviceBean.resolveBean();for (Method method : methodSet) {attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));}}});for (Method method : methods) {Object bean = handlerMethod.getBean();attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));}// 新建ModelFactory对象,此处需要三个参数,第一个是注释了@ModelAttribute的方法,第二个是WebDataBinderFactory,第三个是SessionAttributeHandlerreturn new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);}private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);if (this.argumentResolvers != null) {attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);attrMethod.setDataBinderFactory(factory);return attrMethod;}private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {Class<?> handlerType = handlerMethod.getBeanType();// 检查当前Handler中的initBinder方法是否已经存在于缓存中Set<Method> methods = this.initBinderCache.get(handlerType);// 如果没有则查找并设置到缓冲中if (methods == null) {methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);// 将当前Controller中所有被@InitBinder注解修饰的方法都获取到this.initBinderCache.put(handlerType, methods);}// 定义保存InitBinder方法的临时变量 对当前controller有效List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();// Global methods first// 将所有符合条件的全局InitBinder方法添加到initBinderMethodsthis.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {Object bean = controllerAdviceBean.resolveBean();for (Method method : methodSet) {initBinderMethods.add(createInitBinderMethod(bean, method));}}});// 将当前handler中的initBinder方法添加到initBinderMethodsfor (Method method : methods) {// 创建当前方法对应的bean对象Object bean = handlerMethod.getBean();// 将method适配为可执行的invocableHandlerMethodinitBinderMethods.add(createInitBinderMethod(bean, method));}// 创建DataBinderFactory并返回return createDataBinderFactory(initBinderMethods);}private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);if (this.initBinderArgumentResolvers != null) {binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);}binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);return binderMethod;}/*** Template method to create a new InitBinderDataBinderFactory instance.* <p>The default implementation creates a ServletRequestDataBinderFactory.* This can be overridden for custom ServletRequestDataBinder subclasses.* @param binderMethods {@code @InitBinder} methods* @return the InitBinderDataBinderFactory instance to use* @throws Exception in case of invalid state or arguments*/protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)throws Exception {return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());}@Nullableprivate ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {// 更新model(设置sessionAttributes和给model设置BindingResult)modelFactory.updateModel(webRequest, mavContainer);// 情况一,如果 mavContainer 已处理,则返回“空”的 ModelAndView 对象。if (mavContainer.isRequestHandled()) {return null;}// 情况二,如果mavContainer未处理,则基于`mavContainer`生成ModelAndView对象ModelMap model = mavContainer.getModel();// 创建 ModelAndView 对象,并设置相关属性ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());// 如果mavContainer里的view不是引用,也就是不是string类型,则设置到mv中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;}}

HandlerFunctionAdapter

public class HandlerFunctionAdapter implements HandlerAdapter, Ordered {private int order = Ordered.LOWEST_PRECEDENCE;/*** Specify the order value for this HandlerAdapter bean.* <p>The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered.* @see org.springframework.core.Ordered#getOrder()*/public void setOrder(int order) {this.order = order;}@Overridepublic int getOrder() {return this.order;}@Overridepublic boolean supports(Object handler) {return handler instanceof HandlerFunction;}@Nullable@Overridepublic ModelAndView handle(HttpServletRequest servletRequest,HttpServletResponse servletResponse,Object handler) throws Exception {HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;ServerRequest serverRequest = getServerRequest(servletRequest);ServerResponse serverResponse = handlerFunction.handle(serverRequest);return serverResponse.writeTo(servletRequest, servletResponse,new ServerRequestContext(serverRequest));}private ServerRequest getServerRequest(HttpServletRequest servletRequest) {ServerRequest serverRequest =(ServerRequest) servletRequest.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);Assert.state(serverRequest != null, () -> "Required attribute '" +RouterFunctions.REQUEST_ATTRIBUTE + "' is missing");return serverRequest;}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {return -1L;}private static class ServerRequestContext implements ServerResponse.Context {private final ServerRequest serverRequest;public ServerRequestContext(ServerRequest serverRequest) {this.serverRequest = serverRequest;}@Overridepublic List<HttpMessageConverter<?>> messageConverters() {return this.serverRequest.messageConverters();}}
}

在这里插入图片描述
循环调用adapter的supports校验handler
在这里插入图片描述
当前的请求是上传文件,是@Controller注解修饰的类方法,进入到这个适配器符合条件,取得适配器。
在这里插入图片描述

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

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

相关文章

数据结构与算法——Java实现 32.堆

人的想法和感受是会随着时间的认知改变而改变&#xff0c; 原来你笃定不会变的事&#xff0c;也会在最后一刻变得释然 —— 24.10.10 堆 堆是基于二叉树实现的数据结构 大顶堆每个分支的上一个节点的权值要大于它的孩子节点 小顶堆每个分支的上一个节点的权值要小于它的孩子…

开源催生开源:Tesla 如何加速 AI 发展

特斯拉最近宣布开源其特斯拉以太网传输协议 &#xff08;TTPoE&#xff09;&#xff0c;这是一种尖端网络结构&#xff0c;专为 AI/ML 数据中心环境中的高速、低延迟数据传输而设计&#xff0c;从而掀起了波澜。此举反映了特斯拉利用开源战略加速全行业进步的更广泛历史&#x…

Spring Boot课程问答:技术难题轻松解决

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

基于pytorch的手写数字识别-训练+使用

import pandas as pd import numpy as np import torch import matplotlib import matplotlib.pyplot as plt from torch.utils.data import TensorDataset, DataLoadermatplotlib.use(tkAgg)# 设置图形配置 config {"font.family": serif,"mathtext.fontset&q…

洗衣店订单管理:Spring Boot技术革新

3系统分析 3.1可行性分析 通过对本洗衣店订单管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本洗衣店订单管理系统采用JAVA作为开发语言&#xff0c;S…

pytest(六)——allure-pytest的基础使用

前言 一、allure-pytest的基础使用 二、需要掌握的allure特性 2.1 Allure报告结构 2.2 Environment 2.3 Categories 2.4 Flaky test 三、allure的特性&#xff0c;allure.step()、allure.attach的详细使用 3.1 allure.step 3.2 allure.attach&#xff08;挺有用的&a…

如何利用wsl-Ubuntu里conda用来给Windows的PyCharm开发

前提&#xff1a;咱们在wsl-Ubuntu上&#xff0c;有conda的虚拟环境 咱们直接打开PyCharm,打开Settings 更换Python Interpreter即可 当然一开始可能没有下面的选项&#xff0c;需要我们点击右边的Add Interpreter 这里选择wsl 点击next 将这两步进行修改 可以看出来&#xff0…

kubernetes中微服务部署

微服务 问&#xff1a;用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&#xff1f; 答&#xff1a;需要通过微服务暴漏出去后才能被访问 Service 是一组提供相同服务的Pod对外开放的接口借助Service&#xff0c;应用可以实现服务发现和负载均衡Service 默认只…

智谱开放平台API调用解析

一、什么是智谱AI 智谱AI成立于2019年&#xff0c;由‌清华大学计算机系知识工程实验室的技术成果转化而来&#xff0c;是一家致力于人工智能技术研发和应用的公司。智谱致力于打造新一代认知智能大模型&#xff0c;专注于做大模型的中国创新。 二、智谱开放平台API调用 官方文…

【LeetCode】动态规划—673. 最长递增子序列的个数(附完整Python/C++代码)

动态规划—673. 最长递增子序列的个数 前言题目描述基本思路1. 问题定义2. 理解问题和递推关系3. 解决方法3.1 动态规划方法3.2 优化方法 4. 进一步优化5. 小总结 代码实现PythonPython3代码实现Python 代码解释 CC代码实现C 代码解释1. 初始化&#xff1a;2. 动态规划过程&…

FiBiNET模型实现推荐算法

1. 项目简介 A031-FiBiNET模型项目是一个基于深度学习的推荐系统算法实现&#xff0c;旨在提升推荐系统的性能和精度。该项目的背景源于当今互联网平台中&#xff0c;推荐算法在电商、社交、内容分发等领域的广泛应用。推荐系统通过分析用户的历史行为和兴趣偏好&#xff0c;预…

Django学习笔记十三:优秀案例学习

Django CMS 是一个基于 Django 框架的开源内容管理系统&#xff0c;它允许开发者轻松地创建和管理网站内容。Django CMS 提供了一个易于使用的界面来实现动态网站的快速开发&#xff0c;并且具有丰富的内容管理功能和多种插件扩展。以下是 Django CMS 的一些核心特性和如何开始…

opencv的相机标定与姿态解算

首先我们要知道四个重要的坐标系 世界坐标系相机坐标系图像成像坐标系图像像素坐标系 坐标系之间的转换 世界坐标系——相机坐标系 从世界坐标系到相机坐标系&#xff0c;涉及到旋转和平移&#xff08;其实所有的运动也可以用旋转矩阵和平移向量来描述&#xff09;。绕着不…

最新Prompt预设词指令教程大全ChatGPT、AI智能体(300+预设词应用)

使用指南 直接复制在AI工具助手中使用&#xff08;提问前&#xff09; 可以前往已经添加好Prompt预设的AI系统测试使用&#xff08;可自定义添加使用&#xff09; SparkAi系统现已支持自定义添加官方GPTs&#xff08;对专业领域更加专业&#xff0c;支持多模态文档&#xff0…

同三维T80001EHK 4K超高清HDMI编码器

【系列介绍】 同三维T80001EHK 4K超高清HDMI编码器 4K超高清编码器&#xff08;采集盒&#xff09;是专业的高清音视频编码产品&#xff0c;只需要占用较小的带宽&#xff0c;即可获得高清晰度的视频信号。该产品采用H.265编码格式&#xff0c;可同时对视频音频进行编码。输出…

【万字长文】Word2Vec计算详解(二)Skip-gram模型

【万字长文】Word2Vec计算详解&#xff08;二&#xff09;Skip-gram模型 写在前面 本篇介绍Word2Vec中的第二个模型Skip-gram模型 【万字长文】Word2Vec计算详解&#xff08;一&#xff09;CBOW模型 markdown行 9000 【万字长文】Word2Vec计算详解&#xff08;二&#xff09;S…

<Project-8.1 pdf2tx-MM> Python Flask 用浏览器翻译PDF内容 2个翻译引擎 繁简中文结果 从P8更改

更新 Project Name&#xff1a;pdf2tx (P6) Date: 5oct.24 Function: 在浏览器中翻译PDF文件 Code:https://blog.csdn.net/davenian/article/details/142723144 升级 Project Name: pdf2tx-mm (P8) 7oct.24 加入多线程&#xff0c;分页OCR识别&#xff0c;提高性能与速度 使…

5G NR UE初始接入信令流程

文章目录 5G NR UE初始接入信令流程 5G NR UE初始接入信令流程 用户设备向gNB-DU发送RRCSetupRequest消息。gNB-DU 包含 RRC 消息&#xff0c;如果 UE 被接纳&#xff0c;则在 INITIAL UL RRC MESSAGE TRANSFER 消息中包括为 UE 分配的低层配置&#xff0c;并将其传输到 gNB-CU…

【OpenCV】基础操作学习--实现原理理解

读取和显示图像 基本操作 cv2.imread(filename , flags)&#xff1a;文件中读取图像&#xff0c;从指定路径中读取图像&#xff0c;返回一个图像数组&#xff08;NumPy数组&#xff09; filename&#xff1a;图像文件的路径flags&#xff1a;指定读取图像的方式 cv2.IMREAD_COL…

linux线程 | 线程的概念

前言:本篇讲述linux里面线程的相关概念。 线程在我们的教材中的定义通常是这样的——线程是进程的一个执行分支。 线程的执行粒度&#xff0c; 要比进程要细。 我们在读完这句话后其实并不能很好的理解什么是线程。 所以&#xff0c; 本节内容博主将会带友友们理解什么是线程&a…