Skip to content

SPEL 表达式

文本表达式

java
ExpressionParser parser = new SpelExpressionParser();

// 解析普通字符串,输出:"Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

// 解析包含单引号的字符串(单引号需用两个单引号转义),输出:"Tony's Pizza"
String pizzaParlor = (String) parser.parseExpression("'Tony''s Pizza'").getValue();

// 解析科学计数法表示的浮点数,输出:6.0221415E23(阿伏伽德罗常数)
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// 解析十六进制整数,输出:2147483647(int类型的最大值)
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

// 解析布尔值,输出:true
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

// 解析null值,输出:null
Object nullValue = parser.parseExpression("null").getValue();

java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 1. 对象属性导航(支持链式调用)
// 获取 Tesla 的出生年份(假设 birthdate 是 Date 类型,1900 是年份偏移量)
int year = parser.parseExpression("birthdate.year + 1900").getValue(context, tesla, Integer.class);

// 2. 数组/List 索引访问(从0开始)
// 获取 inventions 数组的第4个元素(索引3)
String invention = parser.parseExpression("inventions[3]").getValue(context, tesla, String.class);

// 3. Map 访问(通过键值)
// 获取 officers Map 中 key 为 'president' 的值
Inventor president = parser.parseExpression("officers['president']").getValue(context, String.class);

// 4. 字符串字符访问
// 获取 name 字符串的第8个字符(索引7)
char letter = parser.parseExpression("name[7]").getValue(context, tesla, Character.class);

// 5. 集合投影(![])和筛选(?[])
// 获取所有成员的 name 属性列表
List<String> names = parser.parseExpression("members.![name]").getValue(context, List.class);
// 筛选 age > 18 的成员
List<Person> adults = parser.parseExpression("members.?[age > 18]").getValue(context, List.class);

// 6. 安全导航(避免 NPE)
// 如果 placeOfBirth 为 null 不会抛异常,返回 null
String city = parser.parseExpression("placeOfBirth?.city").getValue(context, String.class);

// 7. 自定义类型索引访问(Spring 6.2+)
// 注册自定义索引访问器后,可像 Map 一样操作非 Map 对象
context.addIndexAccessor(new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit"));
String fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]").getValue(context, String.class);

java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 定义一个包含 1, 2, 3, 4 的列表
List<Integer> numbers = (List<Integer>) parser.parseExpression("{1, 2, 3, 4}").getValue(context);
System.out.println(numbers); // 输出: [1, 2, 3, 4]

java
// 定义一个嵌套列表:{{'a', 'b'}, {'x', 'y'}}
List<List<String>> listOfLists = (List<List<String>>) parser.parseExpression("{{'a', 'b'}, {'x', 'y'}}").getValue(context);
System.out.println(listOfLists); // 输出: [[a, b], [x, y]]

java
// 定义一个空列表
List<Object> emptyList = (List<Object>) parser.parseExpression("{}").getValue(context);
System.out.println(emptyList); // 输出: []

java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 定义一个包含 name 和 dob 的 Map
Map<String, Object> inventorInfo = (Map<String, Object>) 
    parser.parseExpression("{name:'Nikola', dob:'10-July-1856'}").getValue(context);

System.out.println(inventorInfo); 
// 输出: {name=Nikola, dob=10-July-1856}

java
// 定义一个嵌套 Map:包含 name(嵌套)和 dob(嵌套)
Map<String, Map<String, Object>> mapOfMaps = (Map<String, Map<String, Object>>) 
    parser.parseExpression("{name:{first:'Nikola', last:'Tesla'}, dob:{day:10, month:'July', year:1856}}")
    .getValue(context);

System.out.println(mapOfMaps); 
// 输出: {name={first=Nikola, last=Tesla}, dob={day=10, month=July, year=1856}}

java
// 定义一个空 Map
Map<Object, Object> emptyMap = (Map<Object, Object>) parser.parseExpression("{}").getValue(context);
System.out.println(emptyMap); // 输出: {}

创建空数组

java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 创建一个长度为 4 的 int 数组
int[] emptyArray = (int[]) parser.parseExpression("new int[4]").getValue(context);
System.out.println(Arrays.toString(emptyArray)); // 输出: [0, 0, 0, 0]

创建并初始化数组

java
// 创建并初始化 int 数组
int[] initializedArray = (int[]) parser.parseExpression("new int[] {1, 2, 3}").getValue(context);
System.out.println(Arrays.toString(initializedArray)); // 输出: [1, 2, 3]

创建多维数组

