当前位置: 首页 > news >正文

spring.expression 随笔0 概述

0. 我只是个普通码农,不值得挽留

Spring SpEL表达式的使用
常见的应用场景:分布式锁的切面借助SpEL来构建key
比较另类的的应用场景:动态校验


个人感觉可以用作控制程序的走向,除此之外,spring的一些模块的自动配置类,也会在@Conditional注解中使用SpEL来实现有条件的加载特定的bean.

1. UML

1.1 ExpressionParser

解释器设计模式的体现了
在这里插入图片描述
单纯的(非模板表达式)spel表达式将通过 SpelExpressionParser 创建 InternalSpelExpressionParser, 来实现 parseExpression() 的底层逻辑.

	// org.springframework.expression.spel.standard.InternalSpelExpressionParser#doParseExpression@Overrideprotected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context)throws ParseException {try {this.expressionString = expressionString;// 1.对相关的符号进行分词Tokenizer tokenizer = new Tokenizer(expressionString);this.tokenStream = tokenizer.process();this.tokenStreamLength = this.tokenStream.size();this.tokenStreamPointer = 0;this.constructedNodes.clear();// 2.构建 ASTSpelNodeImpl ast = eatExpression();Assert.state(ast != null, "No node");Token t = peekToken();if (t != null) {throw new SpelParseException(t.startPos, SpelMessage.MORE_INPUT, toString(nextToken()));}Assert.isTrue(this.constructedNodes.isEmpty(), "At least one node expected");// 3. 返回创建好的表达式实例return new SpelExpression(expressionString, ast, this.configuration);}catch (InternalParseException ex) {throw ex.getCause();}}

1.2 ParserContext

在这里插入图片描述

  • 这里前后缀例如:支持模板表达式的实现类TemplateParserContext使用了#{}
  • TemplateAwareExpressionParser(支持模板的ExpressionParser实现类),根据 ParserContext.isTemplate()来决定处理流程
  • 有必要给出模板表达式的定义: 可以理解为多个、多种表达式的组合
	// org.springframework.expression.common.TemplateAwareExpressionParser#parseExpression(java.lang.String, org.springframework.expression.ParserContext)@Overridepublic Expression parseExpression(String expressionString, @Nullable ParserContext context) throws ParseException {if (context != null && context.isTemplate()) {return parseTemplate(expressionString, context);}else {return doParseExpression(expressionString, context);}}

1.3 Expression

在这里插入图片描述

转换并获取表达式对应的数值

1.4 EvaluateContext

  • 支持间接的关联 beanFactory ,来注入spring bean
  • 该功能很好的体现了 spring-expression 的独立性
  • 支持往该上下文中加入静态方法、java bean
  • 与 Expression.getValue() 有较大的关系
    在这里插入图片描述

在这里插入图片描述

2. test-classes

因为要debug beanFactory关联的 parser,便懒得构造applicationContext,直接@SpringbootTest 启动容器了

