spring mvc源码学习笔记之十一

news2025/1/15 4:47:12
  • pom.xml 内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qs.demo</groupId>
    <artifactId>test-011</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.30.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring4</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>
    </dependencies>

</project>
  • src/main/webapp/WEB-INF/web.xml 内容如下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <!-- / 表示除了 xxx.jsp 之外的所有请求 -->
        <!-- /* 表示所有请求 -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  • src/main/webapp/WEB-INF/app-servlet.xml 内容如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 这个文件的名字是有讲究的 -->
    <!-- springmvc 的配置 -->

    <mvc:annotation-driven/>

    <!-- 开启组件扫描 -->
    <context:component-scan base-package="com.qs.demo"/>

    <!-- 配置视图解析器 -->
    <bean id="thymeleafViewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
        <property name="characterEncoding" value="UTF-8"/>
        <property name="order" value="1"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring4.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
                        <property name="prefix" value="/WEB-INF/templates/"/>
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

</beans>
  • src/main/webapp/WEB-INF/templates/t01.html 内容如下
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>t01</title>
</head>
<body>
<a th:href="@{/t02}">hello</a>
</body>
</html>
  • src/main/webapp/WEB-INF/templates/t02.html 内容如下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>t01</title>
</head>
<body>
   <h1>Peter</h1>
</body>
</html>
  • com.qs.demo.A_ControllerAdvice 内容如下
package com.qs.demo;

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

import java.beans.PropertyEditorSupport;
import java.util.Date;

/**
 * @author qs
 * @date 2025/01/10
 */
@ControllerAdvice(basePackages = "com.qs.demo.controller")
public class A_ControllerAdvice {

  @InitBinder({"time"})
  public void a(WebDataBinder webDataBinder) {
    webDataBinder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
      @Override
      public void setAsText(String text) throws IllegalArgumentException {
        setValue(new Date(Long.parseLong(text) / 1000));
      }
    });
  }

}
  • com.qs.demo.controller.FirstController 内容如下
package com.qs.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.beans.PropertyEditorSupport;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author qs
 * @date 2024/12/20
 */
@Controller
public class FirstController {

  @InitBinder({"date"})
  public void a(WebDataBinder webDataBinder) {
    webDataBinder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
      @Override
      public void setAsText(String text) throws IllegalArgumentException {
        setValue(new Date(Long.parseLong(text)));
      }
    });
  }

  @RequestMapping("/t01")
  public String t01() {
    return "t01";
  }

  @RequestMapping("/t02")
  public String t02() {
    return "t02";
  }

  @RequestMapping("/t03")
  @ResponseBody
  public String t03(@RequestParam Date date) {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
  }

  @RequestMapping("/t04")
  @ResponseBody
  public String t04(@RequestParam Date time) {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time);
  }

}

以上就是全部代码。需要注意的是代码里边有跟本文无关的内容。本文只需要关注 /t03 和 /t04 这2个接口即可。

写这个例子,主要是为了讲解 @InitBinder 注解。
在例子中,我们展示了 @InitBinder 的两种用法:一种局部的(或者说本地的,此处的本地指的是controller内部)和一种全局的。
不管是本地的还是全局的,代码所做的事情都是我们编码中常见的将前端传过来的 long 格式的日期转换为 Java 中的Date。无非是这里为了区分,全局的 @InitBinder 对前端传过来的 long 格式的时间先除以1000然后转为 Date,而本地的 @InitBinder 则直接将前端传来的 long 格式的时间转换为 Date
当然,还有一个点需要注意,在全局的 @InitBinder 上,我们将它的 value 值设置为 {"time"} 意思是只转换名字为 time 的参数,对应的就是接口 /t04,同样的,本地的 @InitBindervalue 值是 {"date"} 意思是只转换名字为 date 的参数,对应的就是接口 /t03。

测试 /t03 接口

http://localhost:8080/test_011/t03?date=1736501024000

结果是

2025-01-10 17:23:44

测试 /t04 接口

http://localhost:8080/test_011/t04?time=1736501024000

结果是

1970-01-21 10:21:41

