解锁Spring Boot中的设计模式—02.解释器模式:探索【解释器模式】的奥秘与应用实践!

解释器模式

1.简介

解释器模式(Interpreter Pattern)是一种行为设计模式,它用于定义语言的文法,并且解释语言中的表达式。在Java中,解释器模式可以用于构建解释器以解析特定的语言或表达式,如数学表达式、查询语言等。

优点:

  1. 灵活性: 解释器模式可以灵活地添加新的表达式和规则,因此适用于处理不断变化的语言或表达式。
  2. 易扩展性: 可以轻松地扩展文法,添加新的解释器,而不会影响现有的解释器。
  3. 简化语法分析: 将文法规则分解成一个个小的解释器,使得语法分析更加简单和清晰。
  4. 易于实现: 每个表达式都可以由一个简单的解释器实现,降低了系统的复杂度易于实现和维护

缺点:

  1. 复杂度高: 对于复杂的文法规则,需要创建大量的解释器对象,可能会导致类爆炸问题,增加系统的复杂度。
  2. 执行效率低: 解释器模式通常采用递归调用的方式进行解释,可能会导致解释过程比较耗时,执行效率低下。
  3. 维护困难: 随着文法的变化和解释器的增加,维护成本可能会增加,特别是在处理复杂语言时。

使用场景:

  1. 特定领域语言(DSL)解析: 适用于需要解析和执行特定领域语言的场景,如数学表达式正则表达式查询语言等。
  2. 规则引擎: 用于构建规则引擎,根据不同的规则执行相应的操作。
  3. 编译器和解释器设计: 可用于编写编译器和解释器,将源代码转换成目标代码或执行相应的操作。
  4. 配置文件解析: 适用于解析配置文件,将配置信息转换成相应的对象或操作

2.案例-生成SQL建表语句

需求描述:

假设我们需要开发一个简单的数据库表生成工具,它可以根据用户提供的列信息生成相应的 SQL 建表语句。用户可以指定表名、列名、数据类型等信息,并且可以选择是否设置列为主键、非空等约束。

用户需求:

  1. 用户希望能够指定表名以及列的详细信息,包括列名、数据类型、长度等。
  2. 用户希望能够选择是否将某列设置为主键、非空等约束。
  3. 用户希望能够生成符合特定数据库类型的建表语句,例如 PostgreSQL、MySQL 等。

实现思路:

  1. 定义抽象表达式接口
    • 创建一个接口,定义解释方法 interpret(),该方法将返回解释后的结果。
  2. 创建终结符表达式类
    • 实现抽象表达式接口的类,用于表示文法中的最小单元。
    • 包含需要解释的数据和解释方法的具体实现。
  3. 创建非终结符表达式类
    • 实现抽象表达式接口的类,用于表示文法中的复合结构。
    • 包含其他表达式对象或者其他操作的组合,并实现解释方法。
  4. 创建环境类(Context)
    • 封装解释器需要的数据和方法。
    • 提供执行解释器的方法,并返回结果。
    • 解耦解释器与客户端代码: Context提供统一接口,客户端代码不需要直接操作解释器,降低了耦合度。
    • 封装解释器的创建逻辑: Context封装解释器的创建逻辑,使客户端代码更简洁,只需调用Context方法即可执行解释器。
    • 统一的异常处理机制: Context提供统一异常处理,捕获解释器执行过程中的异常,进行统一处理,如记录日志、返回错误信息。
    • 支持扩展和替换解释器: 可以通过修改Context中的创建逻辑来引入新的解释器实现或替换现有实现,提高系统的灵活性和可扩展性。
  5. 客户端代码
    • 创建具体的终结符和非终结符表达式对象。
    • 创建环境对象,将表达式对象传递给环境对象。
    • 调用环境对象的解释方法,获取解释结果。

2.1.实现