java
// 创建一个 4x5 的二维 int 数组(不能直接初始化)
int[][] multiDimArray = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
System.out.println(multiDimArray.length); // 输出: 4(第一维长度)
System.out.println(multiDimArray[0].length); // 输出: 5(第二维长度)

方法

java
ExpressionParser parser = new SpelExpressionParser();

// 调用字符串的 substring(1, 3) 方法
String result = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
System.out.println(result); // 输出: "bc"

java
// 假设 societyContext 中有 isMember 方法
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')")
                         .getValue(societyContext, Boolean.class);
System.out.println(isMember); // 输出: true(根据实际方法逻辑)

java
// 调用 Math.max(5, 10)
int max = parser.parseExpression("T(java.lang.Math).max(5, 10)").getValue(Integer.class);
System.out.println(max); // 输出: 10

java
// 调用 String.format(String format, Object... args)
String formatted = parser.parseExpression("'Hello, %s!'.format('World')").getValue(String.class);
System.out.println(formatted); // 输出: "Hello, World!"

java
ExpressionParser parser = new SpelExpressionParser();

// 1. 基本关系运算
boolean isEqual = parser.parseExpression("2 eq 2").getValue(Boolean.class); // true

// 2. between 判断范围
boolean inRange = parser.parseExpression("5 between {1, 10}").getValue(Boolean.class); // true

// 3. instanceof 类型检查
boolean isString = parser.parseExpression("'abc' instanceof T(String)").getValue(Boolean.class); // true

// 4. matches 正则匹配
boolean isValid = parser.parseExpression("'123' matches '\\d+'").getValue(Boolean.class); // true

java
// 1. AND 运算
boolean result = parser.parseExpression("true and false").getValue(Boolean.class); // false

// 2. OR 运算
boolean result = parser.parseExpression("true or false").getValue(Boolean.class); // true

// 3. NOT 运算
boolean result = parser.parseExpression("not false").getValue(Boolean.class); // true

java
// 1. 字符串拼接
String concat = parser.parseExpression("'Hello' + ' ' + 'World'").getValue(String.class); // "Hello World"

// 2. 字符减法(ASCII 码计算)
char ch = parser.parseExpression("'d' - 3").getValue(Character.class); // 'a' (d=100, a=97)

// 3. 字符串重复
String repeated = parser.parseExpression("'abc' * 2").getValue(String.class); // "abcabc"

java
// 1. 基本运算
int sum = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
int power = parser.parseExpression("2 ^ 3").getValue(Integer.class); // 8

// 2. 自增/自减
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
int val = parser.parseExpression("counter++").getValue(context, inventor, Integer.class); // 0 (后自增)
int newVal = parser.parseExpression("counter").getValue(context, inventor, Integer.class); // 1

java
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

// 1. 使用 setValue 赋值
parser.parseExpression("name").setValue(context, inventor, "Nikola Tesla");

// 2. 直接在表达式内赋值
String name = parser.parseExpression("name = 'Tesla'").getValue(context, inventor, String.class);

java
public class ListConcatenation implements OperatorOverloader {
    @Override
    public boolean overridesOperation(Operation op, Object left, Object right) {
        return op == Operation.ADD && left instanceof List && right instanceof List;
    }

    @Override
    public Object operate(Operation op, Object left, Object right) {
        List<Object> result = new ArrayList<>((List<?>) left);
        result.addAll((List<?>) right);
        return result;
    }
}

// 使用自定义运算符
StandardEvaluationContext context = new StandardEvaluationContext();
context.setOperatorOverloader(new ListConcatenation());

List<?> combined = parser.parseExpression("{1, 2} + {3, 4}").getValue(context, List.class); // [1, 2, 3, 4]

java
ExpressionParser parser = new SpelExpressionParser();

// 获取 java.util.Date 的 Class 对象
Class<?> dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);

// 获取 String 的 Class 对象(java.lang 包可省略包名)
Class<?> stringClass = parser.parseExpression("T(String)").getValue(Class.class);

java
// 调用 Math.random()
double random = parser.parseExpression("T(java.lang.Math).random()").getValue(Double.class);

// 调用 Integer.parseInt()
int num = parser.parseExpression("T(Integer).parseInt('123')").getValue(Integer.class);

java
// 比较枚举值
boolean result = parser.parseExpression(
    "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR"
).getValue(Boolean.class); // true(枚举 ordinal 比较)

java
// 等效于 T(java.lang.String)
Class<?> clazz = parser.parseExpression("T(String)").getValue(Class.class);

java
// 必须写全名
Class<?> clazz = parser.parseExpression("T(java.util.Date)").getValue(Class.class);

