mybatis源码学习-3-解析器模块

1. 目录结构

image-20230902211807278

  1. XNode类

    • 作用:XNode 类表示XML文档中的一个节点(Element或Node),它用于封装XML节点的信息,例如标签名、属性和子节点等。
    • 使用场景:MyBatis使用 XNode 来解析XML配置文件中的各种元素,如<select>, <update>, <insert>, <delete>等,并提供了一种方便的方式来访问这些元素的属性和子元素。
  2. PropertyParser类

    • 作用:PropertyParser 类是MyBatis用于解析属性表达式的工具类。属性表达式是在XML配置文件中引用JavaBean属性的方式,通常用于动态SQL。
    • 使用场景:MyBatis在解析动态SQL语句或属性表达式时,会使用 PropertyParser 来解析${}#{}中的属性表达式,并将其替换为实际的属性值或SQL参数。
  3. GenericTokenParser类

    • 作用:GenericTokenParser 类是MyBatis用于解析通用占位符(例如${}#{})的工具类。它允许你指定一个TokenHandler接口的实现,用于处理占位符内的内容。
    • 使用场景:MyBatis在解析SQL语句中的占位符时,会使用 GenericTokenParser 来查找和替换占位符内的内容,然后将处理后的SQL语句返回。
  4. ParsingException类

    • 作用:ParsingException 类是MyBatis中的自定义异常类,用于表示解析过程中的异常情况。它通常用于捕获和处理解析XML配置文件或SQL语句时可能发生的错误。
    • 使用场景:当MyBatis在解析配置文件或SQL语句时遇到不合法的语法或结构时,会抛出 ParsingException 异常,以便开发者识别和处理问题。
  5. TokenHandler接口

    • 作用:TokenHandler 接口是一个回调接口,定义了如何处理占位符内的内容。它用于与 GenericTokenParser 类一起工作,允许开发者自定义处理占位符内部内容的逻辑。
    • 使用场景:当MyBatis使用 GenericTokenParser 解析占位符时,它会调用 TokenHandler 接口的实现来处理占位符内的内容。开发者可以实现自定义的 TokenHandler 来定义处理逻辑,例如从配置文件或参数中获取属性值。
  6. XPathParser类

    • 作用:XPathParser 类是MyBatis中的XML解析工具,用于解析XML配置文件和映射文件。它提供了一种方便的方式来处理XML文档,包括节点遍历、属性获取、XPath表达式解析等。
    • 使用场景:XPathParser 类通常用于加载和解析MyBatis的XML配置文件,例如mybatis-config.xml和映射文件。它允许MyBatis以一种结构化的方式访问和操作XML配置文件中的内容。

2. XPathParser类

  1. 参数

XPathParser 类通常用于加载和解析MyBatis的XML配置文件,例如mybatis-config.xml和映射文件。它允许MyBatis以一种结构化的方式访问和操作XML配置文件中的内容。

public class XPathParser {private final Document document;private boolean validation;private EntityResolver entityResolver;private Properties variables;private XPath xpath;//省略
}
  • document属性,XML 被解析后,生成的org.w3c.dom.Document对象。

  • validation 属性,是否校验 XML 。一般情况下,值为 true

  • entityResolver属性,org.xml.sax.EntityResolver对象,XML 实体解析器。默认情况下,对 XML 进行校验时,会基于 XML 文档开始位置指定的 DTD 文件或 XSD 文件。例如说,解析mybatis-config.xml 配置文件时,会加载http://mybatis.org/dtd/mybatis-3-config.dtd这个 DTD 文件。但是,如果每个应用启动都从网络加载该 DTD 文件,势必在弱网络下体验非常下,甚至说应用部署在无网络的环境下,还会导致下载不下来,那么就会出现 XML 校验失败的情况。所以,在实际场景下,MyBatis 自定义了 [EntityResolver](# 3. XMLMapperEntityResolver) 的实现,达到使用本地DTD 文件,从而避免下载网络DTD 文件的效果.

  • xpath 属性,javax.xml.xpath.XPath 对象,用于查询 XML 中的节点和元素。如果对 XPath 的使用不了解的胖友,请先跳转 《Java XPath 解析器 - 解析 XML 文档》 中,进行简单学习,灰常简单。

  • variables 属性,变量 Properties 对象,用来替换需要动态配置的属性值。例如:

    <dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/>
    </dataSource>
    
    • variables 的来源,即可以在常用的 Java 配置文件中配置,也可以使用 MyBatis <property /> 标签进行配置。例如:

      <properties resource="org/mybatis/example/config.properties"><property name="username" value="dev_user"/><property name="password" value="F2Fa3!33TYyg"/>
      </properties>
      
      • 这里配置的 usernamepassword 属性,就可以替换上面的 ${username}${password} 这两个动态属性。
      • 具体如何实现的,可以看下面的 PropertyParser#parse(String string, Properties variables) 方法。
  1. 构造方法

解释完属性后,我们可以看见后面有一长串的构造方法,挑选其中一个

/*** 构造 XPathParser 对象** @param xml XML 文件地址* @param validation 是否校验 XML* @param variables 变量 Properties 对象* @param entityResolver XML 实体解析器*/
public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {//1. 公共构造方法commonConstructor(validation, variables, entityResolver);//2. 创建document对象,将xml文件解析成一个document对象this.document = createDocument(new InputSource(new StringReader(xml)));
}
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {this.validation = validation;this.entityResolver = entityResolver;this.variables = variables;// 创建 XPathFactory 对象XPathFactory factory = XPathFactory.newInstance();this.xpath = factory.newXPath();
}
/*** 创建 Document 对象,这里就是简单的调用别人提供的java xml api,就像我们调用netty提供的api那样** @param inputSource XML 的 InputSource 对象* @return Document 对象*/
private Document createDocument(InputSource inputSource) {// important: this must only be called AFTER common constructortry {// 1. 创建 DocumentBuilderFactory 对象DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();// 设置是否验证 XMLfactory.setValidating(validation); factory.setNamespaceAware(false);factory.setIgnoringComments(true);factory.setIgnoringElementContentWhitespace(false);factory.setCoalescing(false);factory.setExpandEntityReferences(true);// 2. 创建 DocumentBuilder 对象DocumentBuilder builder = factory.newDocumentBuilder();// 设置实体解析器builder.setEntityResolver(entityResolver); // 实现都空的builder.setErrorHandler(new ErrorHandler() { @Overridepublic void error(SAXParseException exception) throws SAXException {throw exception;}@Overridepublic void fatalError(SAXParseException exception) throws SAXException {throw exception;}@Overridepublic void warning(SAXParseException exception) throws SAXException {}});// 3. 解析 XML 文件return builder.parse(inputSource);} catch (Exception e) {throw new BuilderException("Error creating document instance.  Cause: " + e, e);}
}

至此,我们可以通过构造方法创建一个对象对xml文件进行解析

例如,在org.apache.ibatis.parsing.XPathParserTest#shouldTestXPathParserMethods方法中,我们可以通过测试代码解析nodelet_test.xml中的元素,通过构造方法后获得的XPathParser对象已经对xml文件进行解析,如下

在这里插入图片描述

  1. eval元素

eval 元素的方法,用于获得 Boolean、Short、Integer、Long、Float、Double、String 类型的元素的值。我们以 #evalString(Object root, String expression) 方法为例子,代码如下:

public String evalString(Object root, String expression) {// 1. 获得指定元素或节点的值String result = (String) evaluate(expression, root, XPathConstants.STRING);// 2. 基于 variables 替换动态值,如果 result 为动态值,还记得variables是什么吗?Properties 对象,用来替换需要动态配置的属性值。result = PropertyParser.parse(result, variables);return result;
}
/*** 获得指定元素或节点的值,例如在上例中箭头所指表达式为"/employee/@id" , 节点为"[#document:null]" , 返回类型为String,那么返回的结果就是result = "${id_var}"** @param expression 表达式* @param root       指定节点* @param returnType 返回类型* @return 值*/
private Object evaluate(String expression, Object root, QName returnType) {try {return xpath.evaluate(expression, root, returnType);} catch (Exception e) {throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);}
}