@DisplayName("Spring Expression Language")
@SpringBootTest
public class SpELTest {@Value("#{testBean.value}")String value;@AutowiredApplicationContext appCtx;SpelExpressionParser parser;StandardEvaluationContext stdEvaCtx;@PostConstructprivate void postConstruct() throws NoSuchMethodException {parser = new SpelExpressionParser();// rootObjectstdEvaCtx = new StandardEvaluationContext(new TestBean("rootValue", null));// variablestdEvaCtx.setVariable("testBean", this.appCtx.getBean("testBean"));Method parseInt = Integer.class.getDeclaredMethod("valueOf", String.class);stdEvaCtx.registerFunction("doValueOf", parseInt);stdEvaCtx.setBeanResolver(new BeanFactoryResolver(this.appCtx));}@DisplayName("注解方式")@Testvoid t0() {// 这个上下文其实就是表达式、变量的容器(缓存)System.err.println(this.value);}@DisplayName("编码方式")@Testvoid t1() {// 不需要 {}// spring security 中也使用编码的方式解析权限注解 @PrePreAuthorizeExpression expression = parser.parseExpression("#testBean.value");System.err.println(expression.getValue(this.stdEvaCtx));}@DisplayName("运算")@Nestedclass Count {@DisplayName("字面量")@Testvoid t0() {// 上下文中找不到这个变量,报错:// spel.SpelEvaluationException: EL1007E: Property or field 'test' cannot be found on null// System.err.println("找不到变量="+parser.parseExpression("test").getValue(String.class));// 字符串System.err.println("字符串1=" + parser.parseExpression("'test'").getValue(String.class));System.err.println("字符串2=" + parser.parseExpression("\"test\"").getValue(String.class));// 数字System.err.println("int=" + parser.parseExpression("1").getValue(Integer.class));System.err.println("long=" + parser.parseExpression("1L").getValue(long.class));System.err.println("float=" + parser.parseExpression("1.1").getValue(Float.class));System.err.println("double=" + parser.parseExpression("1.1E+1").getValue(double.class));System.err.println("hex=" + parser.parseExpression("0xf").getValue(int.class));// 布尔System.err.println("bool=" + parser.parseExpression("false").getValue(boolean.class));// nullSystem.err.println(parser.parseExpression("null").getValue());}@DisplayName("算数")@Testvoid t1() {System.err.println("3+2=" + parser.parseExpression("3+2").getValue(Integer.class));System.err.println("3-2=" + parser.parseExpression("3-2").getValue(Integer.class));System.err.println("3*2=" + parser.parseExpression("3*2").getValue(Integer.class));System.err.println("3/2=" + parser.parseExpression("3/2").getValue(Integer.class));System.err.println("3%2=" + parser.parseExpression("3%2").getValue(Integer.class));System.err.println("3^2=" + parser.parseExpression("3^2").getValue(Integer.class));}@DisplayName("关系运算")@Testvoid t2() {System.err.println("3==2=" + parser.parseExpression("3==2").getValue(Boolean.class));System.err.println("3 == 2=" + parser.parseExpression("3 == 2").getValue(Boolean.class));System.err.println("3 ge 2 =" + parser.parseExpression("3 ge 2").getValue(boolean.class));System.err.println("3 LT 2 = " + parser.parseExpression("3 LT 2").getValue(boolean.class));// SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'NE2'// System.err.println("3NE2 = "+parser.parseExpression("3NE2").getValue(boolean.class));// 并不能返回 int:0、1,会抛出异常System.err.println("2 between {1, 2}=" + parser.parseExpression("2 between {1, 2}").getValue(Boolean.class));// SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'le(<=)'// System.err.println("1<2<=2="+parser.parseExpression("1<2<=3").getValue(Boolean.class));}@DisplayName("逻辑运算")@Testvoid t3() {System.err.println("2>1 and false = " + parser.parseExpression("2>1 and false").getValue(boolean.class));System.err.println("2>1 && false = " + parser.parseExpression("2>1 && false").getValue(Boolean.class));// SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'factory_bean_ref(&)'// System.err.println("2>1 & false = "+parser.parseExpression("2>1 & false").getValue(Boolean.class));System.err.println("2>1 or NOT false and (! NOT false || 1==1) = " + parser.parseExpression("2>1 or NOT false and (! NOT false || 1==1)").getValue(Boolean.class));}@DisplayName("三目运算")@Testvoid t4() {System.err.println("3 > 2 ? true : false = " + parser.parseExpression("3 > 2 ? true : false").getValue(boolean.class));}@DisplayName("elivis")@Testvoid t5() {System.err.println("null ?: 'false' = " + parser.parseExpression("null ?: 'false'").getValue(Boolean.class));System.err.println("null ?: 'false' = " + parser.parseExpression("null ?: 'false'").getValue(String.class));}@DisplayName("正则")@Testvoid t6() {System.err.println("" + parser.parseExpression("'123' matches '\\d{3}'").getValue(boolean.class));System.err.println("" + parser.parseExpression("123 matches '\\d{3}'").getValue(Boolean.class));}@DisplayName("instanceof")@Testvoid t7() {System.err.println("'123' instanceof T(String) = " + parser.parseExpression("'123' instanceof T(String)").getValue(Boolean.class));System.err.println("123 instanceof T(String) = " + parser.parseExpression("123 instanceof T(java.lang.String)").getValue(Boolean.class));}}@DisplayName("类型")@Nestedclass Type {@DisplayName("class")@Testvoid t0() {// java.lang 以外的类均需要全限定名System.err.println(parser.parseExpression("T(String)").getValue(Class.class));System.err.println(parser.parseExpression("T(java.util.Map)").getValue(Class.class));// 访问 静态的属性、方法System.err.println(parser.parseExpression("T(Integer).MAX_VALUE").getValue(int.class));System.err.println(parser.parseExpression("T(Integer).parseInt(3)").getValue(Integer.class));}@DisplayName("instance")@Testvoid t1() {// java.lang 包 同理System.err.println(parser.parseExpression("new String('苹果一样的甜')").getValue(String.class));System.err.println(parser.parseExpression("new java.util.Date()").getValue(Date.class));}@DisplayName("reference")@Testvoid t2() {System.err.println("#testBean.value=" + parser.parseExpression("#testBean.value").getValue(stdEvaCtx, String.class));System.err.println("#this.value=" + parser.parseExpression("#this.value").getValue(stdEvaCtx, String.class));System.err.println("#root.value=" + parser.parseExpression("#root.value").getValue(stdEvaCtx, String.class));// rootObject缺省时,访问其属性,不能加#前缀System.err.println("(root属性可以省略#root)value=" + parser.parseExpression("value").getValue(stdEvaCtx, String.class));}@DisplayName("assign")@Testvoid t3() {System.err.println("#testBean.value=newValue --> " + parser.parseExpression("#testBean.value='newValue'").getValue(stdEvaCtx, String.class));System.err.println("#this.value=newThisValue --> " + parser.parseExpression("#this.value='newThisValue'").getValue(stdEvaCtx, String.class));System.err.println("#root.value=newRootValue --> " + parser.parseExpression("#root.value='newRootValue'").getValue(stdEvaCtx, String.class));System.err.println("value=newValue --> " + parser.parseExpression("value='newValue'").getValue(stdEvaCtx, String.class));}@DisplayName("func")@Testvoid t4() {System.err.println(parser.parseExpression("#doValueOf('20').byteValue()").getValue(stdEvaCtx, Byte.class));}@DisplayName("对象属性获取 及 安全导航")@Testvoid t5() {System.err.println(parser.parseExpression("value").getValue(stdEvaCtx, String.class));// Value 可以,Value 不得行(首字母不敏感)System.err.println(parser.parseExpression("Value").getValue(stdEvaCtx, String.class));// 支持groovy的elivis表达式// 安全导航运算符前面的#root可以省略,但后面的#root不可省略System.err.println(parser.parseExpression("#root?.#root").getValue(stdEvaCtx, TestBean.class));System.err.println(parser.parseExpression("value?.#root.value").getValue(stdEvaCtx, String.class));// SpelParseException: Expression [username?.'核弹拉链'] @8: EL1049E: Unexpected data after '.': ''核弹拉链''// SpEL引入了Groovy语言中的安全导航运算符"(对象|属性)?.属性"// 常量显然不得行// System.err.println(parser.parseExpression("username?.'核弹拉链'").getValue(stdEvaCtx, String.class));}@DisplayName("对象方法调用")@Testvoid t6() {System.err.println(parser.parseExpression("value.substring(1, 6).toUpperCase()").getValue(stdEvaCtx, String.class));System.err.println(parser.parseExpression("toString()").getValue(stdEvaCtx, String.class));}@DisplayName("bean引用(BeanFactoryResolver)")@Testvoid t7() {// @BeanName// EvaluationContext.setBeanResolver() 需要借助 beanFactorySystem.err.println(Jsons.NO_OP.stringify(parser.parseExpression("@systemProperties").getValue(stdEvaCtx, Properties.class)));System.err.println(Jsons.NO_OP.stringify(parser.parseExpression("@testBean").getValue(stdEvaCtx, TestBean.class)));}}@DisplayName("集合")@Nestedclass Collect {@DisplayName("内联数组")@Testvoid t0() {System.err.println(Arrays.toString(parser.parseExpression("new int[2]{1, 2}").getValue(int[].class)));System.err.println(Arrays.toString(parser.parseExpression("new String[2][2]").getValue(String[][].class)));// 不支持多维数组创建同时,做初始化System.err.println(Arrays.toString(parser.parseExpression("new String[2][2]{'1','2'},{'3','4'}").getValue(String[][].class)));}@DisplayName("内联集合")@Testvoid t1() {System.err.println(parser.parseExpression("{}").getValue(List.class));// java.util.Collections$UnmodifiableRandomAccessListSystem.err.println(parser.parseExpression("{1, 2,3}").getValue(List.class).getClass().getName());// 此时的 List<List> .class = java.util.ArrayList// 存在非字面量表达式时,集合类型将转为原始类型(可修改的集合)System.err.println(parser.parseExpression("{{1+2,2+4},{3,4+4}}").getValue(List.class).getClass().getName());}@DisplayName("数组、集合、字典元素访问")@Testvoid t2() {System.err.println(parser.parseExpression("[0]").getValue(new int[]{1, 2, 3}, Integer.class));System.err.println(parser.parseExpression("{1, 2, 3}[0]").getValue(int.class));System.err.println(parser.parseExpression("[0]").getValue(Lists.newArrayList(1, 2, 3), int.class));Map<String, Integer> map = Maps.newHashMap();map.put("weng", 4);map.put("chong", 5);map.put("yu", 2);System.err.println(parser.parseExpression("['chong']").getValue(map, int.class));}// 很像 streamApi.peek().collect(toList())@DisplayName("数组、集合、字典 转换")@Testvoid t3() {// array|list|map.![表达式]System.err.println(Arrays.toString(parser.parseExpression("#root.![#this+1]").getValue(new int[]{1, 2, 3}, int[].class)));System.err.println(parser.parseExpression("#root.![#this+1]").getValue(Lists.newArrayList(1, 2, 3), List.class));Map<String, Integer> map = Maps.newHashMap();map.put("weng", 4);map.put("chong", 5);map.put("yu", 2);System.err.println(Jsons.NO_OP.stringify(parser.parseExpression("#root.![#this.key+1]").getValue(map, List.class)));System.err.println(Jsons.NO_OP.stringify(parser.parseExpression("#root.![#this.value+1]").getValue(map, List.class)));// 报错: cannot convert from java.util.ArrayList<?> to java.util.Map<?, ?>// 集合、数组之间可以随意转换// System.err.println(Jsons.NO_OP.stringify(parser.parseExpression("#root.![#this.value+1]").getValue(map, Map.class)));}// 相当于 streamApi.filter.collect(toList)@DisplayName("数组、集合、字典 选择")@Testvoid t4() {// array|list|map.?[表达式]System.err.println(Arrays.toString(parser.parseExpression("#root.?[#this>=2]").getValue(new int[]{1, 2, 3}, int[].class)));System.err.println(Arrays.toString(parser.parseExpression("#root.?[#this>=2]").getValue(Lists.newArrayList(1, 2, 3), int[].class)));Map<String, Integer> map = Maps.newHashMap();map.put("weng", 4);map.put("chong", 5);map.put("yu", 2);// {"weng":4,"yu":2}System.err.println(Jsons.NO_OP.stringify(parser.parseExpression("#root.?[#this.key!='chong']").getValue(map, Map.class)));// 这里转的集合,有些怪异// [{"weng":4,"yu":2}]System.err.println(Jsons.NO_OP.stringify(parser.parseExpression("#root.?[#this.key!='chong']").getValue(map, List.class)));System.err.println(Jsons.NO_OP.stringify(parser.parseExpression("#root.?[#this.value<=2]").getValue(map, Map.class)));}}
}

附上相关的类

@Component("testBean")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class TestBean {@Value("${angel.spel.key}")public String value;private String username;
}

相关文章:

spring.expression 随笔0 概述

0. 我只是个普通码农&#xff0c;不值得挽留 Spring SpEL表达式的使用 常见的应用场景:分布式锁的切面借助SpEL来构建key 比较另类的的应用场景:动态校验 个人感觉可以用作控制程序的走向&#xff0c;除此之外&#xff0c;spring的一些模块的自动配置类&#xff0c;也会在Cond…...

从Cookie到Session: Servlet API中的会话管理详解

文章目录 一. Cookie与Session1. Cookie与Session2. Servlet会话管理操作 二. 登录逻辑的实现 一. Cookie与Session 1. Cookie与Session 首先, 在学习过 HTTP 协议的基础上, 我们需要知道 Cookie 是 HTTP 请求报头中的一个关键字段, 本质上是浏览器在本地存储数据的一种机制,…...

docker数据管理与网络通信

一、管理docker容器中数据 管理Docker 容器中数据主要有两种方式:数据卷(Data Volumes)和数据卷容器( DataVolumes Containers) 。 1、 数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立刻…...

怎么查询电脑的登录记录及密码更改情况?

源头是办公室公用的电脑莫名其妙打不开了&#xff0c;问别人也都不知道密码是多少 因为本来就没设密码啊&#xff01;&#xff08;躺倒&#xff09; 甚至已经想好了如果是50万想攻破电脑&#xff0c;被po抓住要怎么花这笔钱了 是我想太多 当然最后也没解决&#xff0c;莫名…...

《三》TypeScript 中函数的类型

TypeScript 允许指定函数的参数和返回值的类型。 函数声明的类型定义&#xff1a;function 函数名(形参: 形参类型, 形参: 形参类型, ...): 返回值类型 {} function sum(x: number, y: number): number {return x y } sum(1, 2) // 正确 sum(1, 2, 3) // 错误。输入多余的或者…...

深入学习 Mysql 引擎 InnoDB、MyISAM

tip&#xff1a;作为程序员一定学习编程之道&#xff0c;一定要对代码的编写有追求&#xff0c;不能实现就完事了。我们应该让自己写的代码更加优雅&#xff0c;即使这会费时费力。 &#x1f495;&#x1f495; 推荐&#xff1a;体系化学习Java&#xff08;Java面试专题&#…...

【华为OD统一考试B卷 | 100分】阿里巴巴找黄金宝箱(V)(C++ Java JavaScript Python)

题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0~N的箱子,每个箱子上面贴有一个数字。 阿里巴巴念出一个咒语数字k(k<N),找出连续k个宝箱数字和的最大值,并输出该最大值。 输入描述 第一行输入一个数字字串,数字之间…...

六步快速搭建个人网站

目录 第一步、选择搭建平台WordPress 第二步、选域名 1&#xff09;域名在哪买&#xff1f; 2&#xff09;域名怎么选&#xff1f; 3&#xff09;以阿里云为例&#xff0c;讲解怎么买域名 第三步、选择服务器 第四步、申请主机、安装WordPress 第五步、选择WordPress模…...

TypeScript 中的 type 关键字有什么用?

创建类型别名 在 TypeScript 中&#xff0c;type 关键字用于创建类型别名&#xff08;Type Alias&#xff09;。类型别名可以给一个类型起一个新的名字&#xff0c;使代码更具可读性和可维护性。 类型别名可以用于定义各种类型&#xff0c;包括基本类型、复合类型和自定义类型…...

27 getcwd 的调试

前言 同样是一个 很常用的 glibc 库函数 不管是 用户业务代码 还是 很多类库的代码, 基本上都会用到 获取当前路径 不过 我们这里是从 具体的实现 来看一下 测试用例 就是简单的使用了一下 getcwd rootubuntu:~/Desktop/linux/HelloWorld# cat Test04Getcwd.c #inc…...

使用IDEA使用Git:Git使用指北——实际操作篇

Git使用指北——实际操作 &#x1f916;:使用IDEA Git插件实际工作流程 &#x1f4a1; 本文从实际使用的角度出发&#xff0c;以IDEA Git插件为基座讲述了如果使用IDEA的Git插件来解决实际开发中的协作开发问题。本文从 远程仓库中拉取项目&#xff0c;在本地分支进行开发&…...

java boot将一组yml配置信息装配在一个对象中

其实将一组yml数据封进一个对象中才是以后的主流开发方式 我们创建一个springboot项目 找到项目中的启动类所在目录 在同目录下创建一个类 名字你们可以随便取 我这里直接叫 dataManager 然后 在yml中定义这样一组数据信息 然后 我们在类中定义三个和这个配置信息相同的字段…...

【裸机开发】链接脚本(.lds文件)的基本语法

目录 一、什么是链接脚本&#xff1f; 二、链接脚本的基本语法格式 1、常用命令 2、内置变量 三、链接脚本的简单案例 一、什么是链接脚本&#xff1f; 一段程序的编译需要经历四个阶段&#xff08;预处理—编译—汇编—链接&#xff09;&#xff0c;而链接脚本管理的就是…...

Java 进阶 -- 集合(三)

4、实现 实现是用于存储集合的数据对象&#xff0c;它实现了接口部分中描述的接口。本课描述了以下类型的实现: 通用实现是最常用的实现&#xff0c;是为日常使用而设计的。它们在标题为“通用实现”的表格中进行了总结。特殊目的实现是为在特殊情况下使用而设计的&#xff0…...

【华为OD机试真题 C语言】5、TLV解析 | 机试真题+思路参考+代码解析

文章目录 一、题目&#x1f383;题目描述&#x1f383;输入输出&#x1f383;样例1 二、思路参考三、代码参考&#x1f3c6;C语言 作者&#xff1a;KJ.JK &#x1f342;个人博客首页&#xff1a; KJ.JK &#x1f342;专栏介绍&#xff1a; 华为OD机试真题汇总&#xff0c;定期…...

(七)CSharp-刘铁锰版-事件

一、初步了解事件 定义&#xff1a;单词 Event &#xff0c;译为“事件” 《牛津词典》中的解释是“a thing that happens,especially something important”通顺的解释就是“能够发生的什么事情” 角色&#xff1a; 使对象或类具备通知能力的成员 &#xff08;中译&#x…...

【ROS】郭老二博文之:ROS目录

1、ROS2 【ROS】Ubuntu22.04安装ROS2&#xff08;Humble Hawksbill&#xff09; 【ROS】ROS2命令行工具详解 【ROS】ROS2中的概念和名词解释 【ROS】ROS2编程示例&#xff1a;话题订阅-发布-C版 【ROS】ROS2编程示例&#xff1a;服务和客户端-C版 【ROS】ROS2编程示例&#xf…...

Android应用程序进程的启动过程

Android应用程序进程的启动过程 导语 到这篇文章为止&#xff0c;我们已经简要地了解过了Android系统的启动流程了&#xff0c;其中比较重要的内容有Zygote进程的启动和SystemService以及Launcher的启动&#xff0c;接下来我们将要学习的是Android应用程序的启动过程&#xff…...

【2】Midjourney注册

随着AI技术的问世&#xff0c;2023年可以说是AI爆炸性成长的一年&#xff0c;近期最广为人知的AI服务除了chatgpt外&#xff0c;就是从去年五月就已经问世的AI绘画工具mid journey了。 ▲几个AI工具也代表了人工智能的热门阶段 只要输入一段文字&#xff0c;AI就会根据语意计算…...

第六十八天学习记录:高等数学:导数(宋浩板书)

导数是微积分中的一个概念&#xff0c;描述了函数在某一个点上的变化率。具体地说&#xff0c;函数 f ( x ) f(x) f(x)在 x a xa xa处的导数为 f ′ ( a ) f(a) f′(a)&#xff0c;表示当 x x x在 a a a处发生微小的变化 Δ x \Delta x Δx时&#xff0c; f ( x ) f(x) f(x)对…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知&#xff0c;帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量&#xff0c;能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度&#xff0c;还为机器人、医疗设备和制造业的智…...

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

【51单片机】4. 模块化编程与LCD1602Debug

1. 什么是模块化编程 传统编程会将所有函数放在main.c中&#xff0c;如果使用的模块多&#xff0c;一个文件内会有很多代码&#xff0c;不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里&#xff0c;在.h文件里提供外部可调用函数声明&#xff0c;其他.c文…...

python基础语法Ⅰ

python基础语法Ⅰ 常量和表达式变量是什么变量的语法1.定义变量使用变量 变量的类型1.整数2.浮点数(小数)3.字符串4.布尔5.其他 动态类型特征注释注释是什么注释的语法1.行注释2.文档字符串 注释的规范 常量和表达式 我们可以把python当作一个计算器&#xff0c;来进行一些算术…...