深入解析Spring EL表达式:概念、特性与应用

在Spring框架中,Spring Expression Language(简称Spring EL)是一种功能强大的表达式语言,它支持在运行时查询和操作对象图。本文将详细介绍Spring EL表达式的概念、特点、基本语法和用法,并探讨在Spring框架中的应用场景。同时,我们还将分析Spring EL的优势和局限性,并与其他相关技术进行比较。

一、Spring EL表达式的概念和特点

EL表达式,全称Expression Language,是一种表达式语言,它借鉴了JavaScript和XPath的表达式语言,并设计用来简化在Java Web应用程序中的表达式。在JSP 2.0及以后的版本中,EL表达式被引入,允许开发者在JSP页面上更方便地访问和操作数据。

在Spring框架中,Spring EL(Spring Expression Language)被引入,以提供一种更强大、更简洁的方式来装配Bean,处理运行时数据,并执行方法。Spring EL允许开发者通过表达式将数据装配到属性或构造函数中,调用JDK中提供的静态常量,获取外部Properties文件中的配置,甚至可以对不同Bean的字段进行计算再进行赋值。

使用Spring EL的主要原因包括:

  1. 简化数据访问:Spring EL提供了一种简洁的语法来访问和操作对象图中的数据,无需编写大量的Java代码。
  2. 动态性:Spring EL表达式在运行时解析和执行,这使得应用程序可以根据运行时条件动态地改变行为。
  3. 强大的功能:Spring EL支持各种操作符和函数,包括算术、逻辑、关系、条件、集合和字符串操作等。
  4. 与Spring的无缝集成:Spring EL与Spring框架的其他部分(如Spring MVC、Spring Data等)紧密集成,使得开发者能够更轻松地在Spring应用程序中使用表达式。

总的来说,Spring EL是Spring框架中一个重要的工具,它简化了数据访问和操作,提高了应用程序的灵活性和可维护性。它是一种基于Java的表达式语言,它可以在运行时对Spring管理的对象进行动态访问和操作。

Spring EL具有以下特点:

  1. 简洁明了的语法:Spring EL的语法简洁且易于理解,类似于传统的编程语言中的语法结构。
  2. 强大的功能:支持方法调用、访问属性、集合投影、集合过滤、算术运算等。
  3. 高度的灵活性:Spring EL可以与Spring框架的其他部分(如Spring MVC、Spring Data等)无缝集成,为开发者提供了丰富的表达式操作选项。
  4. 良好的性能:Spring EL在解析和执行表达式时具有高效的性能表现。

二、Spring EL的基本语法和用法

2.1. 基本语法

Spring EL的基本语法结构如下:

#{expression}

其中,expression是一个符合Spring EL语法的表达式。

当然,下面我将详细介绍Spring EL表达式的语法。

2.2. 基本用法

Spring EL的语法非常直观且易于学习,它允许你通过简单的表达式来访问和操作Java对象。以下是Spring EL表达式的一些基本语法元素:

1. 变量和方法

在Spring EL中,你可以使用.来访问对象的属性或方法。例如:

// 访问属性
#{user.name}// 调用方法(无参数)
#{user.getName()}// 调用方法(有参数)
#{user.getFullName('John', 'Doe')}

如果属性名或方法与Java关键字冲突,你可以使用['']语法来访问它们:

#{user['class']}  // 访问名为class的属性
2. 字面量

Spring EL支持各种字面量,包括字符串、数字、布尔值等:

#{100}       // 数字字面量
#{'Hello'}   // 字符串字面量
#{true}      // 布尔字面量
3. 算术运算符

你可以使用标准的算术运算符来执行计算:

#{10 + 20}   // 加法
#{30 - 10}   // 减法
#{10 * 5}    // 乘法
#{100 / 2}   // 除法
#{15 % 4}    // 取模
4. 比较运算符

Spring EL支持各种比较运算符,用于比较值:

#{10 == 10}  // 等于
#{10 != 20}  // 不等于
#{5 < 10}    // 小于
#{15 <= 10}  // 小于等于
#{10 > 5}    // 大于
#{10 >= 5}   // 大于等于
5. 逻辑运算符

