spring-web HandlerAdapter 源码分析

news2024/10/5 14:56:36

说明

  1. 本文基于 jdk 8, spring-framework 5.2.x 编写。
  2. @author JellyfishMIX - github / blog.jellyfishmix.com
  3. LICENSE GPL-2.0

HandlerAdapter 接口

提供作为处理器适配器的能力。

  1. supports 方法判断是否支持该 handler。
public interface HandlerAdapter {
	/**
	 * 判断是否支持该 handler
	 */
	boolean supports(Object handler);

	/**
	 * handler 执行,返回 ModelAndView 结果
	 */
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	/**
	 * 返回请求的最新更新时间,如果不支持该操作,则返回 -1 即可
	 */
	long getLastModified(HttpServletRequest request, Object handler);
}

类层次

img

HandlerAdapter 的定位/职责

HandlerAdapter 存在的原因是,springMVC 通过 HandlerMapping 为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器(interceptors)。其中处理器(handler)的实现有多种,例如实现 Controller 接口,实现 HttpRequestHandler 接口,或者使用 @RequestMapping 注解将方法作为一个处理器等。导致 springMVC 无法直接执行这个处理器,所以这里需要一个适配器(adapter),由它去执行处理器(handler)。

HandlerAdapter 也有多种,哪种 adapter 支持处理这种类型的 handler,则由该 adapter 去执行,如下:

  • HttpRequestHandlerAdapter: 适配实现了 HttpRequestHandler 接口的处理器。
  • SimpleControllerHandlerAdapter: 适配实现了 Controller 接口的处理器。
  • SimpleServletHandlerAdapter: 适配实现了 Servlet 接口的处理器。
  • RequestMappingHandlerAdapter: 适配 HandlerMethod 类型的处理器,也就是通过 @RequestMapping 注解标注的方法。

重点关注 RequestMappingHandlerAdapter,这种方式是目前使用最普遍的。

HandlerAdapter 初始化过程

DispatcherServlet 重写 FrameworkServlet#onRefresh 方法,调用 DispatcherServlet#initHandlerAdapters 方法,初始化 HandlerAdapter。

org.springframework.web.servlet.DispatcherServlet#onRefresh

	/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

DispatcherServlet#initHandlerAdapters 方法

org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters

  1. 如果探测功能开启,则扫描已注册的 HandlerAdapter 的 beans,添加到 handlerAdapters 集合中,默认开启。这里会进行排序,可以通过实现 Order 接口设置排序值。

  2. 如果探测功能关闭,则获得 beanName 为 “handlerAdapter” 对应的 bean,添加至 handlerAdapters。

  3. 未获取到 handlerAdapter 则获取默认的,如下: HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, RequestMappingHandlerAdapter

    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
    
	private void initHandlerAdapters(ApplicationContext context) {
		this.handlerAdapters = null;

		// 如果探测功能开启,则扫描已注册的 HandlerAdapter 的 beans,添加到 handlerAdapters 集合中,默认开启。这里会进行排序,可以通过实现 Order 接口设置排序值。
		if (this.detectAllHandlerAdapters) {
			// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerAdapter> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerAdapters = new ArrayList<>(matchingBeans.values());
				// We keep HandlerAdapters in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerAdapters);
			}
		}
		// 如果探测功能关闭,则获得 beanName 为 "handlerAdapter" 对应的 bean,添加至 handlerAdapters
		else {
			try {
				HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
				this.handlerAdapters = Collections.singletonList(ha);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerAdapter later.
			}
		}

		// Ensure we have at least some HandlerAdapters, by registering
		// default HandlerAdapters if no other adapters are found.
		// 未获取到 handlerAdapter 则获取默认的
		if (this.handlerAdapters == null) {
			this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

AbstractHandlerMethodAdapter#handle 方法 – 处理请求

org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle

处理请求,封装了一层内部调用,具体逻辑 handleInternal 交由子类实现。由 DispatcherServlet#doDispatch 调用。

	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		return handleInternal(request, response, (HandlerMethod) handler);
	}

三个默认的 HandlerAdapter

org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter

对于 HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter,阅读 HandlerAdapter 接口的定义后,没有太多需要说明的。我们重点关注 RequestMappingHandlerAdapter。

public class HttpRequestHandlerAdapter implements HandlerAdapter {
	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}
}

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}

}

RequestMappingHandlerAdapter

RequestMappingHandlerAdapter#afterPropertiesSet 方法 – 初始化 adapter

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet

RequestMappingHandlerAdapter 实现了 InitializingBean 接口,在 spring-ioc 初始化 RequestMappingHandlerAdapter 的 bean 实例时候,会调用此方法,完成一些初始化工作。

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
	@Override
	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
		// 初始化 ControllerAdvice 相关
		initControllerAdviceCache();

		// 初始化 argumentResolvers 属性
		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			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);
		}
	}
}

@ControllerAdvice 注解

用于 Controller 类的增强,@ControllerAdvice 加载增强类上,此类中可使用方法级别的注解增强方法,例如 @ExceptionHandler 注解加载方法上,用于处理 Controller 抛出的异常。demo:

@Order(Integer.MAX_VALUE)
@ControllerAdvice
public class DefaultExceptionHandler {
    protected Logger log = LoggerFactory.getLogger(DefaultExceptionHandler.class);

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseEntity defaultErrorHandler(Exception e) {
        // ...
        return xxx;
    }
}

RequestMappingHandlerAdapter#getDefaultArgumentResolvers 方法 – 获取默认的参数解析器

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultArgumentResolvers

获取默认的参数解析器,按顺序添加了多个参数解析器对象。

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

RequestMappingHandlerAdapter#getDefaultInitBinderArgumentResolvers 方法 – 获取默认的参数绑定器

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultInitBinderArgumentResolvers

获取默认的参数绑定器,按顺序添加了多个参数解析器对象。

	private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);

		// 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 ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		// Type-based argument resolution
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());

		// Custom arguments
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		// Catch-all
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

		return resolvers;
	}

RequestMappingHandlerAdapter#getDefaultReturnValueHandlers 方法 – 获取默认的返回值处理器

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultReturnValueHandlers

获取默认的返回值处理器,按顺序添加了多个返回值处理器对象。

	private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

		// Single-purpose return value types
		handlers.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 types
		handlers.add(new ModelAttributeMethodProcessor(false));
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		handlers.add(new ViewNameMethodReturnValueHandler());
		handlers.add(new MapMethodProcessor());

		// Custom return value types
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

RequestMappingHandlerAdapter#handleInternal 方法 – 处理请求

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

处理请求,由 AbstractHandlerMethodAdapter#handle 方法触发。

	@Override
	protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		ModelAndView mav;
		// 校验请求(requestMethod 和 pre-session 的校验)
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		// 调用 HandlerMethod 方法,同步相同 Session 的逻辑,默认情况 false
		if (this.synchronizeOnSession) {
			HttpSession 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 necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

RequestMappingHandlerAdapter#invokeHandlerMethod

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod

处理请求,由 RequestMappingHandlerAdapter#handleInternal 方法触发。

  1. 创建 ServletWebRequest 对象,包含了 request 和 response。创建 WebDataBinderFactory 对象,和数据绑定相关。创建 ModelFactory 对象,和往 Model 对象设置数据相关。创建 ServletInvocableHandlerMethod 对象,ModelAndViewContainer 对象,AsyncWebRequest 对象,WebAsyncManager 对象,并初始其相关属性。
  2. 核心,调用 ServletInvocableHandlerMethod#invokeAndHandle 方法处理请求。
  3. 获得 ModelAndView 对象,标记请求完成。
	@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		// 创建 ServletWebRequest 对象,包含了 request 和 response
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			// 创建 WebDataBinderFactory 对象,和数据绑定相关
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			// 创建 ModelFactory 对象,和往 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 对象,并初始其相关属性
			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			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);

			// 异步处理,并发结果相关
			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 + "]";
				});
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}

			// 核心,由 ServletInvocableHandlerMethod 处理请求
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			// 获得 ModelAndView 对象
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			// 标记请求完成
			webRequest.requestCompleted();
		}
	}

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

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

相关文章

【跟着陈七一起学C语言】今天总结:初识C语言