在这里没有使用动态替换,因此即便result = “${id_var}”,在经过PropertyParser#parse(String string, Properties variables)进行动态解析后,结果依然保持不变.

除了上面的vealString之类的方法,还有一个evalNode()方法,用户获得Node类型的节点的值,代码如下

//返回 Node 数组
public List<XNode> evalNodes(String expression) { return evalNodes(document, expression);
}public List<XNode> evalNodes(Object root, String expression) {// 1. 封装成 XNode 数组List<XNode> xnodes = new ArrayList<>();// 2. 获得 Node 数组NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);for (int i = 0; i < nodes.getLength(); i++) {xnodes.add(new XNode(this, nodes.item(i), variables));}return xnodes;
}//返回 Node 对象
public XNode evalNode(String expression) { return evalNode(document, expression);
}public XNode evalNode(Object root, String expression) {//1. 获得 Node 对象Node node = (Node) evaluate(expression, root, XPathConstants.NODE);if (node == null) {return null;}//2. 封装成 XNode 对象return new XNode(this, node, variables);
}

1处,返回结果有 Node 对象数组两种情况,根据方法参数 expression 需要获取的节点不同。

2处,会将结果Node封装为org.apache.ibatis.parsing.XNode对象,主要为了动态值的替换,例如测试方法org.apache.ibatis.parsing.XPathParserTest#shouldTestXPathParserMethods调用parser.evalNode("/employee/@id").toString().trim()这条语句时,会产生下面的一个XNode对象,也就是获取xml文件中id节点的值了