你可以使用逻辑运算符来组合或修改布尔表达式:

#{true && false}  // 逻辑与
#{true || false}  // 逻辑或
#{!true}          // 逻辑非
6. 三元运算符

Spring EL支持三元运算符,它允许你根据条件选择值:

#{10 > 5 ? 'Greater' : 'Lesser or Equal'}  // 如果10大于5,则结果为'Greater',否则为'Lesser or Equal'
7. 集合和数组

你可以使用Spring EL来访问和操作集合(如列表、集合)和数组:

// 访问列表元素
#{myList[0]}       // 访问列表的第一个元素
#{myList[1]}       // 访问列表的第二个元素// 访问数组元素
#{myArray[0]}      // 访问数组的第一个元素// 访问Map元素
#{myMap['key']}    // 访问Map中键为'key'的值
#{myMap.key}       // 如果键是合法的标识符,也可以这样访问
8. 投影和选择

对于集合,你可以使用.?[]来进行投影(选择集合中每个元素的某个属性)和选择(基于某个条件过滤集合):

// 投影 - 选择每个用户的名字
#{users.![name]}// 选择 - 选择年龄大于18的用户
#{users.?[age > 18]}
9. 调用静态方法

Spring EL允许你调用静态方法,但通常这需要在Spring配置中明确允许:

// 调用静态方法(需要配置)
#{T(java.lang.Math).random()}

在这里,T()函数用于获取类类型,然后你可以调用其静态方法。但请注意,出于安全考虑,默认情况下Spring EL不允许调用静态方法,你需要显式地在Spring配置中启用它。

10. 正则表达式

Spring EL还支持正则表达式匹配:

#{user.email matches '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'}  // 检查email是否匹配正则表达式

11. 方法参数

在Spring EL中,你可以调用带有参数的方法。这些参数可以是字面量、变量表达式或其他EL表达式。方法参数按照它们在EL表达式中出现的顺序进行传递。

// 调用带有参数的方法
#{user.getFullName('John', 'Doe')}  // 假设getFullName接收两个字符串参数

12. 自定义函数

Spring EL允许你注册自定义函数,这些函数可以在EL表达式中调用。自定义函数通过实现特定的接口或使用Spring的@ValueMethodInvokingFactoryBean来定义。

一旦注册了自定义函数,你就可以在EL表达式中像调用内置函数一样调用它们。

例如,假设你注册了一个名为concat的自定义函数,你可以这样使用它:

#{concat('Hello', ' World')}  // 调用自定义的concat函数

要实现自定义函数,你需要创建一个Java类,实现Spring的org.springframework.expression.ExpressionParser接口,或者使用Spring提供的StandardEvaluationContext来注册你的函数。

13. 内联列表

Spring EL支持内联列表,允许你在表达式中直接定义列表。这对于临时需要列表的场景非常有用。

// 定义内联列表
#{[1, 2, 3, 4, 5]}  // 创建一个包含整数的列表
#{['apple', 'banana', 'cherry']}  // 创建一个包含字符串的列表

你还可以在内联列表中混合使用不同类型的元素。

14. 内联映射(字典)

与内联列表类似,Spring EL也支持内联映射(有时也称为字典或哈希表)。你可以使用{key1: value1, key2: value2, ...}的语法来定义它们。

// 定义内联映射
#{{'key1': 'value1', 'key2': 'value2'}}  // 创建一个映射,其中key1对应value1,key2对应value2

内联映射在需要快速定义键值对集合时非常有用。

15. 变量和作用域

在Spring EL中,你可以定义和使用变量。这些变量可以根据它们的作用域(如方法作用域、请求作用域、会话作用域等)进行存储和访问。

// 设置变量(通常在Spring配置中完成)
#set($var = 'someValue')// 使用变量
#{$var}

请注意,上面的#set指令不是Spring EL标准语法的一部分,但某些Spring EL的扩展或模板引擎(如Thymeleaf)可能支持这种语法来设置变量。在纯Spring EL表达式中,变量的设置通常是通过Spring的上下文管理来完成的。