java
StandardEvaluationContext context = new StandardEvaluationContext();
StandardTypeLocator typeLocator = new StandardTypeLocator();
typeLocator.registerImport("com.example"); // 添加自定义包
context.setTypeLocator(typeLocator);

// 现在可以省略包名
Class<?> clazz = parser.parseExpression("T(User)").getValue(context, Class.class);

java
ExpressionParser parser = new SpelExpressionParser();

// 调用 java.util.Date 的无参构造
Date now = parser.parseExpression("new java.util.Date()").getValue(Date.class);
System.out.println(now); // 输出当前时间

// 调用自定义类的构造方法(需全限定类名)
Inventor einstein = parser.parseExpression(
    "new org.example.Inventor('Albert Einstein', 'German')"
).getValue(Inventor.class);
System.out.println(einstein.getName()); // 输出: Albert Einstein

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("members", new ArrayList<Inventor>());

// 在 List.add() 中直接构造对象
parser.parseExpression(
    "#members.add(new org.example.Inventor('Nikola Tesla', 'Serbian'))"
).getValue(context);

List<Inventor> members = (List<Inventor>) context.lookupVariable("members");
System.out.println(members.get(0).getName()); // 输出: Nikola Tesla

变量

基本变量

java
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

// 设置变量 #newName
context.setVariable("newName", "Mike Tesla");

// 在表达式中使用变量
parser.parseExpression("name = #newName").getValue(context, tesla);
System.out.println(tesla.getName()); // 输出: Mike Tesla

/**
 * 变量命名规则
 * 首字符:字母(支持 Unicode)、下划线(_)或美元符号($)。
 * 后续字符:字母、数字、_ 或 $。
 * 示例合法变量名:
 * #user、#_count、#$value、#姓名
 */

#this:当前对象

java
List<Integer> primes = List.of(2, 3, 5, 7, 11, 13, 17);
context.setVariable("primes", primes);

// 筛选大于 10 的元素
List<Integer> result = parser.parseExpression("#primes.?[#this > 10]")
                            .getValue(context, List.class);
System.out.println(result); // 输出: [11, 13, 17]

#root:根对象

java
Inventor tesla = new Inventor("Nikola Tesla");
tesla.setInventions("Telephone repeater", "Tesla coil transformer");

// 使用 #root 和 #this 拼接字符串
String expr = "#root.inventions.![#root.name + ' invented the ' + #this + '.']";
List<String> inventions = parser.parseExpression(expr)
                              .getValue(context, tesla, List.class);

// 输出: 
// ["Nikola Tesla invented the Telephone repeater.", 
//  "Nikola Tesla invented the Tesla coil transformer."]

自定义函数

注册反射方法

java
// 定义静态工具方法
public class StringUtils {
    public static String reverseString(String input) {
        return new StringBuilder(input).reverse().toString();
    }
}

java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 注册反射方法
Method method = StringUtils.class.getMethod("reverseString", String.class);
context.setVariable("reverseString", method);

// 调用函数
String result = parser.parseExpression("#reverseString('hello')")
                    .getValue(context, String.class);
System.out.println(result); // 输出: "olleh"

java
// 注册 String.format() 方法
MethodHandle mh = MethodHandles.lookup()
    .findVirtual(String.class, "formatted", 
        MethodType.methodType(String.class, Object[].class));
context.setVariable("format", mh);

// 调用函数(支持可变参数)
String message = parser.parseExpression(
    "#format('Hello, %s! Today is %s.', 'Alice', 'Monday')"
).getValue(context, String.class);
System.out.println(message); // 输出: "Hello, Alice! Today is Monday."

java
// 提前绑定模板和参数
String template = "Result: %s (Score: %d)";
Object[] args = new Object[]{"Pass", 90};
MethodHandle boundMh = MethodHandles.lookup()
    .findVirtual(String.class, "formatted", 
        MethodType.methodType(String.class, Object[].class))
    .bindTo(template)
    .bindTo(args);
context.setVariable("preboundFormat", boundMh);

// 调用时无需参数
String result = parser.parseExpression("#preboundFormat()")
                     .getValue(context, String.class);
System.out.println(result); // 输出: "Result: Pass (Score: 90)"

可变参数 (Varargs) 支持

java
ExpressionParser parser = new SpelExpressionParser();

// 调用 String.format(),直接传递参数
String result = parser.parseExpression("'%s is color #%d'.formatted('blue', 1)")
                    .getValue(String.class);
System.out.println(result); // 输出: "blue is color #1"