image-20230903005305441

3. XMLMapperEntityResolver

MyBatis 自定义 EntityResolver 实现类,用于加载本地的 mybatis-3-config.dtdmybatis-3-mapper.dtd 这两个 DTD 文件。

简单介绍一下DTD文件的作用,菜🐤教程

  • DTD文件用于验证和定义相应的XML文件的结构和规则,以确保XML文件的正确性和一致性。解析器会根据publicIdsystemId来决定使用哪个DTD文件进行验证。
    • mybatis-config.dtd 定义了MyBatis配置文件中允许的元素和属性,包括数据源配置、事务管理器配置、映射器配置等。
    • mybatis-mapper.dtd 定义了映射文件中允许的元素和属性,包括SQL语句、参数映射、结果映射等。
public class XMLMapperEntityResolver implements EntityResolver {private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";/*** 本地 mybatis-config.dtd 文件*/private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";/*** 本地 mybatis-mapper.dtd 文件*/private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";/*** 将公共DTD转换为本地DTD** @param publicId publicId 是DTD声明中的 PUBLIC 标识符。它用于指定一个公共标准的标识符,通常用于引用在公共资源中定义的DTD。* @param systemId 是DTD声明中的 SYSTEM 标识符。它用于指定一个系统标准的标识符,通常用于引用本地文件系统或远程资源中的DTD。* @return The InputSource for the DTD** @throws org.xml.sax.SAXException If anything goes wrong*/@Overridepublic InputSource resolveEntity(String publicId, String systemId) throws SAXException {try {if (systemId != null) {String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);// 本地 mybatis-config.dtd 文件if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);// 本地 mybatis-mapper.dtd 文件} else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);}}return null;} catch (Exception e) {throw new SAXException(e.toString());}}private InputSource getInputSource(String path, String publicId, String systemId) {InputSource source = null;if (path != null) {try {// 创建 InputSource 对象InputStream in = Resources.getResourceAsStream(path);source = new InputSource(in);// 设置  publicId、systemId 属性source.setPublicId(publicId);source.setSystemId(systemId);} catch (IOException e) {// ignore, null is ok}}return source;}}

4. GenericTokenParser

在初识mybatis源码时,我会产生这样的疑惑,这里的Token指的是什么,与SpringSecurity中的Token的区别是什么?

在GPT之后😂,给出如下解释