16. 类型引用和类型转换

使用T()运算符,你可以引用Java类型,并在必要时执行类型转换。这对于访问静态方法或执行类型转换特别有用。

// 引用Java类型并调用静态方法(需要配置支持)
#{T(java.lang.Math).random()}  // 调用Math类的random静态方法// 类型转换
#{T(java.lang.Integer).valueOf('42')}  // 将字符串'42'转换为Integer类型

17. 模板文字

在某些情况下,你可能希望在EL表达式中使用模板文字,这些模板文字允许你插入表达式的值。虽然这不是Spring EL核心功能的一部分,但某些与Spring集成的模板引擎(如Thymeleaf或FreeMarker)提供了这种功能。

18. 安全性和限制

由于Spring EL非常强大,因此在使用时需要注意安全性。默认情况下,Spring会限制EL表达式的某些功能(如访问Java类、调用静态方法等),以防止潜在的安全风险。你可以通过配置来放宽这些限制,但这需要谨慎考虑。

总的来说,Spring EL是一个功能丰富的表达式语言,它提供了许多高级特性和功能来满足复杂的应用程序需求。通过合理地使用这些特性,你可以编写出更简洁、更灵活的代码。

三、Spring EL在Spring框架中的应用场景

Spring EL在Spring框架中有广泛的应用场景,以下是一些常见的例子:

  1. Spring MVC中的数据绑定:在Spring MVC中,可以使用Spring EL表达式在JSP页面或Thymeleaf模板中动态绑定数据。
  2. Spring Security中的权限控制:Spring EL可以用于定义基于表达式的访问控制规则。
  3. Spring Data中的查询:在Spring Data JPA中,可以使用Spring EL表达式定义动态查询条件。
  4. Spring Integration中的消息处理:Spring EL可以用于处理和转换消息流中的数据。

四、Spring EL的综合复杂案例:电商订单处理

业务场景

在电商系统中,我们通常需要处理用户的购物车、订单以及订单中的商品项。本案例将模拟一个用户结算购物车的流程,并使用Spring EL来处理订单数据的计算和验证。

实体类

首先定义UserCartCartItemProductOrder等实体类。

public class User {private String username;// ... 其他属性 ...// 省略getter和setter方法
}public class Cart {private List<CartItem> items = new ArrayList<>();public BigDecimal getTotalPrice() {return items.stream().map(CartItem::getTotalPrice).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);}// 省略getter和setter方法
}public class CartItem {private Product product;private int quantity;public BigDecimal getTotalPrice() {return product.getPrice().multiply(BigDecimal.valueOf(quantity));}// 省略getter和setter方法
}public class Product {private String name;private BigDecimal price;// 省略getter和setter方法
}public class Order {private User user;private List<OrderItem> orderItems = new ArrayList<>();private BigDecimal totalAmount;// 根据订单项计算订单总额public void calculateTotalAmount() {this.totalAmount = orderItems.stream().map(OrderItem::getTotalPrice).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);}// 省略getter和setter方法
}public class OrderItem {private Product product;private int quantity;public BigDecimal getTotalPrice() {return product.getPrice().multiply(BigDecimal.valueOf(quantity));}// 省略getter和setter方法
}
服务类

接下来,我们创建一个服务类来模拟购物车结算流程,并使用Spring EL来处理业务逻辑。