友情链接&#xff1a;专栏地址 知识总结顺序参考C Primer Plus&#xff08;第六版&#xff09;和谭浩强老师的C程序设计&#xff08;第五版&#xff09;等&#xff0c;内容以书中为标准&#xff0c;同时参考其它各类书籍以及优质文章&#xff0c;以至减少知识点上的错误&#x…

Ansys Zemax | 设计抬头显示器时要使用哪些工具 – 第二部分

本文为使用OpticStudio工具设计优化HUD抬头显示器系统的第二部分&#xff0c;主要包含演示了如何使用OpticStudio工具设计分析抬头显示器&#xff08;HUD&#xff09;性能&#xff0c;即全视场像差&#xff08;FFA&#xff09;和NSC矢高图。&#xff08;联系我们获取文章附件&a…

RabbitMQ之工作队列 ( Work Queues )

Work Queues 1. 轮询分发消息1.1 抽取工具类1.2 启动两个工作线程1.3 启动一个发送线程1.4 结果展示 2. 消息应答2.1 概念2.2 自动应答2.3 消息应答的方法2.4 Multiple 的解释2.5 消息自动重新入队2.6 消息手动应答代码2.7 手动应答效果演示 3. RabbitMQ 持久化3.1 概念3.2 队列…

逍遥自在学C语言 | 条件控制的正确使用姿势

前言 在C语言中&#xff0c;有三种条件判断结构&#xff1a;if语句、if-else语句和switch语句。 一、人物简介 第一位闪亮登场&#xff0c;有请今后会一直教我们C语言的老师 —— 自在。 第二位上场的是和我们一起学习的小白程序猿 —— 逍遥。 二、if语句 基本语法 if (条…

大厂过来人忠告:学java有没有前途?想转行应该准备什么?

对于想转行学习java来人说&#xff0c;最可怕的问题就是信息闭塞。很多人开始的时候都是因为没能了解清楚情况&#xff0c;找不到学习思路&#xff0c;胡乱下手学习一通其实效果并不好&#xff0c;只是感动了自己&#xff0c;没有太大成效。毕竟时间这么宝贵&#xff0c;你也不…

ThingsBoard教程(四十):规则节点解析 计算增量节点 Calculate delta

本篇文章介绍一个ThingsBoard 规则引擎中的一个节点,Calculate delta Calculate delta 计算增量 该节点可以在规则中获取上一次遥测的值,以此可以实现二次遥测的差。比如一个设备,一天上传一次数据,如果你要对比今天和昨天的数据,并将两者数据差保存到数据库,就能够使用…

三子棋游戏的实现(C语言)

三子棋游戏的实现&#xff0c;在这里我们要求满足&#xff1a; 游戏不退出&#xff0c;继续下一把&#xff08;循环&#xff09;用多文件的形式实现&#xff0c;如下&#xff1a; 用game.h文件存放函数的声明并包含需要的头文件用game.c文件存放各个函数的具体实现用test.c文件…

【Linux】Keepalived+Haproxy实现数据库集群负载均衡

1、简介&#xff1a; 本文章的负载均衡和高可用是体现在两个从服务器上的。一般来说高可用是用在主服务器中的&#xff0c;例如双主多从的结构&#xff0c;双主做keepalived的高可用&#xff08;当然也可以加上haproxy做负载均衡&#xff09;&#xff0c;多从做haproxy的负载均…

微盟餐饮SaaS蜕变时刻:战略投资奥琦玮,领军之势已成

从火爆了整个春天的淄博烧烤&#xff0c;到“五一”人山人海的全国各地核心商圈&#xff0c;餐饮业热度狂飙不止。餐饮SaaS领域&#xff0c;大事件也在发生。 5月8日&#xff0c;微盟集团&#xff08;2013.HK&#xff09;宣布以“资产现金”方式&#xff0c;向餐饮行业数字化服…

前端get请求参数包含数组的情况

前端get请求参数包含数组的情况 问题描述解决办法文章参考 问题描述 当我们使用post传数组参数的时候&#xff0c;是没有问题的&#xff0c;可以不经过参数处理即可正常传参&#xff0c;但是当我们使用get请求传数组参数的时候&#xff0c;会出现下图这样的情况&#xff1a; a…