  • 在 MyBatis 中,“token” 通常指的是通用标记解析器(GenericTokenParser)中的占位符标记,用于解析 SQL 语句或其他文本中的占位符,例如 ${variable}#{parameter}。这些占位符标记允许你在 SQL 语句中插入变量或参数值,以动态生成 SQL 查询语句。通用标记解析器会将这些标记替换为实际的值,从而生成最终的 SQL 语句。MyBatis 使用这种机制来支持参数化查询和动态 SQL。
  • 在 Spring Security 中,“token” 通常指的是认证令牌(Authentication Token)或访问令牌(Access Token)的概念。这些令牌用于身份验证和授权,用于验证用户的身份并控制用户对资源的访问。Spring Security 使用这种机制来保护应用程序的安全性,实现认证和授权功能。
public class GenericTokenParser {// 开放令牌,例如 "${"private final String openToken;// 关闭令牌,例如 "}"private final String closeToken;// 用于处理找到的令牌的处理器private final TokenHandler handler;// 构造函数,接收开放令牌,关闭令牌和令牌处理器作为参数public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {this.openToken = openToken;this.closeToken = closeToken;this.handler = handler;}// 解析文本的方法public String parse(String text) {// 如果文本为空,直接返回空字符串if (text == null || text.isEmpty()) {return "";}// 搜索开放令牌的位置int start = text.indexOf(openToken);// 如果没有找到开放令牌,直接返回原文本if (start == -1) {return text;}// 把文本转换为字符数组char[] src = text.toCharArray();int offset = 0;// 用于构建结果的字符串构建器final StringBuilder builder = new StringBuilder();// 用于构建找到的令牌的字符串构建器StringBuilder expression = null;// 当还能找到开放令牌时,执行循环while (start > -1) {// 如果开放令牌前面是反斜杠,那么这个开放令牌是转义的,需要忽略if (start > 0 && src[start - 1] == '\\') {// 添加转义的开放令牌builder.append(src, offset, start - offset - 1).append(openToken);offset = start + openToken.length();} else {// 找到开放令牌,开始搜索关闭令牌if (expression == null) {expression = new StringBuilder();} else {expression.setLength(0);}// 添加开放令牌前面的文本builder.append(src, offset, start - offset);offset = start + openToken.length();int end = text.indexOf(closeToken, offset);// 当还能找到关闭令牌时,执行循环while (end > -1) {// 如果关闭令牌前面是反斜杠,那么这个关闭令牌是转义的,需要忽略if (end > offset && src[end - 1] == '\\') {// 添加转义的关闭令牌expression.append(src, offset, end - offset - 1).append(closeToken);offset = end + closeToken.length();end = text.indexOf(closeToken, offset);} else {// 添加关闭令牌前面的令牌文本expression.append(src, offset, end - offset);offset = end + closeToken.length();break;}}if (end == -1) {// 如果没有找到关闭令牌,添加剩余的文本builder.append(src, start, src.length - start);offset = src.length;} else {// 添加处理后的令牌builder.append(handler.handleToken(expression.toString()));offset = end + closeToken.length();}}// 搜索下一个开放令牌start = text.indexOf(openToken, offset);}// 添加剩余的文本if (offset < src.length) {builder.append(src, offset, src.length - offset);}// 返回构建的字符串return builder.toString();}
}

测试案例org.apache.ibatis.parsing.GenericTokenParserTest

在这里插入图片描述

在这里插入图片描述

像上面两幅图那样,就可以把我们xml文件中诸如#{id}通过反射去转换为具体的id内容了,再去看看带有转义字符的

image-20230903234502608

5. PropertyParser

public class PropertyParser {private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";/*** 特殊属性键,指示是否在占位符上启用默认值。* <p>* 默认值为false (表示禁用占位符上的默认值)如果指定true ,则可以在占位符上指定键和默认值(例如${db.username:postgres} )。* </p>** @since 3.4.2*/public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";/*** 特殊属性键,用于指定占位符上键和默认值的分隔符。* <p>* 默认分隔符是":" 。* </p>** @since 3.4.2*/public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";private static final String ENABLE_DEFAULT_VALUE = "false";private static final String DEFAULT_VALUE_SEPARATOR = ":";private PropertyParser() {// Prevent Instantiation}//1.public static String parse(String string, Properties variables) {VariableTokenHandler handler = new VariableTokenHandler(variables);GenericTokenParser parser = new GenericTokenParser("${", "}", handler);return parser.parse(string);}//2.private static class VariableTokenHandler implements TokenHandler {private final Properties variables;  // 存储变量的属性private final boolean enableDefaultValue;  // 是否启用默认值private final String defaultValueSeparator;  // 默认值分隔符private VariableTokenHandler(Properties variables) {this.variables = variables;// 解析属性值,获取是否启用默认值和默认值分隔符this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);}private String getPropertyValue(String key, String defaultValue) {// 获取属性值,如果属性为空则使用默认值return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);}@Overridepublic String handleToken(String content) {if (variables != null) {String key = content;if (enableDefaultValue) {final int separatorIndex = content.indexOf(defaultValueSeparator);String defaultValue = null;if (separatorIndex >= 0) {key = content.substring(0, separatorIndex);defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());}if (defaultValue != null) {// 如果启用了默认值,并且默认值存在,则返回属性值或默认值return variables.getProperty(key, defaultValue);}}if (variables.containsKey(key)) {// 如果属性中包含该键,则返回属性值return variables.getProperty(key);}}// 如果属性为空或属性中不包含该键,则返回原始的标记内容return "${" + content + "}";}}}

先看看1处的代码,它主要是