以上是对代码的解释,下面开始分析 @InitBinder 的源码

  • 第一点就是看 @InitBinder 这个注解是在哪里被解析的

	@Override
	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
        System.out.println("这个方法仔细看");
        System.out.println("这个方法仔细看");
        System.out.println("这个方法仔细看");

		initControllerAdviceCache();

		if (this.argumentResolvers == null) {

          System.out.println("初始化默认的参数解析器");
          System.out.println("初始化默认的参数解析器");
          System.out.println("初始化默认的参数解析器");

			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
    
          System.out.println("初始化默认的返回值处理器");
          System.out.println("初始化默认的返回值处理器");
          System.out.println("初始化默认的返回值处理器");

			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

上面的代码是 RequestMappingHandlerAdapter 中的 afterPropertiesSet 方法。我们只需要关注第一行的 initControllerAdviceCache();

private void initControllerAdviceCache() {
		if (getApplicationContext() == null) {
			return;
		}

        System.out.println("寻找 @ControllerAdvice 注解。");

		if (logger.isInfoEnabled()) {
			logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
		}

		List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		AnnotationAwareOrderComparator.sort(adviceBeans);

		List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

		for (ControllerAdviceBean adviceBean : adviceBeans) {
			Class<?> beanType = adviceBean.getBeanType();
			if (beanType == null) {
				throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
			}
			Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
			if (!attrMethods.isEmpty()) {
				this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
				if (logger.isInfoEnabled()) {
					logger.info("Detected @ModelAttribute methods in " + adviceBean);
				}
			}
			Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
			
            System.out.println("@InitBinder 方法有 " + binderMethods.size() + " 个");
            
			if (!binderMethods.isEmpty()) {
				this.initBinderAdviceCache.put(adviceBean, binderMethods);
				if (logger.isInfoEnabled()) {
					logger.info("在 ControllerAdviceBean ---> " + adviceBean
            + " 中检测到了 @InitBinder 方法。 Detected @InitBinder methods in " + adviceBean);
				}
			}
			if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
				requestResponseBodyAdviceBeans.add(adviceBean);
				if (logger.isInfoEnabled()) {
					logger.info("Detected RequestBodyAdvice bean in " + adviceBean);
				}
			}
			if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
				requestResponseBodyAdviceBeans.add(adviceBean);
				if (logger.isInfoEnabled()) {
					logger.info("Detected ResponseBodyAdvice bean in " + adviceBean);
				}
			}
		}

		if (!requestResponseBodyAdviceBeans.isEmpty()) {
			this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
		}
	}

这里边我们只需要关注跟 @InitBinder 相关的部分。它所做的事情就是先找到被 @ControllerAdvice 注解标记的 bean,然后遍历这些 bean,找到被 @InitBinder 注解标记的方法,把这些方法缓存到成员变量 initBinderAdviceCache 里边。这其实就是全局的 @InitBinder 被解析的过程。

  • 接下来看本地的 InitBinder 是在哪里被解析的
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {

        System.out.println("获取数据绑定工厂 WebDataBinderFactory ");

		Class<?> handlerType = handlerMethod.getBeanType();
		Set<Method> methods = this.initBinderCache.get(handlerType);
		if (methods == null) {

          System.out.println("找标记了 InitBinder 注解的方法");
          System.out.println("找标记了 InitBinder 注解的方法");
          System.out.println("找标记了 InitBinder 注解的方法");
      
			methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);

			this.initBinderCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
		// Global methods first
    
        System.out.println("全局的 @InitBinder 方法优先");
        System.out.println("全局的 @InitBinder 方法优先");
        System.out.println("全局的 @InitBinder 方法优先");
    
		this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
			if (clazz.isApplicableToBeanType(handlerType)) {
				Object bean = clazz.resolveBean();
				for (Method method : methodSet) {
					initBinderMethods.add(createInitBinderMethod(bean, method));
				}
			}
		});
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			initBinderMethods.add(createInitBinderMethod(bean, method));
		}
		return createDataBinderFactory(initBinderMethods);
	}

上面的代码是 RequestMappingHandlerAdaptergetDataBinderFactory 方法。可以注意下这个方法的入参 HandlerMethod,也就是我们写的 controller 里边处理请求的方法。这段代码的逻辑就是先获取 HandlerMethod 所在的类,也就是我们的 controller 类,然后找 controller 类中的 @InitBinder 方法,把这些方法解析完了以后放到成员变量 initBinderCache 里边。

