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. 我只是个普通码农,不值得挽留 Spring SpEL表达式的使用 常见的应用场景:分布式锁的切面借助SpEL来构建key 比较另类的的应用场景:动态校验 个人感觉可以用作控制程序的走向,除此之外,spring的一些模块的自动配置类,也会在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、 数据卷 数据卷是一个供容器使用的特殊目录,位于容器中。可将宿主机的目录挂载到数据卷上,对数据卷的修改操作立刻…...
怎么查询电脑的登录记录及密码更改情况?
源头是办公室公用的电脑莫名其妙打不开了,问别人也都不知道密码是多少 因为本来就没设密码啊!(躺倒) 甚至已经想好了如果是50万想攻破电脑,被po抓住要怎么花这笔钱了 是我想太多 当然最后也没解决,莫名…...
《三》TypeScript 中函数的类型
TypeScript 允许指定函数的参数和返回值的类型。 函数声明的类型定义:function 函数名(形参: 形参类型, 形参: 形参类型, ...): 返回值类型 {} function sum(x: number, y: number): number {return x y } sum(1, 2) // 正确 sum(1, 2, 3) // 错误。输入多余的或者…...
深入学习 Mysql 引擎 InnoDB、MyISAM
tip:作为程序员一定学习编程之道,一定要对代码的编写有追求,不能实现就完事了。我们应该让自己写的代码更加优雅,即使这会费时费力。 💕💕 推荐:体系化学习Java(Java面试专题&#…...
【华为OD统一考试B卷 | 100分】阿里巴巴找黄金宝箱(V)(C++ Java JavaScript Python)
题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0~N的箱子,每个箱子上面贴有一个数字。 阿里巴巴念出一个咒语数字k(k<N),找出连续k个宝箱数字和的最大值,并输出该最大值。 输入描述 第一行输入一个数字字串,数字之间…...
六步快速搭建个人网站
目录 第一步、选择搭建平台WordPress 第二步、选域名 1)域名在哪买? 2)域名怎么选? 3)以阿里云为例,讲解怎么买域名 第三步、选择服务器 第四步、申请主机、安装WordPress 第五步、选择WordPress模…...
TypeScript 中的 type 关键字有什么用?
创建类型别名 在 TypeScript 中,type 关键字用于创建类型别名(Type Alias)。类型别名可以给一个类型起一个新的名字,使代码更具可读性和可维护性。 类型别名可以用于定义各种类型,包括基本类型、复合类型和自定义类型…...
27 getcwd 的调试
前言 同样是一个 很常用的 glibc 库函数 不管是 用户业务代码 还是 很多类库的代码, 基本上都会用到 获取当前路径 不过 我们这里是从 具体的实现 来看一下 测试用例 就是简单的使用了一下 getcwd rootubuntu:~/Desktop/linux/HelloWorld# cat Test04Getcwd.c #inc…...
使用IDEA使用Git:Git使用指北——实际操作篇
Git使用指北——实际操作 🤖:使用IDEA Git插件实际工作流程 💡 本文从实际使用的角度出发,以IDEA Git插件为基座讲述了如果使用IDEA的Git插件来解决实际开发中的协作开发问题。本文从 远程仓库中拉取项目,在本地分支进行开发&…...
java boot将一组yml配置信息装配在一个对象中
其实将一组yml数据封进一个对象中才是以后的主流开发方式 我们创建一个springboot项目 找到项目中的启动类所在目录 在同目录下创建一个类 名字你们可以随便取 我这里直接叫 dataManager 然后 在yml中定义这样一组数据信息 然后 我们在类中定义三个和这个配置信息相同的字段…...
【裸机开发】链接脚本(.lds文件)的基本语法
目录 一、什么是链接脚本? 二、链接脚本的基本语法格式 1、常用命令 2、内置变量 三、链接脚本的简单案例 一、什么是链接脚本? 一段程序的编译需要经历四个阶段(预处理—编译—汇编—链接),而链接脚本管理的就是…...
Java 进阶 -- 集合(三)
4、实现 实现是用于存储集合的数据对象,它实现了接口部分中描述的接口。本课描述了以下类型的实现: 通用实现是最常用的实现,是为日常使用而设计的。它们在标题为“通用实现”的表格中进行了总结。特殊目的实现是为在特殊情况下使用而设计的࿰…...
【华为OD机试真题 C语言】5、TLV解析 | 机试真题+思路参考+代码解析
文章目录 一、题目🎃题目描述🎃输入输出🎃样例1 二、思路参考三、代码参考🏆C语言 作者:KJ.JK 🍂个人博客首页: KJ.JK 🍂专栏介绍: 华为OD机试真题汇总,定期…...
(七)CSharp-刘铁锰版-事件
一、初步了解事件 定义:单词 Event ,译为“事件” 《牛津词典》中的解释是“a thing that happens,especially something important”通顺的解释就是“能够发生的什么事情” 角色: 使对象或类具备通知能力的成员 (中译&#x…...
【ROS】郭老二博文之:ROS目录
1、ROS2 【ROS】Ubuntu22.04安装ROS2(Humble Hawksbill) 【ROS】ROS2命令行工具详解 【ROS】ROS2中的概念和名词解释 【ROS】ROS2编程示例:话题订阅-发布-C版 【ROS】ROS2编程示例:服务和客户端-C版 【ROS】ROS2编程示例…...
Android应用程序进程的启动过程
Android应用程序进程的启动过程 导语 到这篇文章为止,我们已经简要地了解过了Android系统的启动流程了,其中比较重要的内容有Zygote进程的启动和SystemService以及Launcher的启动,接下来我们将要学习的是Android应用程序的启动过程ÿ…...
【2】Midjourney注册
随着AI技术的问世,2023年可以说是AI爆炸性成长的一年,近期最广为人知的AI服务除了chatgpt外,就是从去年五月就已经问世的AI绘画工具mid journey了。 ▲几个AI工具也代表了人工智能的热门阶段 只要输入一段文字,AI就会根据语意计算…...
第六十八天学习记录:高等数学:导数(宋浩板书)
导数是微积分中的一个概念,描述了函数在某一个点上的变化率。具体地说,函数 f ( x ) f(x) f(x)在 x a xa xa处的导数为 f ′ ( a ) f(a) f′(a),表示当 x x x在 a a a处发生微小的变化 Δ x \Delta x Δx时, f ( x ) f(x) f(x)对…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