如何通过云平台加快Blender渲染?

Blender是一款专业自由及开放源代码的三维计算机图形软件&#xff0c;也是免费的开源3D创作套件&#xff0c;支持整个3D流程——建模、UV、贴图、材质、骨骼、动画、渲染、后期、合成、剪辑、跟踪和抠像等等&#xff0c;CG行业内也有不少大佬们通过Blender制作出了许多优秀作品…

代码随想录 LeetCode数组篇 螺旋矩阵II Java实现

文章目录 &#xff08;中等&#xff09;59. 螺旋矩阵II&#xff08;中等&#xff09;54. 螺旋矩阵&#xff08;简单&#xff09;JZ29 顺时针打印矩阵 &#xff08;中等&#xff09;59. 螺旋矩阵II 因为我是先做的JZ29&#xff0c;所以看到这题的时候&#xff0c;几乎就是一样的…

SpringCloud入门实战之项目(一)

一、新建父工程 以“下单”需要调“支付”模块为例&#xff0c;从零开始搭建springcloud-001项目&#xff0c;陆续集成相关组件。 新建服务提供者cloud-payment工程&#xff0c;服务调用者cloud-order工程&#xff0c;完成order对payment的调用。 只留下pom文件&#xff0c;其…

基于人工智能AI视频分析的智慧安监解决方案

方案背景 为了保证对园区环境风险进行有效识别&#xff0c;传统视频监控存在视频结构化利用率低的问题&#xff0c;在实际使用过程中&#xff0c;安全管理人员工作效率低下&#xff0c;依靠人工肉眼查看灵活度低&#xff0c;风险漏报概率高&#xff0c;出现异常情况跟踪不及时&…

中文润色神器-中文润色软件

中文写作润色软件 中文写作润色软件是一种基于自然语言处理技术和人工智能算法的工具&#xff0c;旨在提高中文文本的语言风格、表达能力和可读性。它可以自动检测文本中出现的语法、拼写、标点符号等语言问题&#xff0c;并给出相应的修正和修改建议。 中文写作润色软件的主…

paddleLite在Android部署初体验(环境问题)

paddleLite初体验&#xff08;环境问题&#xff09; Android Studio下载Paddle Lite Demo打开项目环境配置下载到手机 Paddle Lite是百度开发的一种方便部署的深度学习推理框架&#xff0c;笔者最近想接触一些模型部署相关项目&#xff0c;就先接触了一下Paddle Lite&#xff0…

手术麻醉信息系统源码 php + mysql + vue2,覆盖患者就诊全过程,体征数据自动采集绘制

手术麻醉信息系统源码 php mysql vue2 B/S网页版 麻醉信息系统是HIS产品的中的一个组成部分&#xff0c;主要应用于医院的麻醉科&#xff0c;属于电子病历类产品。医院麻醉监护的功能覆盖整个手术与麻醉的全过程&#xff0c;包括手术申请与排班、审批、安排、术前、术中和术…

list_for_each_entry()函数分析

在Linux内核源码中&#xff0c;经常要对链表进行操作&#xff0c;其中一个很重要的宏是list_for_each_entry&#xff1a; /*** list_for_each_entry - iterate over list of given type* pos: the type * to use as a loop cursor.* head: the head for your list.* member: t…

9个已开源的GPT4平替分享(附开源代码+论文)

资料整理自网络&#xff0c;有误欢迎指正 对于想要研究大模型的同学来说&#xff0c;目前ChatGPT无疑是最好的学习对象&#xff0c;但等它开源估计是不太可能了&#xff0c;所以学姐今天整理了一些开源的类GPT模型&#xff0c;帮助大家更好的理解大模型背后的机理。 PS&#x…

io,nio,aio区别

文章目录 前言io类型介绍同步阻塞io同步非阻塞ioio多路复用异步io 普通ionioChannelChannel实现基本的 Channel代码 示例 BufferBuffer的基本用法Buffer的capacity,position和limitcapacitypositionlimit Buffer的类型Buffer的分配向Buffer中写数据从Buffer中读取数据 Selector…