到这里,我们就知道不管是全局的 @InitBinder 还是本地的 @InitBinder ,最终都是在被解析后放到了 RequestMappingHandlerAdapter 的成员变量里边。

  • 第二点就是看 InitBinder 方法是在哪里被调用的
	/**
	 * Initialize a WebDataBinder with {@code @InitBinder} methods.
	 * If the {@code @InitBinder} annotation specifies attributes names, it is
	 * invoked only if the names include the target object name.
	 * @throws Exception if one of the invoked @{@link InitBinder} methods fail.
	 */
	@Override
	public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception {

        System.out.println("循环调用所有 @InitBinder 方法");
        System.out.println("循环调用所有 @InitBinder 方法");

		for (InvocableHandlerMethod binderMethod : this.binderMethods) {

			if (isBinderMethodApplicable(binderMethod, binder)) {

				Object returnValue = binderMethod.invokeForRequest(request, null, binder);

                System.out.println("@InitBinder methods should return void ----> @InitBinder 方法应该返回 void");
                System.out.println("@InitBinder methods should return void ----> @InitBinder 方法应该返回 void");

				if (returnValue != null) {
					throw new IllegalStateException(
							"@InitBinder methods should return void: " + binderMethod);
				}
			}
		}
	}

上面这段代码是 InitBinderDataBinderFactory 中的。它的逻辑就是遍历成员变量 binderMethods 并依次调用。关于这个成员变量是在哪里被赋值的,其实是在构造方法中

	public InitBinderDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
			@Nullable WebBindingInitializer initializer) {

		super(initializer);
		this.binderMethods = (binderMethods != null ? binderMethods : Collections.emptyList());
	}

而这个构造方法又被子类 ServletRequestDataBinderFactory 的构造方法调用了

	public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
			@Nullable WebBindingInitializer initializer) {

		super(binderMethods, initializer);
	}

子类 ServletRequestDataBinderFactory 的构造方法又被 RequestMappingHandlerAdapter 调用了

	protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
			throws Exception {

		return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
	}

调用的地方恰恰就是上面讲的 getDataBinderFactory 方法。

到这里,不要迷糊,只是追踪了 InitBinderDataBinderFactory 的成员变量 binderMethods 的赋值链路,并没有追踪 binderMethods 的调用链路。
直接说结论,binderMethods的是在参数解析的阶段被调用的,可以以 RequestResponseBodyMethodProcessorresolveArgument 为例看下

@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    parameter = parameter.nestedIfOptional();

    System.out.println("HttpMessageConverter 是在这里用的 ------");
    System.out.println("HttpMessageConverter 是在这里用的 ------");
    System.out.println("HttpMessageConverter 是在这里用的 ------");

    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());

    System.out.println("获取参数对应的变量名");
    System.out.println("获取参数对应的变量名");
    String name = Conventions.getVariableNameForParameter(parameter);
    System.out.println("获取参数对应的变量名,得到的值是 " + name);

    if (binderFactory != null) {

        System.out.println("创建 WebDataBinder -----> 开始");
        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        System.out.println("创建 WebDataBinder -----> 结束");


        if (arg != null) {
            // 参数校验是在这里做的
            // 参数校验是在这里做的
            // 参数校验是在这里做的
            System.out.println("----- 参数校验是在这里做的 ---- 开始");
            System.out.println("----- 参数校验是在这里做的 ---- 开始");
            System.out.println("----- 参数校验是在这里做的 ---- 开始");

            validateIfApplicable(binder, parameter);

            System.out.println("----- 参数校验是在这里做的 ---- 结束");

            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {

                System.out.println("参数校验失败的话就抛出 MethodArgumentNotValidException 。");

                throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
            }
        }
        if (mavContainer != null) {
            mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
        }
    }

    return adaptArgumentIfNecessary(arg, parameter);
}

@InitBinder 方法就是在如下这段代码中被调用的

WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);

多说一嘴,方法的调用链很长,可以根据本文的线索自己追踪。还有一点,RequestMappingHandlerAdapter 是一个很重要的处理器适配器,需要深入仔细研究。

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

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

相关文章

Apache JMeter 压力测试使用说明