2.1.1.抽共享表达式接口
/*** 定义抽象表达式接口* @author 13723* @version 1.0* 2024/2/6 10:01*/
public interface Expression {/*** 解释方法* @return 解释结果*/String interpret();
}
2.1.2.终结符表达式类
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;/*** 终结符实现类* 终结符(Terminal Symbol):在文法中,终结符是指不能进一步被分解的符号或标记。* 在语法分析过程中,终结符是输入字符的最小单元。终结符是文法的基本元素,通常代表实际的词汇或标记。* 文法中的每一个终结符都有一个具体终结表达式与之相对应。*/
@Getter @Setter
@NoArgsConstructor
public class ColumnExpression implements Expression {/*** 列名*/private String name;/*** 列类型*/private String type;/*** 列长度*/private String length;/*** 注释*/private String comment;/*** 是否是主键*/private Boolean primaryKey = false;/*** 是否是非空*/private Boolean notNull = false;public ColumnExpression(String name, String type, String length) {this.name = name;this.type = type;this.length = length;}public ColumnExpression(String name, String type, String length, String comment) {this.name = name;this.type = type;this.length = length;this.comment = comment;}public ColumnExpression(String name, String type, String length, String comment, Boolean notNull) {this.name = name;this.type = type;this.length = length;this.comment = comment;this.notNull = notNull;}public ColumnExpression(String name, String type, String length, String comment, Boolean primaryKey, Boolean notNull) {this.name = name;this.type = type;this.length = length;this.primaryKey = primaryKey;this.notNull = notNull;this.comment = comment;}@Overridepublic String interpret() {StringBuilder sb = new StringBuilder();sb.append("\t").append("\"").append(name).append("\"").append("  ").append(type).append("(").append(length).append(")");// 如果设置了不为空,根据数据类型添加 NOT NULLif (notNull) {if ("VARCHAR".equals(type) || "CHAR".equals(type) || "TEXT".equals(type) || "BLOB".equals(type) || "CLOB".equals(type) || "NCLOB".equals(type)){sb.append("  COLLATE \"pg_catalog\".\"default\" NOT NULL" );}else {sb.append(" NOT NULL");}}return sb.toString();}
}
2.1.3.非终结符表达式类
package com.hrfan.java_se_base.pattern.Interpreter_pattern.se;import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 非终结符(Nonterminal Symbol):非终结符是可以被进一步分解的符号或标记,在文法规则中用来描述语言的结构和组织。* 非终结符通常代表语法结构中的一类元素,可以是语法规则的左侧或右侧的一部分。* 文法中的每条规则都对应于一个非终结符表达式。*/
@Getter @Setter
public class TableExpression  implements Expression{private String tableName;private List<ColumnExpression> columns;public TableExpression(String tableName) {this.tableName = tableName;this.columns = new ArrayList<>();}// 添加列public void addColumn(ColumnExpression column) {columns.add(column);}// 获取设置主键的数据public List<String> getColumnPrimaryKey(){List<String> collect = columns.stream().filter(ColumnExpression::getPrimaryKey).map(ColumnExpression::getName).collect(Collectors.toList());return collect;}// 设置注释 和 字段名称public Map<String,String> getColumnComment(){Map<String, String> map = columns.stream().filter(it -> StringUtils.isNotBlank(it.getComment())).collect(Collectors.toMap(ColumnExpression::getName, ColumnExpression::getComment));return map;}@Overridepublic String interpret() {// 获取主键的列名List<String> columnPrimaryKey = getColumnPrimaryKey();StringBuilder sb = new StringBuilder("\nCREATE TABLE \"gwstd\".\"" + tableName + "\" " + "(" + "\n");for (int i = 0; i < columns.size(); i++) {// 执行非终结符解释逻辑sb.append(columns.get(i).interpret());if (i < columns.size() - 1 || columnPrimaryKey.size() > 0){sb.append(", \n");}}if (columnPrimaryKey.size() > 0){sb.append("\tCONSTRAINT ").append("\"").append(tableName).append("_pkey\"").append("PRIMARY KEY (");for (int i = 0; i < columnPrimaryKey.size(); i++) {sb.append("\"").append(columnPrimaryKey.get(i)).append("\"");if (i < columnPrimaryKey.size() - 1) {sb.append(",");}}sb.append(")\n");}sb.append(");");// 生成输入语句sb.append("\n");sb.append("ALTER TABLE \"gwstd\".").append("\"").append(tableName).append("\"").append(" OWNER TO \"postgres\"").append(";");// 生成注释Map<String, String> columnComment = getColumnComment();if (!columnComment.isEmpty()){for (Map.Entry<String, String> entry : columnComment.entrySet()) {sb.append("\n");// COMMENT ON COLUMN "gwstd"."t_dec_order_head"."sid" IS '主键Sid';sb.append("COMMENT ON COLUMN \"gwstd\".").append("\"").append(tableName).append("\"").append(".").append("\"").append(entry.getKey()).append("\"").append(" IS '").append(entry.getValue()).append("';");}}return sb.toString();}
}
2.1.4.创建Context(承上启下)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.lang.invoke.MethodHandles;/*** @author 13723* @version 1.0* 2024/2/6 10:17*/
public class Context {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());/*** 建表语句表达式*/private Expression expression;public Context(){}/*** 解释方法*/public Context(Expression expression){this.expression = expression;}/*** 执行解释方法* @return 解释结果*/public String interpret(){return expression.interpret();}
}
2.1.5.测试类
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.lang.invoke.MethodHandles;/*** @author 13723* @version 1.0* 2024/2/6 10:20*/
public class ExpressCreateTest {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Test@DisplayName("测试建表语句解释器")public void test(){// 创建表对象(使用建造者模式构建表对象)TableExpressionBuilder builder = new TableExpressionBuilder("t_dec_erp_test_students");// 非终结符解释器 中 添加 终结符解释器// 字段名称,字段类型,字段长度,字段注释,是否为主键,是否为空builder.addColumn(new ColumnExpression("sid",  "VARCHAR", "11","主键sid",true,true)).addColumn(new ColumnExpression("name", "VARCHAR", "255","姓名")).addColumn(new ColumnExpression("age",   "NUMERIC", "3","年龄",true)).addColumn(new ColumnExpression("email", "VARCHAR", "255","邮箱",true)).addColumn(new ColumnExpression("address", "VARCHAR", "255","联系地址")).addColumn(new ColumnExpression("telephone", "VARCHAR", "255","联系电话"));// 构建表达式TableExpression expression = builder.build();// 通过Context执行解释方法,获取解释结果.String interpret = new Context(expression).interpret();logger.error("最终生成sql:\n{}",interpret);}
}

在这里插入图片描述

CREATE TABLE "gwstd"."t_dec_erp_test_students" ("sid"  VARCHAR(11)  COLLATE "pg_catalog"."default" NOT NULL, "name"  VARCHAR(255), "age"  NUMERIC(3) NOT NULL, "email"  VARCHAR(255)  COLLATE "pg_catalog"."default" NOT NULL, "address"  VARCHAR(255), "telephone"  VARCHAR(255), CONSTRAINT "t_dec_erp_test_students_pkey"PRIMARY KEY ("sid")
);
ALTER TABLE "gwstd"."t_dec_erp_test_students" OWNER TO "postgres";
COMMENT ON COLUMN "gwstd"."t_dec_erp_test_students"."address" IS '联系地址';
COMMENT ON COLUMN "gwstd"."t_dec_erp_test_students"."name" IS '姓名';
COMMENT ON COLUMN "gwstd"."t_dec_erp_test_students"."telephone" IS '联系电话';
COMMENT ON COLUMN "gwstd"."t_dec_erp_test_students"."email" IS '邮箱';
COMMENT ON COLUMN "gwstd"."t_dec_erp_test_students"."age" IS '年龄';
COMMENT ON COLUMN "gwstd"."t_dec_erp_test_students"."sid" IS '主键sid';

在这里插入图片描述

在这里插入图片描述

2.2.Spring使用场景

2.2.1.Spring-SpEL

Spring表达式语言(SpEL)允许在运行时动态计算值,它可以用于配置文件、注解等多种场景中。在SpEL的背后,实际上就是使用了解释器模式。

Spring框架中,与Spring EL(表达式语言)相关的类位于org.springframework.expression包下。以下是几个与Spring EL密切相关的类的简要介绍:

  1. SpelExpression: 代表一个EL表达式,它在内部通过抽象语法树(AST)来表示表达式。EL表达式的求值是通过调用this.ast.getValue(expressionState);来实现的。
  2. ExpressionParser: 表达式解析器接口,定义了解析EL表达式的方法。Spring提供了SpelExpressionParser作为默认的表达式解析器实现。
  3. StandardEvaluationContext: 标准的评估上下文,用于在表达式求值期间存储变量和函数。它提供了用于设置变量和函数的方法,这些变量和函数可以在EL表达式中使用。
  4. EvaluationContext: 评估上下文接口,用于在表达式求值期间提供变量和函数的访问。StandardEvaluationContextEvaluationContext接口的实现之一。
  5. SpelNode: SpEL语法树的节点,代表了EL表达式的各个部分。每个节点都有自己的类型和操作,用于实现EL表达式的解析和求值。
2.2.1.1.案例代码
@Test
@DisplayName("测试@Value")
public void test2(){// 1. 构建解析器ExpressionParser parser = new SpelExpressionParser();// 2. 解析表达式Expression expression = parser.parseExpression("100 * 2 + 200 * 1 + 100000");// 3. 获取结果int result = (Integer) expression.getValue();// 4. 打印结果logger.error("result:{}",result);
}

在这里插入图片描述

语法树

抽象语法树(Abstract Syntax Tree,AST)是一种用于表示编程语言代码结构的树形数据结构

在编译器和解释器中,AST是一种常见的数据结构,用于表示程序代码的语法结构和语义信息

在Java中,AST抽象语法树是指代表Java源代码结构的树形数据结构。它由多个节点组成,每个节点代表源代码中的一个语法元素,例如表达式、语句、方法调用等。AST的根节点表示整个程序的起始点,而子节点则代表程序中的具体语法结构。

# 语法树        +/   \*     +/  \   /  \100  2  *  100000/ \200  1
2.2.1.2.SpelExpression

SpelExpression 是 Spring 表达式语言(SpEL)中的一个组成部分,用于表达特定的表达式。它内部采用抽象语法树(AST)来表示表达式的结构。计算表达式的值是通过调用 this.ast.getValue(expressionState); 实现的。在这个过程中,AST被用来解释和执行表达式,最终得到表达式的计算结果。

public class SpelExpression implements Expression {@Nullablepublic Object getValue() throws EvaluationException {CompiledExpression compiledAst = this.compiledAst;if (compiledAst != null) {try {EvaluationContext context = this.getEvaluationContext();return compiledAst.getValue(context.getRootObject().getValue(), context);} catch (Throwable var4) {if (this.configuration.getCompilerMode() != SpelCompilerMode.MIXED) {throw new SpelEvaluationException(var4, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION, new Object[0]);}}this.compiledAst = null;this.interpretedCount.set(0);}ExpressionState expressionState = new ExpressionState(this.getEvaluationContext(), this.configuration);Object result = this.ast.getValue(expressionState);this.checkCompile(expressionState);return result;}
}
2.2.1.2.SpelNodeImpl

SpelNodeImpl 是 Spring 表达式语言(SpEL)中的关键类,用于表示已解析的表达式在抽象语法树(AST)中的节点。

在解释器模式中,AST 的节点充当了终结符和非终结符的角色,帮助构建表达式的结构。

SpelNodeImpl 的子类包括以下几种类型:

  • Literal: 代表各种类型值的父类。
  • Operator: 代表各种操作符的父类。
  • Indexer 等:代表其他类型的节点。
public abstract class SpelNodeImpl implements SpelNode, Opcodes {private static final SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];protected SpelNodeImpl[] children;@Nullableprivate SpelNodeImpl parent;@Nullableprotected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), this.getValueInternal(state), desiredReturnType);}// 抽象方法子类实现,获取对象值public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;}
2.2.1.3.IntLiteral

IntLiteral 表示整型文字的表达式语言的ast结点

public class IntLiteral extends Literal {private final TypedValue value;public IntLiteral(String payload, int startPos, int endPos, int value) {super(payload, startPos, endPos);this.value = new TypedValue(value);this.exitTypeDescriptor = "I";}public TypedValue getLiteralValue() {return this.value;}// 
}
2.2.1.4.OpPlus

OpPlus 表示加法的ast结点,在 getValueInternal 方法中对操作符两边进行相加操作

public class OpPlus extends Operator {private static final int MAX_CONCATENATED_STRING_LENGTH = 100000;public OpPlus(int startPos, int endPos, SpelNodeImpl... operands) {super("+", startPos, endPos, operands);Assert.notEmpty(operands, "Operands must not be empty");}public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {SpelNodeImpl leftOp = this.getLeftOperand();if (this.children.length < 2) {Object operandOne = leftOp.getValueInternal(state).getValue();if (operandOne instanceof Number) {if (operandOne instanceof Double) {this.exitTypeDescriptor = "D";} else if (operandOne instanceof Float) {this.exitTypeDescriptor = "F";} else if (operandOne instanceof Long) {this.exitTypeDescriptor = "J";} else if (operandOne instanceof Integer) {this.exitTypeDescriptor = "I";}return new TypedValue(operandOne);} else {return state.operate(Operation.ADD, operandOne, (Object)null);}} else {// 递归调用leftOp的 getValueInternal(state) ,获取操作符左边的值TypedValue operandOneValue = leftOp.getValueInternal(state);Object leftOperand = operandOneValue.getValue();// 递归调用children[1]的 getValueInternal(state) ,获取操作符右边的值TypedValue operandTwoValue = this.getRightOperand().getValueInternal(state);Object rightOperand = operandTwoValue.getValue();// 如果操作符左右都是数值类型,则将它们相加if (leftOperand instanceof Number && rightOperand instanceof Number) {Number leftNumber = (Number)leftOperand;Number rightNumber = (Number)rightOperand;if (!(leftNumber instanceof BigDecimal) && !(rightNumber instanceof BigDecimal)) {if (!(leftNumber instanceof Double) && !(rightNumber instanceof Double)) {if (!(leftNumber instanceof Float) && !(rightNumber instanceof Float)) {if (!(leftNumber instanceof BigInteger) && !(rightNumber instanceof BigInteger)) {if (!(leftNumber instanceof Long) && !(rightNumber instanceof Long)) {if (!CodeFlow.isIntegerForNumericOp(leftNumber) && !CodeFlow.isIntegerForNumericOp(rightNumber)) {return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());} else {this.exitTypeDescriptor = "I";return new TypedValue(leftNumber.intValue() + rightNumber.intValue());}} else {this.exitTypeDescriptor = "J";return new TypedValue(leftNumber.longValue() + rightNumber.longValue());}} else {BigInteger leftBigInteger = (BigInteger)NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);BigInteger rightBigInteger = (BigInteger)NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);return new TypedValue(leftBigInteger.add(rightBigInteger));}} else {this.exitTypeDescriptor = "F";return new TypedValue(leftNumber.floatValue() + rightNumber.floatValue());}} else {this.exitTypeDescriptor = "D";return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());}} else {BigDecimal leftBigDecimal = (BigDecimal)NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);BigDecimal rightBigDecimal = (BigDecimal)NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);return new TypedValue(leftBigDecimal.add(rightBigDecimal));}} else {String rightString;String leftString;if (leftOperand instanceof String && rightOperand instanceof String) {this.exitTypeDescriptor = "Ljava/lang/String";rightString = (String)leftOperand;leftString = (String)rightOperand;this.checkStringLength(rightString);this.checkStringLength(leftString);return this.concatenate(rightString, leftString);} else if (leftOperand instanceof String) {rightString = (String)leftOperand;this.checkStringLength(rightString);leftString = rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state);this.checkStringLength(leftString);return this.concatenate(rightString, leftString);} else if (rightOperand instanceof String) {rightString = (String)rightOperand;this.checkStringLength(rightString);leftString = leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state);this.checkStringLength(leftString);return this.concatenate(leftString, rightString);} else {return state.operate(Operation.ADD, leftOperand, rightOperand);}}}}
}

解释器模式相对于其他设计模式确实在实际应用中使用较少,这主要是由于以下几个原因:

  1. 复杂性和性能开销: 解释器模式的实现可能会引入较高的复杂性和性能开销。解释器需要对输入进行解析、分析和执行,这可能导致性能上的损失,并且随着解释器规则的增多,代码的复杂度也会增加。
  2. 不易理解和维护: 解释器模式的实现通常较为复杂,需要设计者对语法和语义有深入的理解,同时需要维护大量的解释器规则。这使得解释器模式在代码的可读性和可维护性方面存在挑战。
  3. 有限的适用场景: 解释器模式通常适用于特定领域的问题,例如编译器、正则表达式引擎等。在许多常见的软件开发场景中,并不经常需要使用解释器模式来解决问题。
  4. 其他替代方案: 对于一些需要解释和执行逻辑的问题,通常有更简单、更高效的替代方案,例如使用编译器、脚本引擎、规则引擎等。这些替代方案能够更直接地执行逻辑,而不需要通过解释器的中间层。

尽管解释器模式在一些特定的领域和场景下仍然有其价值,但在许多常见的软件开发任务中,它并不是第一选择。因此,虽然解释器模式是一种重要的设计模式,但其应用相对较少。

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

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

相关文章

Unity中,C#的事件与委托区别和经典实例

文章目录 实例1&#xff1a;委托&#xff08;Delegate&#xff09;的基本用法实例2&#xff1a;事件&#xff08;Event&#xff09;的声明与订阅实例3&#xff1a;Unity引擎中的委托实例 - UI Button.onClick实例4&#xff1a;事件&#xff08;Event&#xff09;的安全性实例5&…

C#安装CommunityToolkit.Mvvm依赖

这里需要有一定C#基础&#xff0c; 首先找到右边的解决方案&#xff0c;右键依赖项 然后选择nuget管理 这里给大家扩展一下nuget的国内源&#xff08;https://nuget.cdn.azure.cn/v3/index.json&#xff09; 然后搜自己想要的依赖性&#xff0c;比如CommunityToolkit.Mvvm 再点…

面试经典150题【1-10】

文章目录 面试经典150题【1-10】88. 合并两个有序数组27.移除元素26.删除有序数组中的重复项80.删除有序数组中的重复项II169.多数元素189.轮转数组121.买卖股票的最佳时机1122. 买卖股票的最佳时机 II55.跳跃游戏![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ff…

【智能家居入门4】(FreeRTOS、MQTT服务器、MQTT协议、微信小程序)

前面已经发了智能家居入门的1、2、3了&#xff0c;在实际开发中一般都会使用到实时操作系统&#xff0c;这里就以FreeRTOS为例子&#xff0c;使用标准库。记录由裸机转到实时操作系统所遇到的问题以及总体流程。相较于裸机&#xff0c;系统实时性强了很多&#xff0c;小程序下发…

Midjourney风格一致功能解读及使用方法

Midjourneys再次迎来更新&#xff0c;本次新增“风格一致”功能&#xff01;用户期待已久的风格模仿功能终于实现了&#xff01; --sref 虽然目前只是测试功能&#xff0c;但已经相当强大了&#xff0c;这篇文章我将带大家先睹为快&#xff01; 别忘了&#xff0c;这个功能目前…

带你了解软件系统架构的演变

随着信息技术的飞速发展&#xff0c;软件系统架构作为支撑软件系统的核心框架&#xff0c;也在不断地演变和进步。本文旨在带你了解软件系统架构的发展历程&#xff0c;从而更好地理解现代软件系统的构建和设计。 一、单体应用架构 单体应用架构是最早的软件系统架构形式&…

这才是大学生该做的副业,别再痴迷于游戏了!

感谢大家一直以来的支持和关注&#xff0c;尤其是在我的上一个公众号被关闭后&#xff0c;仍然选择跟随我的老粉丝们&#xff0c;你们的支持是我继续前行的动力。为了回馈大家长期以来的陪伴&#xff0c;我决定分享一些实用的干货&#xff0c;这些都是我亲身实践并且取得成功的…

上位机图像处理和嵌入式模块部署(上位机主要功能)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前关于机器视觉方面&#xff0c;相关的软件很多。比如说商业化的halcon、vision pro、vision master&#xff0c;当然也可以用opencv、pytorch自…

安装配置NMon

NMon&#xff08;Nigel’s Monitor&#xff09;是一款由IBM公司提供的免费性能监控工具&#xff0c;专门用于监控AIX系统和Linux系统的资源使用情况 下载软件 wget http://sourceforge.net/projects/nmon/files/nmon16p_binaries.tar.gz 如果报错的话&#xff0c;安装提示添加…

Java实现新能源电池回收系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户档案模块2.2 电池品类模块2.3 回收机构模块2.4 电池订单模块2.5 客服咨询模块 三、系统设计3.1 用例设计3.2 业务流程设计3.3 E-R 图设计 四、系统展示五、核心代码5.1 增改电池类型5.2 查询电池品类5.3 查询电池回…

线程池工作过程

线程池工作流程 线程池的处理流程总结 线程池的处理流程 当提交一个新任务到线程池时&#xff0c;线程池的处理流程如下&#xff1a; 1、线程池判断核心线程池里的线程是否都在执行任务。如果不是&#xff0c;则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执…

1Panel使用GMSSL+Openresty实现国密/RSA单向自适应

本文 首发于 Anyeの小站&#xff0c;转载请取得作者同意。 前言 国密算法是国家商用密码算法的简称。自2012年以来&#xff0c;国家密码管理局以《中华人民共和国密码行业标准》的方式&#xff0c;陆续公布了SM2/SM3/SM4等密码算法标准及其应用规范。其中“SM”代表“商密”&a…

人工智能学习与实训笔记(二):神经网络之图像分类问题

人工智能专栏文章汇总&#xff1a;人工智能学习专栏文章汇总-CSDN博客 目录 二、图像分类问题 2.1 尝试使用全连接神经网络 2.2 引入卷积神经网络 2.3 分类函数Softmax 2.4 交叉熵损失函数 2.5 学习率优化算法 2.6 图像预处理算法 2.6.1 随机改变亮暗、对比度和颜色等 …

C#系列-使用 Minio 做图片服务器实现图片上传 和下载(13)

1、Minio 服务器下载和安装 要在本地安装和运行 MinIO 服务器&#xff0c;你可以按照以下 步骤进行操作&#xff1a; 1. 访问 MinIO 的官方网站&#xff1a;https://min.io/&#xff0c;然后 点击页面上的”Download”按钮。 2. 在下载页面上&#xff0c;选择适合你操作系统的 …

论文阅读-EMS: History-Driven Mutation for Coverage-based Fuzzing(2022)模糊测试

一、背景 本文研究了基于覆盖率的模糊测试中的历史驱动变异技术。之前的研究主要采用自适应变异策略或集成约束求解技术来探索触发独特路径和崩溃的测试用例&#xff0c;但它们缺乏对模糊测试历史的细粒度重用&#xff0c;即它们在不同的模糊测试试验之间很大程度上未能正确利用…

Vue2学习第二天

Vue2 学习第二天 1. 数据绑定 Vue 中有 2 种数据绑定的方式&#xff1a; 单向绑定(v-bind)&#xff1a;数据只能从 data 流向页面。双向绑定(v-model)&#xff1a;数据不仅能从 data 流向页面&#xff0c;还可以从页面流向 data。 备注&#xff1a; 双向绑定一般都应用在表单…

javaweb学习day03(JS+DOM)

一、javascript入门 1 官方文档 地址: https://www.w3school.com.cn/js/index.asp离线文档: W3School 离线手册(2017.03.11 版).chm 2 基本说明 JavaScript 能改变 HTML 内容&#xff0c;能改变 HTML 属性&#xff0c;能改变 HTML 样式 (CSS)&#xff0c;能完成 页面的数据…

基于Java SSM框架实现疫情防控系统项目【项目源码】

基于java的SSM框架实现疫情防控系统演示 Java技术 Java技术它是一个容易让人学会和使用的一门服务器语言。它在编程的过程当中只需要很少的知识就能建立起一个真正的交互站点。对于这个教程来说它并不需要你完全去了解这种语言&#xff0c;只要能快速融入web站点就可以&#x…

2024春日营销三大内容趋势,种草爆文轻松get丨小红书数据分析

春季是市场迎来消费焕活的新周期&#xff0c;也是新一年品牌实现生意高速起步的必争节点。一年之“计”在于春&#xff0c;春日营销&#xff0c;吹响品牌营销第一声号角。那么&#xff0c;春日营销在小红书上有何内容趋势&#xff0c;跟着小编的脚步一起来看看~ 内容趋势 1、亲…

34461A 数字万用表,六位半,Truevolt DMM

01 34461A 数字万用表&#xff0c;六位半 产品综述&#xff1a; 34461A 六位半万用表是替代 Keysight 34401A 数字万用表&#xff08;此前为 Agilent 34401A&#xff09;的新一代产品。 34461A 拥有 Truevolt 系列数字万用表的全新图形显示界面、先进的分析模式和内置数学函…