java
// 通过数组传递可变参数
String result = parser.parseExpression(
    "'%s is color #%d'.formatted(new Object[] {'blue', 1})"
).getValue(String.class);
System.out.println(result); // 输出: "blue is color #1"

java
// 使用 SpEL 内联列表 {} 传递参数
String result = parser.parseExpression(
    "'%s is color #%d'.formatted({'blue', 1})"
).getValue(String.class);
System.out.println(result); // 输出: "blue is color #1"

java
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 注册自定义函数(反转多个字符串)
Method method = StringUtils.class.getMethod("reverseStrings", String[].class);
context.setVariable("reverseStrings", method);

// 混合传递字符串、数字、浮点数(自动转换为 String)
String output = parser.parseExpression(
    "#reverseStrings('SpEL', 1, 10F / 5, 3.0000)"
).getValue(context, String.class);
System.out.println(output); // 输出: "3.0, 2.0, 1, SpEL"

Bean 引用

java
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();

// 设置 Bean 解析器(需实现 BeanResolver 接口)
context.setBeanResolver(new MyBeanResolver());

// 通过 @ 引用 Bean
Object myService = parser.parseExpression("@myService").getValue(context);

java
// 引用名为 "order.service" 的 Bean
Object orderService = parser.parseExpression("@'order.service'").getValue(context);

java
// 引用名为 "someFactoryBean" 的 FactoryBean 实例
Object factoryBean = parser.parseExpression("&someFactoryBean").getValue(context);

三元运算符

java
ExpressionParser parser = new SpelExpressionParser();

// 基本用法
String result = parser.parseExpression("true ? 'A' : 'B'").getValue(String.class);
System.out.println(result); // 输出: "A"

// 使用变量
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("score", 85);

String grade = parser.parseExpression("#score >= 90 ? 'A' : (#score >= 60 ? 'B' : 'C')")
                   .getValue(context, String.class);
System.out.println(grade); // 输出: "B"

java
ExpressionParser parser = new SpelExpressionParser();

// 如果 name 为 null,返回 "Unknown"
String result = parser.parseExpression("name?:'Unknown'")
                    .getValue(new Inventor(), String.class);
System.out.println(result); // 输出: "Unknown"(因为 name 为 null)

java
Inventor tesla = new Inventor("", "Serbian"); // name 是空字符串

// 如果 name 为 null 或空,返回 "Elvis Presley"
String name = parser.parseExpression("name?:'Elvis Presley'")
                   .getValue(tesla, String.class);
System.out.println(name); // 输出: "Elvis Presley"

安全导航运算符

安全访问属性

java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));

// 正常访问嵌套属性
String city = parser.parseExpression("placeOfBirth?.city")
                   .getValue(context, tesla, String.class);
System.out.println(city); // 输出: "Smiljan"

// placeOfBirth 为 null 时安全返回 null
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("placeOfBirth?.city")
            .getValue(context, tesla, String.class);
System.out.println(city); // 输出: null(不会抛 NPE)

安全调用方法

java
// 如果 calculator 为 null,返回 null
Double result = parser.parseExpression("#calculator?.add(1, 2)")
                    .getValue(context, Double.class);

java
IEEE society = new IEEE();
society.setMembers(Arrays.asList(new Inventor("Nikola Tesla")));

// 安全访问集合元素
Inventor inventor = parser.parseExpression("members?.[0]")
                         .getValue(context, society, Inventor.class);
System.out.println(inventor.getName()); // 输出: "Nikola Tesla"

// members 为 null 时安全返回 null
society.setMembers(null);
inventor = parser.parseExpression("members?.[0]")
               .getValue(context, society, Inventor.class);
System.out.println(inventor); // 输出: null

java
// 筛选国籍为 Serbian 的成员(如果 members 为 null,返回 null)
List<Inventor> serbians = parser.parseExpression(
    "members?.?[nationality == 'Serbian']"
).getValue(context, society, List.class);

java
// 选择第一个符合条件的成员
Inventor first = parser.parseExpression(
    "members?.^[nationality == 'Serbian']"
).getValue(context, society, Inventor.class);

// 选择最后一个符合条件的成员
Inventor last = parser.parseExpression(
    "members?.$[nationality == 'Serbian']"
).getValue(context, society, Inventor.class);

java
// 提取所有成员的出生地城市(如果 members 为 null,返回 null)
List<String> cities = parser.parseExpression(
    "members?.![placeOfBirth.city]"
).getValue(context, society, List.class);

java
// 错误:如果 address 为 null,仍会抛 NPE
String risky = parser.parseExpression("person.address.city")
                   .getValue(context, String.class);