  • 创建一个VariableTokenHandler对象

  • 创建一个[GenericTokenParser](# 4. GenericTokenParser)

  • 调用org.apache.ibatis.parsing.GenericTokenParser#parse方法对对象进行动态替换,从而完成动态映射的功能

再来看看2处的代码,它是org.apache.ibatis.parsing.TokenHandler的实现类,主要用于定义一些动态字符串的映射规则,例如将id->123456.

TokenHandler 子类

测试代码org.apache.ibatis.parsing.PropertyParserTest

在这里插入图片描述

写在后面,这里会有很多借鉴的内容,有以下三个原因

  1. 本博客只是作为本人学习记录并用以分享,并不是专业的技术型博客
  2. 笔者是位刚刚开始尝试阅读源码的人,对源码的阅读流程乃至整体架构并不熟悉,观看他人博客可以帮助我快速入门
  3. 如果只是笔者自己观看,难免会有很多弄不懂乃至理解错误的地方,观看他人的体会能有效改善这个问题

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

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

相关文章

长胜证券:资本市场的含义是什么?

本钱商场是指企业和政府通过证券生意来筹集资金并进行出资活动的商场。本钱商场通常被分为两个部分&#xff1a;初级商场和二级商场。初级商场是新证券发行的商场&#xff0c;而二级商场则是已发行证券的生意商场。本钱商场的展开程度是一个国家经济展开的重要目标之一。 从宏…

详解Transformer中的Encoder

一.Transformer架构 左半边是Encoder&#xff0c;右半边是Decoder。 二.Vision Transformer Vision Transformer取了Transformer的左半边。包含 Input EmbeddingPositional Encoding多头注意力机制 Add & Norm(前馈网络)Feed Forward Add & Norm 2.1 Input Embe…

企业架构LNMP学习笔记4

企业服务器LNMP环境搭建&#xff1a; 常见的软件架构&#xff1a; 1&#xff09;C/S: client/server 2&#xff09;B/S: browser/server 不管是C还是B&#xff0c;都是属于客户端属于前端。那么运维人员主要是负责和管理的Server端&#xff0c;也统称为服务器端。为了快速的…

RetroArch 接入两个同款手柄只能识别到一个导致无法双打的问题

测试平台 设备:StationPC M3 RetroArch: 1.1.5(当前官方最新) 手柄:北通蝙蝠BD2F(XBOX360键位) 问题说明 RetroArch插入两个同款手柄/摇杆时只能识别到一个&#xff0c;此时两个手柄都是可以控制模拟器&#xff0c;但是进入游戏也都是p1&#xff0c;无法实现双打 解决办法 …

Window安装虚拟机+给虚拟机安装Linux

一、虚拟机下载 这里使用Virtualbox虚拟机。可以直接从官网下载&#xff1a;Downloads – Oracle VM VirtualBox 点击进行下载&#xff0c;选择window版本的。直接双击&#xff0c;一直下一步 进行安装 PS&#xff1a;安装需要开启CPU虚拟化&#xff0c;一般电脑都已经开启了…

最新社区团购电商小程序源码 无bug完美运营版+详细搭建部署教程

分享一个开源社区团购电商小程序源码&#xff0c;无bug完美运营版&#xff0c;含完整前后端详细搭建部署教程。 系统运营模式&#xff1a;整合线下社区资源&#xff0c;由各快递代收点、社区便利店、社区物业、业主等发起的社区微信群&#xff0c;推送商品信息&#xff0c;消费…

【数据恢复】.360勒索病毒|金蝶、用友、OA、ERP等软件数据库恢复

引言&#xff1a; 在当今数字化的时代&#xff0c;网络犯罪已经演变成了一场全球性的威胁&#xff0c;而 360 勒索病毒则是其中最为可怕和具有破坏性的威胁之一。这种恶意软件以其危害深远、难以防范的特点而令人震惊。本文91数据恢复将深入探讨 360 勒索病毒的可怕性&#xff…

sql:SQL优化知识点记录(九)

&#xff08;1&#xff09;小表驱动大表 对sql调优的分析&#xff1a; 排序优化&#xff1a; 数据库的连接方式&#xff0c;里面的数据尽量这样连接&#xff0c;尽量选择第一个方式&#xff0c;因为两个表的连接一共建立5次连接&#xff0c;第二个建立1000次连接&#xff0c;从…

使用spring自带的发布订阅机制来实现消息发布订阅

背景 公司的项目以前代码里面有存在使用spring自带发布订阅的代码&#xff0c;因此稍微学习一下如何使用&#xff0c;并了解一下这种实现方式的优缺点。 优点 实现方便&#xff0c;代码方面基本只需要定义消息体和消费者&#xff0c;适用于小型应用程序。不依赖外部中间件&a…

h5微传单制作教程:快速轻松制作

在当今社交媒体充斥的时代&#xff0c;微传单作为一种新型的宣传推广方式&#xff0c;成为了企业和个人在传播信息时的重要工具。h5微传单相比传统的纸质传单更加灵活多样&#xff0c;并且能够通过手机、平板等设备随时随地进行浏览和分享&#xff0c;具有很高的传播效果。下面…

SpringMVC_SSM整合

一、回顾SpringMVC访问接口流程 1.容器加载分析 容器分析 手动注册WebApplicationContext public class ServletConfig extends AbstractDispatcherServletInitializer {Overrideprotected WebApplicationContext createServletApplicationContext() {//获取SpringMVC容器An…

Python实现猎人猎物优化算法(HPO)优化卷积神经网络回归模型(CNN回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 猎人猎物优化搜索算法(Hunter–prey optimizer, HPO)是由Naruei& Keynia于2022年提出的一种最新的…

OpenCV(二十八):连通域分割

目录 1.介绍连通域分割 2.像素领域介绍 3.两遍法分割连通域 4.连通域分割函数 1.介绍连通域分割 连通域分割是一种图像处理技术&#xff0c;用于将图像中的相邻像素组成的区域划分为不同的连通域。这些像素具有相似的特性&#xff0c;如相近的灰度值或颜色。连通域分割可以…

ue5 物理场的应用

cable mat wpo particle 流体粒子 choas 破损 刚体 布料 cloud abp blueprint riggedbody 体积雾 毛发 全局的 局部的 非均匀的 连续变化的 也可以多个叠加 从全局 到 范围 除了vector还有scalar的值也就是0--1的黑白灰的值 但是最终输出的值的类型还是取决于这个 一…

链动2+1天天秒商城商业模式

链动21天天秒商城商业模式 在当今市场&#xff0c;一种名为链动21天天的秒杀商城商业模式正在引发广泛关注。这种创新的商业模式具有快速拓展市场的强大能力&#xff0c;让许多用户和商家都感到非常惊讶。那么&#xff0c;这种模式究竟是什么&#xff0c;它又为何具有如此大的…

【前端打怪升级日志之CSS篇】position定位

学习链接&#xff1a;阮一峰CSS定位详解 学习总结&#xff1a; 学习应用&#xff1a;待补充。。。

STM32-DMA

1 DMA简介 DMA&#xff08;Direct Memory Access&#xff09;,中文名为直接内存访问&#xff0c;它是一些计算机总线架构提供的功能&#xff0c;能使数据从附加设备&#xff08;如磁盘驱动器&#xff09;直接发送到计算机主板的内存上。对应嵌入式处理器来说&#xff0c;DMA可…

LINE自动回复:快速回复提升客服效率

2023年&#xff0c;LINE在其4个主要市场&#xff1a;对话、日本、台湾和泰国拥有约1.78亿月活跃用户。 LINE不仅是一个通讯软件&#xff0c;更提供广泛的服务&#xff0c;包括语音和视讯通话、群组、发布社交帖子及商务功能。近年来&#xff0c;越来越多的企业在客户服务中使用…

雷士明轩好用吗?测评师对比横评书客、雷士、米家哪款好

如今&#xff0c;大多数人的日常工作和学习都离不开电子设备&#xff0c;长时间盯着屏幕容易造成眼睛疲劳和视力下降。全国近视率占多数的还是青少年&#xff0c;护眼台灯作为一种照明设备&#xff0c;具有调节光线亮度和色温的功能&#xff0c;可以有效减少眼睛的疲劳&#xf…

Day_81-87 CNN卷积神经网络

目录 一. CNN卷积神经网络与传统神经网络的不同 1. 模型图 2. 参数分布情况 3. 卷积神经网络和传统神经网络的层次结构 4. 传统神经网络的缺点&#xff1a; 二. CNN的基本操作 1. 卷积 2. 池化 三. CNN实现过程 1. 算法流程图 2. 输入层 3. 卷积层 4. 激活层 5. 池化层 6. 全连…