文章目录 一、 安装步骤步骤一 下载相关的包步骤二 安装 Jmeter步骤三 设置 Jmeter 工具语言类型为中文 二、使用工具2.1 创建测试任务步骤一 创建线程组步骤二 创建 HTTP 请求 2.2 配置 HTTP 默认参数添加 HTTP消息头管理器HTTP请求默认值 2.3 添加 查看结果监听器2.4 查看结果…

计算机网络 (40)域名系统DNS

前言 计算机网络域名系统DNS&#xff08;Domain Name System&#xff09;是互联网的基础技术之一&#xff0c;它负责将人类可读的域名转换为计算机用来通信的数字IP地址。 一、基本概念 DNS的主要目的是将域名解析或翻译为IP地址&#xff0c;使得用户可以通过简单易记的域名来访…

本地服务器Docker搭建个人云音乐平台Splayer并实现远程访问告别烦人广告

前言 大家好&#xff01;今天我要给大家分享的是如何在Ubuntu上用Docker快速搭建高颜值无广告的某抑云音乐播放器Splayer的详细流程&#xff0c;并且结合cpolar内网穿透工具实现远程访问。如果你是音乐爱好者&#xff0c;经常需要在外办公或旅行&#xff0c;这个教程绝对能让你…

黑马linux入门笔记(01)初始Linux Linux基础命令 用户和权限 实用操作

B站 黑马程序员 的视频 BV1n84y1i7td 黑马程序员新版Linux零基础快速入门到精通&#xff0c;全涵盖linux系统知识、常用软件环境部署、Shell脚本、云平台实践、大数据集群项目实战等 增强自控力 冥想慢呼吸绿色锻炼充分休息减少决策次数优先做重要的事情(早晨)融入强自控群控…

小程序组件 —— 31 事件系统 - 事件绑定和事件对象

小程序中绑定事件和网页开发中绑定事件几乎一致&#xff0c;只不过在小程序不能通过 on 的方式绑定事件&#xff0c;也没有 click 等事件&#xff0c;小程序中绑定事件使用 bind 方法&#xff0c;click 事件也需要使用 tap 事件来进行代替&#xff0c;绑定事件的方式有两种&…

UE5 使用内置组件进行网格切割

UE引擎非常强大&#xff0c;直接内置了网格切割功能并封装为蓝图节点&#xff0c;这项功能在UE4中就存在&#xff0c;并且无需使用Chaos等模块。那么就来学习下如何使用内置组件实现网格切割。 1.配置测试用StaticMesh 对于被切割的模型&#xff0c;需要配置一些参数。以UE5…

ue5 1.平A,两段连击蒙太奇。鼠标点一下,就放2段动画。2,动画混合即融合,边跑边挥剑,3,动画通知,动画到某一帧,把控制权交给蓝图。就执行蓝图节点

新建文件夹 创建一个蒙太奇MA_Melee 找到c_slow 调节一下速度 把D_slow拖上去 中间加一个片段 哎呀呀&#xff0c;写错了&#xff0c;我想写2 把这个标记拖过来&#xff0c;点击默认default 弄第二个片段 就会自己变成这个样子 把2这个标记拖到中间 鼠标左键&a…

《机器学习》之K-means聚类

目录 一、简介 二、K-means聚类实现步骤 1、初始化数据点、确定K值 2、通过距离分配数据点 3、更新簇中心 4、 迭代更新 三、聚类效果评价方式 1、轮廓系数的定义 2、整体轮廓系数 3、使用场景 4、优点 5、缺点 6、代码实现方法 四、K-means聚类代码实现 1、API接…

Wireshark抓包教程(2024最新版个人笔记)

改内容是个人的学习笔记 Wireshark抓包教程&#xff08;2024最新版&#xff09;_哔哩哔哩_bilibili 该课程笔记1-16 wireshark基础 什么是抓包工具&#xff1a;用来抓取数据包的一个软件 wireshark的功能&#xff1a;用来网络故障排查&#xff1b;用来学习网络技术 wireshark下…

Web开发(一)HTML5

Web开发&#xff08;一&#xff09;HTML5 写在前面 参考黑马程序员前端Web教程做的笔记&#xff0c;主要是想后面自己搭建网页玩。 这部分是前端HTML5CSS3移动web视频教程的HTML5部分。主要涉及到HTML的基础语法。 HTML基础 标签定义 HTML定义 HTML(HyperText Markup Lan…