// 正确:全程安全导航
String safe = parser.parseExpression("person?.address?.city")
                  .getValue(context, String.class);

集合筛选

基本用法

java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 原始集合
List<Inventor> members = Arrays.asList(
    new Inventor("Nikola Tesla", "Serbian"),
    new Inventor("Marie Curie", "Polish"),
    new Inventor("Albert Einstein", "German")
);
context.setVariable("members", members);

// 筛选国籍为 Serbian 的成员
List<Inventor> serbians = (List<Inventor>) parser.parseExpression(
    "#members.?[nationality == 'Serbian']"
).getValue(context);
System.out.println(serbians); // 输出: [Inventor(name=Nikola Tesla, nationality=Serbian)]

java
Map<String, Integer> scores = Map.of("Alice", 25, "Bob", 30, "Charlie", 20);
context.setVariable("scores", scores);

// 筛选值小于 27 的条目
Map<String, Integer> filtered = (Map<String, Integer>) parser.parseExpression(
    "#scores.?[value < 27]"
).getValue(context);
System.out.println(filtered); // 输出: {Alice=25, Charlie=20}

特殊筛选操作

java
// 获取第一个国籍为 Serbian 的成员
Inventor firstSerbian = parser.parseExpression(
    "#members.^[nationality == 'Serbian']"
).getValue(context, Inventor.class);
System.out.println(firstSerbian.getName()); // 输出: Nikola Tesla

java
// 获取最后一个国籍为 German 的成员
Inventor lastGerman = parser.parseExpression(
    "#members.$[nationality == 'German']"
).getValue(context, Inventor.class);
System.out.println(lastGerman.getName()); // 输出: Albert Einstein

安全筛选

java
// 安全筛选(members 为 null 时返回 null)
List<Inventor> result = (List<Inventor>) parser.parseExpression(
    "#members?.?[nationality == 'French']"
).getValue(context);
System.out.println(result); // 输出: null(不会抛异常)

集合投影

基本用法

提取对象属性

java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 原始集合
List<Inventor> members = Arrays.asList(
    new Inventor("Nikola Tesla", new PlaceOfBirth("Smiljan")),
    new Inventor("Michael Pupin", new PlaceOfBirth("Idvor"))
);
context.setVariable("members", members);

// 提取所有成员的出生地城市
List<String> cities = (List<String>) parser.parseExpression(
    "#members.![placeOfBirth.city]"
).getValue(context);
System.out.println(cities); // 输出: ["Smiljan", "Idvor"]

操作 Map 条目

java
Map<String, Integer> scores = Map.of("Alice", 90, "Bob", 85);
context.setVariable("scores", scores);

// 提取所有键值对的字符串表示
List<String> entries = (List<String>) parser.parseExpression(
    "#scores.![key + ': ' + value]"
).getValue(context);
System.out.println(entries); // 输出: ["Alice: 90", "Bob: 85"]

安全投影

java
// 安全投影(members 为 null 时返回 null)
List<String> safeCities = (List<String>) parser.parseExpression(
    "#members?.![placeOfBirth.city]"
).getValue(context);
System.out.println(safeCities); // 输出: null(不会抛异常)

表达式模板

基本用法

定义模板

java
ExpressionParser parser = new SpelExpressionParser();

// 模板:静态文本 + 动态表达式
String result = parser.parseExpression(
    "随机数是:#{T(java.lang.Math).random()}",
    new TemplateParserContext() // 使用 #{ } 作为分隔符
).getValue(String.class);

System.out.println(result); // 输出: "随机数是:0.7038186818312008"

多表达式块

java
String name = "Alice";
int score = 95;

// 多个动态表达式
String report = parser.parseExpression(
    "学生 #{#name} 的成绩是 #{#score} 分",
    new TemplateParserContext()
).getValue(context, String.class);

System.out.println(report); // 输出: "学生 Alice 的成绩是 95 分"

自定义分隔符

java
public class CustomParserContext implements ParserContext {
    @Override
    public String getExpressionPrefix() { return "{{"; }
    @Override
    public String getExpressionSuffix() { return "}}"; }
    @Override
    public boolean isTemplate() { return true; }
}

// 使用自定义分隔符 {{ }}
String output = parser.parseExpression(
    "当前时间:{{T(java.time.LocalTime).now()}}",
    new CustomParserContext()
).getValue(String.class);
System.out.println(output); // 输出: "当前时间:15:30:45.123"

更新: 2025-07-20 16:08:53
原文: https://www.yuque.com/lsxxyg/sz/fiqlmye0cgag3f04