@Service
public class OrderService {@Autowiredprivate ApplicationContext applicationContext;public Order createOrderFromCart(Cart cart, User user) {Order order = new Order();order.setUser(user);ExpressionParser parser = new SpelExpressionParser();// 遍历购物车中的商品项,转换为订单项for (CartItem cartItem : cart.getItems()) {OrderItem orderItem = new OrderItem();orderItem.setProduct(cartItem.getProduct());orderItem.setQuantity(cartItem.getQuantity());// 使用Spring EL计算订单项的总价(虽然这里可以直接调用方法,但为了展示EL,我们依然使用它)StandardEvaluationContext context = new StandardEvaluationContext();context.setVariable("cartItem", cartItem);BigDecimal totalPrice = parser.parseExpression("#cartItem.product.price * #cartItem.quantity").getValue(context, BigDecimal.class);// 这里只是为了演示EL,实际上可以直接使用orderItem.getTotalPrice()System.out.println("Order item total price calculated by Spring EL: " + totalPrice);order.getOrderItems().add(orderItem);}// 计算订单总额(同样可以使用EL,但这里我们调用方法以保持清晰)order.calculateTotalAmount();return order;}
}

注意:在实际应用中,我们通常不会使用Spring EL来计算订单项的总价或订单总额,因为直接调用方法会更加直观和高效。Spring EL更适合用于动态表达式求值,如配置文件中的条件判断、动态方法调用等场景。

配置类

为了简化配置,我们可以使用Java配置类来创建和配置ApplicationContext。但在这个案例中,我们实际上不需要特殊的配置,因为服务类可以自动装配ApplicationContext。不过,为了完整性,这里还是提供一个简单的配置类。

@Configuration
public class AppConfig {// 可以在这里配置其他beans,如数据源、服务类等// 但在这个案例中,我们不需要额外的配置
}
运行和测试

最后,我们可以编写一个简单的测试类来运行和测试我们的服务。

@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderServiceTest {@Autowiredprivate OrderService orderService;@Testpublic void testCreateOrderFromCart() {User user = new User();user.setUsername("testUser");Product product1 = new Product();product1.setName("Product 1");product1.setPrice(new BigDecimal("10.00"));Product product2 = new Product();product2.setName("Product 2");product2.setPrice(new BigDecimal("20.00"));Cart cart = new Cart();cart.getItems().add(new CartItem(product1, 2));cart.getItems().add(new CartItem(product2, 1));Order order = orderService.createOrderFromCart(cart, user);assertNotNull(order);assertEquals(2, order.getOrderItems().size());assertEquals(new BigDecimal("40.00"), order.getTotalAmount());}
}

这个测试类使用Spring Boot的测试功能来运行,并自动装配了OrderService。它创建了一个购物车和用户,然后调用createOrderFromCart方法来创建订单,并验证订单的内容是否正确。

注意:虽然这个案例包含了Spring EL的使用,但如前所述,这里使用Spring EL并不是最佳实践。在实际项目中,应该根据具体需求来决定是否使用Spring EL以及如何使用它来最大化其价值和灵活性。

五、Spring EL的优势和局限性

1. 优势

  • 简洁易懂的语法:Spring EL的语法简洁明了,易于学习和使用。
  • 强大的功能:支持丰富的表达式操作和对象访问方式。
  • 与Spring框架的无缝集成:Spring EL与Spring框架的其他部分(如Spring MVC、Spring Data等)紧密集成,方便开发者在项目中快速应用。

2. 局限性

  • 学习成本:虽然Spring EL的语法相对简单,但对于初学者来说仍有一定的学习成本。特别是当表达式变得复杂时,理解和维护起来可能会比较困难。
  • 性能开销:虽然Spring EL在性能上进行了优化,但在处理大量数据时仍可能存在一定的性能开销。因此,在需要高性能的场景下,可能需要考虑其他更高效的解决方案。
  • 与其他技术的互操作性:虽然Spring EL可以与许多其他技术(如JSP、Thymeleaf等)一起使用,但在某些特定场景下可能需要额外的配置或转换工作才能实现与其他技术的无缝互操作。

五、与其他相关技术的比较和优劣分析

与Spring EL相似的其他表达式语言包括JSP表达式语言(JSP EL)、OGNL(Object-Graph Navigation Language)和MVEL(MVFLEX Expression Language)等。以下是对这些技术的简要比较:

  1. JSP EL:JSP EL主要用于JSP页面中的数据绑定和表达式计算。与Spring EL相比,JSP EL的语法和功能相对简单,且主要用于Web页面开发。然而,随着JSP的逐渐淘汰和Thymeleaf等现代模板引擎的普及,JSP EL的使用范围逐渐受限。
  2. OGNL:OGNL是一种功能强大的表达式语言,支持复杂的对象图导航和表达式计算。与Spring EL相比,OGNL提供了更多的功能和灵活性,但语法相对复杂且学习成本较高。此外,OGNL在某些场景下可能存在性能问题。
  3. MVEL:MVEL是另一个流行的表达式语言,具有简洁的语法和高效的性能表现。与Spring EL相比,MVEL在某些特定场景下可能具有更高的执行效率。然而,MVEL的社区支持和生态系统相对较弱,且在与Spring框架的集成方面可能不如Spring EL顺畅。

综上所述,Spring EL作为一种功能强大且易于使用的表达式语言,在Spring框架中发挥着重要作用。虽然它具有一定的学习成本和性能开销,但通过与Spring框架的无缝集成和丰富的功能支持,使得开发者能够更高效地处理动态数据和表达式计算任务。

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

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

相关文章

数学实验第三版(主编:李继成 赵小艳)课后练习答案(十一)(1)(2)(3)

目录 实验十一&#xff1a;非线性方程&#xff08;组&#xff09;求解 练习一 练习二 练习三 实验十一&#xff1a;非线性方程&#xff08;组&#xff09;求解 练习一 1.求莱昂纳多方程 的解 clc;clear; p[1,2,10,-20]; roots(p)ans -1.6844 3.4313i -1.6844 - 3.4313i…

U盘重装系统

因为系统管理员密码忘记&#xff0c;登录不了window系统&#xff0c;使用老毛桃制作U盘启动盘 1、下载老毛桃 下载地址为http://lmt.psydrj.com/index.html 安装后&#xff0c;桌面上显示为 2、制作U盘启动盘 启动老毛桃U盘启动装机工具&#xff0c;插入U盘&#xff0c;点击一…

2/15 homework

1、选择题 1.1、有以下程序 int main() { char a[7]"a0\0a0\0";int i,j; isizeof(a); jstrlen(a); printf("%d %d\n",i,j); } //strlen求出字符串的长度&#xff0c;其实是字符串中字符的个数&#xff0c;不包括\0 程序运行后的输出结果是…

【More Effective C++】条款21:用重载技术避免隐式类型转换

没有重载形式&#xff0c;upi3 upi1 10和upi3 20 upi2也能调用成功&#xff0c;因为进行了隐式转换 增加重载函数&#xff0c;避免隐式转换过程中临时对象的构建和析构成本C中的操作符重载要求至少有一个操作数是用户定义的类型&#xff08;如类或枚举类型&#xff09; 所以…

Springboot的it职业生涯规划系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; Springboot的it职业生涯规划系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&a…

力扣_字符串8—不同的子序列

题目 给你两个字符串 s s s 和 t t t &#xff0c;统计并返回在 s s s 的 子序列 中 t t t 出现的个数&#xff0c;结果需要对 1 0 9 7 10^9 7 1097 取模。 示例&#xff1a; 输入&#xff1a; s r a b b b i t , t r a b b i t s rabbbit, t rabbit srabbbit,tra…

【Linux】Linux编译器-gcc/g++ Linux项目自动化构建工具-make/Makefile

目录 Linux编译器-gcc/g使用 1.背景知识 Linux中头文件的目录在 Linux 库 条件编译的典型应用 2.gcc如何完成 动态库 vs 静态库 debug && release Linux项目自动化构建工具-make/Makefile 背景 用法 特殊符号 Linux编译器-gcc/g使用 1.背景知识 预处理&am…

C语言指针(初阶)

文章目录 1:内存与地址1.1内存1.2:如何理解编址 2:指针变量与地址2.1:指针变量与解引用操作符2.1.1:指针变量2.1.2:如何拆解指针类型2.1.3:解引用操作符 2.2:指针变量的大小 3:指针变量类型的意义代码1解引用修改前解引用修改后 代码2解引用修改前解引用修改后 4:const修饰指针…

如何监控另一台电脑屏幕画面?如何远程监控电脑屏幕?

在数字化时代&#xff0c;随着远程工作和协作的普及&#xff0c;电脑屏幕监控的需求也日益增长。无论是出于安全考虑、提高员工工作效率&#xff0c;还是确保企业机密的保密性&#xff0c;电脑屏幕监控都成为了企业不可或缺的管理工具。那么&#xff0c;如何监控另一台电脑屏幕…

AtCoder Beginner Contest 332 --- E - Lucky bag --- 题解

目录 E - Lucky bag 题目大意&#xff1a; 思路解析&#xff1a; 代码实现&#xff1a; E - Lucky bag 题目大意&#xff1a; 思路解析&#xff1a; 在方差中平均值只与输入有关为定值。看到数据范围为 2 < D < N < 15&#xff0c;想到是否能使用状压dp来进行解答…

Solidworks:平面草图练习

继续练习平面草图&#xff0c;感觉基本入门了。

shell脚本命令:mktemp和install

目录 一、mktemp命令 1、mktemp命令用法和格式 2、mktemp命令的实现原理 3、相关操作 3.1 创建临时文件或目录 3.2 指定临时文件名或目录名的后缀字符位数 3.3 指定临时文件或目录的父目录 3.4 指定临时文件或目录的后缀 4、实现文件独立的目录垃圾箱 二、install命令…

Leetcode-657. 机器人能否返回原点

题目&#xff1a; 在二维平面上&#xff0c;有一个机器人从原点 (0, 0) 开始。给出它的移动顺序&#xff0c;判断这个机器人在完成移动后是否在 (0, 0) 处结束。 移动顺序由字符串 moves 表示。字符 move[i] 表示其第 i 次移动。机器人的有效动作有 R&#xff08;右&#xff09…

Qt 入门

一、三个窗口的区别 QMainWindow&#xff1a;包含菜单栏、工具栏、状态栏 QWidget&#xff1a;一个普通窗口&#xff0c;不包含菜单栏、状态栏 QDialog&#xff1a;对话框&#xff0c;常用来做登入窗口、弹出窗口 二、vs qt 与QtCreator项目相互转换 在vs端先安装Qt VS Tools…

MySQL数据库基础(四):图形化开发工具DataGrip

文章目录 图形化开发工具DataGrip 一、DataGrip介绍 二、DataGrip安装 三、创建工程 四、连接数据库 五、选择要使用的数据库 六、DataGrip软件设置 1、设置字体大小 2、设置关键字大写 3、自动排版 图形化开发工具DataGrip 一、DataGrip介绍 DataGrip是JetBrains公…

[word] word 2010宏已被禁用警告关闭方法 #媒体#学习方法

word 2010宏已被禁用警告关闭方法 Word2010宏已被禁用警告关闭方法&#xff1a;在「信任中心设置」选项的宏设置中选择「禁用所有宏&#xff0c;并且不通知」即可。 每次打开Word 2010&#xff0c;都会提示「完全警告&#xff1a;宏已被禁用」提示。自从Word 2010安装完毕&am…

Java线程与进程

线程 概念 Java中&#xff0c;线程是程序执行的最小单位&#xff0c;它是进程的一个执行流&#xff0c;也是CPU调度和分配的基本单位。每个进程都可以运行多个线程&#xff0c;这些线程共享进程的内存块&#xff0c;但每个线程都有自己的堆栈和局部变量。 Java中的线程有两种…

Kafka Producer/Consumer 关系解释及测试demo

文章目录 Producer/Consumer1. 餐厅的故事2. Kafka的工作方式3. 生动的场景4. 测试Demo4.1 KafkaProducer4.2 KafkaConsumer Producer/Consumer Kafka的生产者&#xff08;Producer&#xff09;和消费者&#xff08;Consumer&#xff09;的关系&#xff0c;可以通过一个餐厅的…

Mysql运维篇(四) Xtarbackup--备份与恢复练习

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 前言 xtrabackup是Percona公司CTO Vadim参与开发的一款基于InnoDB的在线热备工具&#xff0c;具有…

Compose自定义动画API指南

很多动画API都可以自定义其参数达到不同的效果&#xff0c;Compose也提供了相应的API供开发者进行自定义动画规范。 AnimationSpec 主要用存储动画规格&#xff0c;可以自定义动画的行为&#xff0c;在animate*AsState和updateTransition函数中&#xff0c;此函数默认参数为s…