RabbitMQ 的工作模式

目录 工作模式 Simple&#xff08;简单模式&#xff09; Work Queue&#xff08;工作队列&#xff09; Publish/Subscribe&#xff08;发布/订阅&#xff09; Exchange&#xff08;交换机&#xff09; Routing&#xff08;路由模式&#xff09; Topics&#xff08;通配…

备战蓝桥杯:树的存储与遍历(dfs和bfs)

树的概念 树的逻辑结构是树形结构&#xff0c;和我们之前的线性结构又不太一样了&#xff0c;是一种一对多的关系 树的结点分为根节点&#xff0c;叶子结点&#xff08;没有分支的结点&#xff09; 以及分支结点 从上往下看&#xff0c;每个结点都有0个或多个后继 从下往上…

超大规模分类(三):KNN softmax

传统的分类损失计算输入数据和每个类别中心的距离&#xff0c;来优化模型的训练。KNN softmax通过选择和输入数据最相关的top-K个类别&#xff0c;仅计算输入数据和top-K个类别中心的距离&#xff0c;以减小计算量。 KNN softmax首次诞生于达摩院机器智能技术实验室发表的SIGKD…

ubuntu官方软件包网站 字体设置

在https://ubuntu.pkgs.org/22.04/ubuntu-universe-amd64/xl2tpd_1.3.16-1_amd64.deb.html搜索找到需要的软件后&#xff0c;点击&#xff0c;下滑&#xff0c; 即可在Links和Download找到相关链接&#xff0c;下载即可&#xff0c; 但是找不到ros的安装包&#xff0c; 字体设…

项目实战——使用python脚本完成指定OTA或者其他功能的自动化断电上电测试

前言 在嵌入式设备的OTA场景测试和其他断电上电测试过程中&#xff0c;有的场景发生在夜晚或者随时可能发生&#xff0c;这个时候不可能24h人工盯着&#xff0c;需要自动化抓取串口日志处罚断电上电操作。 下面的python脚本可以实现自动抓取串口指定关键词&#xff0c;然后触发…

电脑分辨率调到为多少最佳?电脑分辨率最佳设置

电脑分辨率是指电脑屏幕上显示的像素点的数量&#xff0c;通常用水平和垂直方向的像素点数来表示&#xff0c;例如19201080。像素点越多&#xff0c;显示的内容就越清晰&#xff0c;但也会占用更多的系统资源和电力。那么多电脑分辨率多少最佳&#xff1f;以及电脑分辨率如何调…

代码随想录算法【Day20】

Day20 二叉搜索树 235. 二叉搜索树的最近公共祖先 理解只要当前节点的值在p和q节点的值的中间&#xff0c;那这个值就是最近的公共祖先&#xff0c;绝对不是次近的&#xff0c;这个题就好做了。 递归法 二叉搜索树本身是有序的&#xff0c;所以不涉及到前中后序的遍历 cl…

【SpringBoot】@Value 没有注入预期的值

问题复现 在装配对象成员属性时&#xff0c;我们常常会使用 Autowired 来装配。但是&#xff0c;有时候我们也使用 Value 进行装配。不过这两种注解使用风格不同&#xff0c;使用 Autowired 一般都不会设置属性值&#xff0c;而 Value 必须指定一个字符串值&#xff0c;因为其…

车联网安全 -- 数字证书到底证明了什么?

在车联网安全--TLS握手过程详解里面&#xff0c;我们了解到握手时&#xff0c;Server会向Client发送Server Certificate&#xff0c;用于证明自己的身份合法&#xff0c;为什么会有这一步呢&#xff1f; 我们回顾一下数字签名的过程&#xff1a; Bob将使用自己的公钥对“Hello…

Elasticsarch:使用全文搜索在 ES|QL 中进行过滤 - 8.17

8.17 在 ES|QL 中引入了 match 和 qstr 函数&#xff0c;可用于执行全文过滤。本文介绍了它们的作用、使用方法、与现有文本过滤方法的区别、当前的限制以及未来的改进。 ES|QL 现在包含全文函数&#xff0c;可用于使用文本查询过滤数据。我们将回顾可用的文本过滤方法&#xf…