SpEL表达式使用方法

1 SpEL简介

SpEL(Spring Expression Language)是一种用于在Spring框架中进行数据绑定和执行业务逻辑的表达式语言。Spring EL提供了一种简洁、灵活和强大的方式来访问对象的属性、调用方法、执行运算和逻辑判断等操作。

官方文档:https://docs.spring.io/spring-framework/reference/core/expressions.html

2 基本使用

2.1 通过@Value注解使用

可以将@Value注解加在字段,方法,方法参数,构造方法参数上使用,${…}表示直接读取上下文的属性值,而#{…}就表示使用SpEL进行运算。

字段示例:

public class FieldValueTestBean {@Value("#{systemProperties['user.region'] }")private String defaultLocale;private String defaultTimezone;public void setDefaultLocale(String defaultLocale) {this.defaultLocale = defaultLocale;}
}

方法示例:

public class PropertyValueTestBean {private String defaultLocale;@Value("#{ systemProperties['user.region'] }")public void setDefaultLocale(String defaultLocale) {this.defaultLocale = defaultLocale;}public String getDefaultLocale() {return this.defaultLocale;}
}

@Autowired方法示例:

public class SimpleMovieLister {private MovieFinder movieFinder;private String defaultLocale;@Autowiredpublic void configure(MovieFinder movieFinder,@Value("#{ systemProperties['user.region'] }") String defaultLocale) {this.movieFinder = movieFinder;this.defaultLocale = defaultLocale;}// ...
}

构造方法示例:

public class MovieRecommender {private String defaultLocale;private CustomerPreferenceDao customerPreferenceDao;public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,@Value("#{systemProperties['user.country']}") String defaultLocale) {this.customerPreferenceDao = customerPreferenceDao;this.defaultLocale = defaultLocale;}// ...
}

2.2 编程方式执行SpEL

2.2.1 基本使用

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'"); 
String message = (String) exp.getValue(); // "Hello World"

2.2.2 设置根对象

Inventor tesla = new Inventor();
tesla.setName("Nikola Tesla");ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(tesla); // "Nikola Tesla"

2.2.3 设置上下文类型

以上的例子中,会创建一个上下文(EvaluationContext),默认实现类为StandardEvaluationContext
上下文接口有两种实现:

  • StandardEvaluationContext 完整的上下文功能
  • SimpleEvaluationContext 精简版的上下文,去除了Java类型参照、构造器、Bean参照等功能
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();Inventor tesla = new Inventor();
tesla.setName("Nikola Tesla");ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(context, tesla); // "Nikola Tesla"

2.2.4 SpEL配置

可通过SpelParserConfiguration的构造方法,设置SpEL相关的配置, 例如:

class Demo {public List<String> list;
}// 开启空对象自动初始化,开启集合自动增长
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
ExpressionParser parser = new SpelExpressionParser(config);ExpressionParser parser = new SpelExpressionParser(config);
Expression expression = parser.parseExpression("list[3]");
Demo demo = new Demo();
Object o = expression.getValue(demo); // 将不会报空指针异常,list会自动初始化, 并且将包含4个元素, 每个元素为空字符串

可以配置的功能有:

  • autoGrowNullReferences 开启自动生成对象,默认 false
  • autoGrowCollections 开启集合自动增长,默认 false
  • maximumAutoGrowSize 集合增长最大长度,默认 Integer.MAX_VALUE
  • maximumExpressionLength 表达式最大长度,默认 1000
  • compilerMode 开启预编译, 默认 OFF

2.2.4 编译SpEL

SpEL编译功能默认是关闭的,如果对性能有要求,那么可以开启预编译功能,

  • SpelCompilerMode.OFF 默认关闭
  • SpelCompilerMode.IMMEDIATE 立即编译,在第一次使用时就会编译
  • SpelCompilerMode.MIXED 混合编译,再使用几次后才会尝试编译,如果编译出错,就会用上次的编译成功的结果
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,this.getClass().getClassLoader());SpelExpressionParser parser = new SpelExpressionParser(config);
Expression expr = parser.parseExpression("payload");
MyMessage message = new MyMessage();
Object payload = expr.getValue(message);

3 表达式用法

3.1 字面量

  • String 使用单引号包裹
  • Number 使用int、long、float、double表示
  • Boolean true/false
  • Null null
xpressionParser parser = new SpelExpressionParser();// evaluates to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();// evaluates to "Tony's Pizza"
String pizzaParlor = (String) parser.parseExpression("'Tony''s Pizza'").getValue();double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();// evaluates to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();boolean trueValue = (Boolean) parser.parseExpression("true").getValue();Object nullValue = parser.parseExpression("null").getValue();

3.2 属性、数组、列表、字典、索引

3.2.1 直接访问

通过对象.属性即可快速访问属性

// evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);

属性访问对首字母大小写是不敏感的,所以上面的表达式也可以写成"Birthdate.Year + 1900",“PlaceOfBirth.City”。
另外可以通过getter来访问,比如 “getPlaceOfBirth().getCity()”

通过中括号加数字来访问数组、列表的指定的元素

xpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();// Inventions Array// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(context, tesla, String.class);// Members List// evaluates to "Nikola Tesla"
String name = parser.parseExpression("members[0].name").getValue(context, ieee, String.class);// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(context, ieee, String.class);

通过中括号加键名访问字典的元素

// Officer's DictionaryInventor pupin = parser.parseExpression("officers['president']").getValue(societyContext, Inventor.class);// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(societyContext, String.class);// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(societyContext, "Croatia");

3.2.2 集合、字典过滤

使用.?[选择表达式]可以方便的对数组、集合、字典进行过滤,类似于Java Stream的filter方法

List<Inventor> list = (List<Inventor>) parser.parseExpression("members.?[nationality == 'Serbian']").getValue(societyContext);Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);

.^[选择表达式]表示只取第一个,.$[选择表达式]表示只取最后一个

Inventor first = (Inventor) parser.parseExpression("members.^[nationality == 'Serbian']").getValue(societyContext);Inventor last= (Inventor) parser.parseExpression("members.$[nationality == 'Serbian']").getValue(societyContext);

3.2.3 集合投影

使用.![投影表达式]可以方便的对数组、集合、字典进行转换成别的集合,类似于Java Stream的map方法

// evaluates to ["Smiljan", "Idvor"]
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]").getValue(societyContext, List.class);

3.3 定义列表、字典、数组

定义列表

// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);

定义字典

// evaluates to a Java map containing the two entries
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);

定义数组

// 
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[] {1, 2, 3}").getValue(context);// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);

初始化数组的表达式是无法预编译的

3.4 执行方法

和调用Java的方法一样,在SpEL中也是可以直接调用对象的方法的

// string literal, evaluates to "bc"
String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);

3.5 运算符

3.5.1 关系运算

// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);// evaluates to true
boolean trueValue = parser.parseExpression("'black' <= 'block'").getValue(Boolean.class); // evaluates to true
boolean falseValue = parser.parseExpression("'abc' > null").getValue(Boolean.class);// uses CustomValue:::compareTo
boolean trueValue = parser.parseExpression("new CustomValue(2) > new CustomValue(1)").getValue(Boolean.class); 

除了用符号外,还可以用文本代替:

  • lt (<)
  • gt (>)
  • le (<=)
  • ge (>=)
  • eq (==)
  • ne (!=)

除了以上的关系运算,还支持between, instanceof, matches 关系运算

boolean result;// evaluates to true
result = parser.parseExpression("1 between {1, 5}").getValue(Boolean.class);// evaluates to true
result = parser.parseExpression("'elephant' between {'aardvark', 'zebra'}").getValue(Boolean.class);// evaluates to true
result = parser.parseExpression("123 instanceof T(Integer)").getValue(Boolean.class);// evaluates to true
result = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

3.5.2 逻辑运算

SpEL 支持一下逻辑运算

  • 与 (and, &&)
  • 或 (or , ||)
  • 非 ( not, !)
// -- AND --// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);// -- OR --// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);// -- NOT --// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);// -- AND and NOT --String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

3.5.3 字符串操作

+ 号表示字符串拼接,- 号表示单字符的ASCII码减去后的新值, * 号表示重复

// -- Concatenation --// evaluates to "hello world"
String helloWorld = parser.parseExpression("'hello' + ' ' + 'world'").getValue(String.class);// -- Character Subtraction --// evaluates to 'a'
char ch = parser.parseExpression("'d' - 3").getValue(char.class);// -- Repeat --// evaluates to "abcabc"
String repeated = parser.parseExpression("'abc' * 2").getValue(String.class);

3.5.4 数学运算

SpEL中支持以下数学运算

  • 加 (+)
  • 减(-)
  • 自增 (++)
  • 自减 (--)
  • 乘 (*)
  • 除 (/ , div)
  • 取模 (% , mod)
  • 指数幂 (^)
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();// -- Addition --int two = parser.parseExpression("1 + 1").getValue(int.class);  // 2// -- Subtraction --int four = parser.parseExpression("1 - -3").getValue(int.class);  // 4double d = parser.parseExpression("1000.00 - 1e4").getValue(double.class);  // -9000// -- Increment --// The counter property in Inventor has an initial value of 0.// evaluates to 2; counter is now 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, int.class);// evaluates to 5; counter is now 2
int five = parser.parseExpression("3 + ++counter").getValue(context, inventor, int.class);// -- Decrement --// The counter property in Inventor has a value of 2.// evaluates to 6; counter is now 1
int six = parser.parseExpression("counter-- + 4").getValue(context, inventor, int.class);// evaluates to 5; counter is now 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, int.class);// -- Multiplication --six = parser.parseExpression("-2 * -3").getValue(int.class);  // 6double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(double.class);  // 24.0// -- Division --int minusTwo = parser.parseExpression("6 / -3").getValue(int.class);  // -2double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(double.class);  // 1.0// -- Modulus --int three = parser.parseExpression("7 % 4").getValue(int.class);  // 3int oneInt = parser.parseExpression("8 / 5 % 2").getValue(int.class);  // 1// -- Exponential power --int maxInt = parser.parseExpression("(2^31) - 1").getValue(int.class);  // Integer.MAX_VALUEint minInt = parser.parseExpression("-2^31").getValue(int.class);  // Integer.MIN_VALUE// -- Operator precedence --int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(int.class);  // -21

3.5.5 赋值运算

在SpEL中也可以通过=表达式或者setValue方法执行对某个变量赋值的操作

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();// 通过setValue方法赋值
parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic");// 通过 = 在表达式中赋值
String aleks = parser.parseExpression("name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);

3.5.6 三元运算

SpEL也支持 boolean ? trueValue : falseValue 形式的三元运算

String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);

3.5.7 Elvis(埃尔维斯) 运算

埃尔维斯运算符是三元运算符语法的一种简化形式,以下示例中当name为null或者空字符串时,返回"Unknown"

ExpressionParser parser = new SpelExpressionParser();String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class);
System.out.println(name);  // 'Unknown'

3.5.8 Safe Navigation(安全导航)

Safe Navigation Operator(安全导航操作符)?.是一种用于处理可能为 null 的对象引用的操作符。它可以在访问可能为 null 的对象成员时避免空指针异常。

// evaluates to null - does not throw NullPointerException
city = parser.parseExpression("placeOfBirth?.city") .getValue(context, tesla, String.class);

也可用于数组、集合、字典的顾虑和投影

  • 安全过滤: ?.?[过滤表达式]
  • 安全首位: ?.\^[过滤表达式]
  • 安全末位: ?.$[过滤表达式]
  • 安全投影: ?.![投影表达式]

安全导航还可以连用

#person?.address?.city

3.5.9 运算符重载

默认情况下,在 SpEL 的操作枚举(加法、减法、除法、乘法、取模和幂)中定义的数学运算支持像数字这样的简单类型。通过提供一个OperatorOverloader的实现,表达式语言可以在其他类型上支持这些操作。
例如,如果我们想要重载加法运算符,以便使用+符号将两个列表连接起来,我们可以如下实现一个自定义的OperatorOverloader。

pubic class ListConcatenation implements OperatorOverloader {@Overridepublic boolean overridesOperation(Operation operation, Object left, Object right) {return (operation == Operation.ADD &&left instanceof List && right instanceof List);}@Overridepublic Object operate(Operation operation, Object left, Object right) {if (operation == Operation.ADD &&left instanceof List list1 && right instanceof List list2) {List result = new ArrayList(list1);result.addAll(list2);return result;}throw new UnsupportedOperationException("No overload for operation %s and operands [%s] and [%s]".formatted(operation, left, right));}
}StandardEvaluationContext context = new StandardEvaluationContext();
context.setOperatorOverloader(new ListConcatenation());// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List.class);

一个操作符重载器不会改变一个操作符的默认语义。例如,在上述例子中 2+2 仍然计算结果为 4。

3.6 类型

可以使用特殊的 T 操作符来指定一个Java类的实例。静态方法也通过使用这个操作符来调用。如果使用StandardEvaluationContext,那么 java.lang 包内的类型的 T()引用不需要是写完整的包名,但所有其他类型引用必须写完整。

Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class);

如果使用自定义EvaluationContext,则需要手动配置一个带有特定ClassLoader的StandardTypeLocator,以确保 SpEL 表达式解析器能够定位用户类型。
例如,spring-context模块中的StandardBeanExpressionResolver使用相应BeanFactory的 beanClassLoader来配置StandardTypeLocator。

3.7 对象构造

需要构造对象的时候,需要用 new 关键字并且要写完整的包名+类名 (除了java.lang包)

Inventor einstein = p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')").getValue(Inventor.class);

3.8 变量

EvaluationContext 中使用setVariable()方法设置变量,在表达式中使用#变量名来获取变量的值, 变量名的命名规范遵循Java语言变量名的命名规范。

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("newName", "Mike Tesla");parser.parseExpression("name = #newName").getValue(context, tesla);
System.out.println(tesla.getName());  // "Mike Tesla"

有特殊的两个变量 #this#root#this变量总是被定义并且指代当前正在评估的对象。#root变量总是被定义并且指代根上下文对象。

// Create a list of prime integers.
List<Integer> primes = List.of(2, 3, 5, 7, 11, 13, 17);// Create parser and set variable 'primes' as the list of integers.
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("primes", primes);// Select all prime numbers > 10 from the list (using selection ?{...}).
String expression = "#primes.?[#this > 10]";// Evaluates to a list containing [11, 13, 17].
List<Integer> primesGreaterThanTen =parser.parseExpression(expression).getValue(context, List.class);

#this#root共用:

// Create parser and evaluation context.
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();// Create an inventor to use as the root context object.
Inventor tesla = new Inventor("Nikola Tesla");
tesla.setInventions("Telephone repeater", "Tesla coil transformer");// Iterate over all inventions of the Inventor referenced as the #root
// object, and generate a list of strings whose contents take the form
// "<inventor's name> invented the <invention>." (using projection !{...}).
String expression = "#root.inventions.![#root.name + ' invented the ' + #this + '.']";// Evaluates to a list containing:
// "Nikola Tesla invented the Telephone repeater."
// "Nikola Tesla invented the Tesla coil transformer."
List<String> results = parser.parseExpression(expression).getValue(context, tesla, List.class);

3.9 方法

通过注册用户定义的函数来扩展 SpEL,这些函数可以在表达式中通过使用#functionName(…)语法来调用。函数可以通过setVariable()方法在EvaluationContext实现中作为变量进行注册。
StandardEvaluationContext还定义了registerFunction(…)方法,这些方法提供了一种方便的方式来将一个函数注册为java.lang.reflect.Methodjava.lang.invoke.MethodHandle

注册一个Mehtod:

public abstract class StringUtils {public static String reverseString(String input) {return new StringBuilder(input).reverse().toString();}
}ExpressionParser parser = new SpelExpressionParser();EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",StringUtils.class.getMethod("reverseString", String.class));// evaluates to "olleh"
String helloWorldReversed = parser.parseExpression("#reverseString('hello')").getValue(context, String.class);

注册一个MethodHandle:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",MethodType.methodType(String.class, Object[].class));
context.setVariable("message", mh);// evaluates to "Simple message: <Hello World>"
String message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')").getValue(context, String.class);

如果目标和所有的参数都被绑定,这很可能会有更好的性能。在那种情况下,在 SpEL 表达式中不需要任何参数

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();String template = "This is a %s message with %s words: <%s>";
Object varargs = new Object[] { "prerecorded", 3, "Oh Hello World!", "ignored" };
MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",MethodType.methodType(String.class, Object[].class)).bindTo(template).bindTo(varargs); //here we have to provide arguments in a single array binding
context.setVariable("message", mh);// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
String message = parser.parseExpression("#message()").getValue(context, String.class);

3.10 Bean参照

如果上下文已经使用一个 Bean 解析器进行了配置,你可以通过使用@符号从表达式中查找 beans。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@something").getValue(context);

要访问工厂 bean 本身,应该在 bean 名称前加上一个&符号

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("&foo").getValue(context);

4 模板表达式

模板表达式允许将字面文本与一个或多个求值块混合。每个求值块用你可以定义的前缀和后缀字符来界定。一个常见的选择是使用#{ }作为界定符。

String randomPhrase = parser.parseExpression("random number is #{T(java.lang.Math).random()}",new TemplateParserContext()).getValue(String.class);// evaluates to "random number is 0.7038186818312008"

TemplateParserContext ParserContext接口的实现类,定义了用#{ }包裹SpEL内容。

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

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

相关文章

C# 多线程:并发编程的利器

在现今日益复杂的软件开发环境中&#xff0c;多线程编程已经成为提升应用程序性能和响应速度的关键技术。C# 作为一种现代、功能强大的编程语言&#xff0c;提供了丰富的多线程支持&#xff0c;使开发者能够充分利用多核处理器和并行计算的优势。本文将深入探讨C#中的多线程编程…

网络协议三

数据中心 一、DNS 现在网站的数目非常多&#xff0c;常用的网站就有二三十个&#xff0c;如果全部用 IP 地址进行访问&#xff0c;恐怕很难记住 根 DNS 服务器 &#xff1a;返回顶级域 DNS 服务器的 IP 地址 顶级域 DNS 服务器&#xff1a;返回权威 DNS 服务器的 IP 地址 …

汇凯金业:贵金属交易规则有哪些

贵金属投资目前非常火热&#xff0c;许多投资者从中获得了可观的收益。新手投资者在进入贵金属市场及其交易之前&#xff0c;务必要了解清楚贵金属的交易规则。了解规则和差异能帮助新手更好地进行贵金属投资交易。下面我们以现货类贵金属为例&#xff0c;详细说明贵金属的交易…

一周学会Django5 Python Web开发 - Django5内置Auth认证系统-用户修改密码实现

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计59条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

关于在 Ubuntu 下安装配置和调优 FTP 服务器

今天我在阿贝云的免费服务器上部署测试FTP服务器&#xff0c;这个阿贝云真是个不错的免费云服务器啊,配置虽然不高,但完全足够搭建一个FTP服务器使用了。1核CPU、1G内存、10G硬盘、5M带宽,对我这个喜欢折腾的家伙来说可太好用了。 首先,让我简单介绍下今天要用到的FTP服务器软…

Mybatis杂记

group by查询返回map类型 1,2 List<Map<String, Object>> getCount();xml: <select id"getCount" resultType"java.util.HashMap">SELECT company_id, ifnull(sum(count_a count_b),0) ctFROM test.com_countWHERE is_del 0 GROUP BY…

天气数据集2-应用RNN做天气预测

二、用循环神经网络做天气(温度)预测 本项目是基于Pytorch的 RNN&GRU模型&#xff0c;用于预测未来温度 数据集: https://mp.weixin.qq.com/s/08BmF4RnnwQ-jX5s_ukDUA 项目代码: https://github.com/disanda/b_code/tree/master/Weather_Prediction RNN 模型本质是用于预…

MySQL(四)查询

1、MySQL限性约束 —非空、唯一(自增)、主外键、检查(MySQL存在但是不能用)。 约束主要完成对数据的校验,保证数据库数据的完整性;如果有相互依赖数据,保证该数据不被删除。 1)常用五类约束 not null :非空约束,指定某列不为空。 unique:唯一约束,指定某列和几列组…

基于springboot的-仓库 管理系统【附:资料➕文档】

前言&#xff1a;我是源码分享交流Coding&#xff0c;专注JavaVue领域&#xff0c;专业提供程序设计开发、源码分享、 技术指导讲解、各类项目免费分享&#xff0c;定制和毕业设计服务&#xff01; 免费获取方式--->>文章末尾处&#xff01; 项目介绍&#xff1a; 管理员…

【VUE】el-table表格 实现滚动到底部加载更多数据

废话不多说&#xff0c;直接上代码 <template></template>部分代码 <!-- 表格 --> <el-tableid"mytable"v-loading"listLoading"highlight-current-rowrow-key"project_id":data"tableData"border:reload"…

java中的三种拷贝方法

在Java编程中&#xff0c;理解深拷贝&#xff08;Deep Copy&#xff09;、浅拷贝&#xff08;Shallow Copy&#xff09;和引用拷贝&#xff08;Reference Copy&#xff09;是非常重要的。这三种拷贝方式涉及对象复制和内存管理。以下是对它们的详细解释&#xff1a; 1. 引用拷…

数字IC后端物理验证PV | TSMC 12nm Calibre Base Layer DRC案例解析

基于TSMC 12nm ARM A55 upf flow后端设计实现训练营将于6月中旬正式开班&#xff01;小班教学&#xff01;目前还有3个名额&#xff0c;招满为止&#xff01;有需要可以私信小编 ic-backend2018报名。吾爱IC社区所有训练营课程均为直播课&#xff01; 这个课程支持升级成双核A…

服务器禁止密码登陆

转载请标明出处&#xff1a;https://blog.csdn.net/donkor_/article/details/139444224 文章目录 一、前言二、编辑sshd_config文件三、重启服务四、总结 一、前言 复杂的密码&#xff0c;登陆服务器的时候&#xff0c;也是很不方便的。并且频繁登陆&#xff0c;暴露给外界&am…

事件总线vueEvent

一个组件结束后要更新另一个组件数据&#xff0c;但是另一个组件和这个组件没有上下级关系 在 Vue 中&#xff0c;非父子组件之间进行通信通常需要使用事件总线或者其他的全局事件管理器。在你的代码片段中&#xff0c;vueEvent 似乎是一个事件总线对象&#xff0c;通过 emit 方…

c++ 里函数选择的优先级:普通函数、模板函数、万能引用,编译器选择哪个执行呢?

看大师写的代码时&#xff0c;除了在类里定义了 copy 构造函数&#xff0c;移动构造函数&#xff0c;还定义了对形参采取万能引用的构造函数&#xff0c;因此有个疑问&#xff0c;这时候的构造函数优先级是什么样的呢&#xff1f;简化逻辑测试一下&#xff0c;如下图&#xff0…

如何实现JavaScript中的寄生组合式继承?

在JavaScript中&#xff0c;寄生组合式继承是一种继承机制&#xff0c;它结合了寄生式继承和组合继承的特点。其核心思想是通过构造函数来继承属性&#xff0c;同时通过原型链来继承方法。以下是实现寄生组合式继承的基本步骤&#xff1a; 首先定义一个辅助函数 inheritProtot…

Pygame:新手指南与入门教程

在游戏开发领域,pygame 是一个广受欢迎的 Python 库,它提供了开发二维游戏的丰富工具和方法。这个库让开发者可以较少地关注底层图形处理细节,更多地专注于游戏逻辑和玩法的实现。本文将详细介绍 pygame,包括其安装过程、基本概念、主要功能和一个简单游戏的开发流程。 一…

【Vue】路由的封装抽离

问题&#xff1a;所有的路由配置都在main.js中合适吗&#xff1f; 目标&#xff1a;将路由模块抽离出来。 好处&#xff1a;拆分模块&#xff0c;利于维护 路径简写&#xff1a; 脚手架环境下 指代src目录&#xff0c;可以用于快速引入组件 完整代码 router/index.js // 但…

探索贷款交易平台的技术架构与创新应用

随着金融科技的快速发展&#xff0c;贷款交易平台作为金融行业的重要组成部分&#xff0c;正扮演着越来越重要的角色。本文将深入探讨贷款交易平台的技术架构和创新应用&#xff0c;从前端设计、后端系统、安全保障和智能化服务等方面进行全面解析&#xff0c;帮助读者更好地了…

【Python报错】已解决AttributeError: list object has no attribute ’shape‘ ( Solved )

解决Python报错&#xff1a;AttributeError: ‘list’ object has no attribute ‘shape’ (Solved) 在Python中&#xff0c;AttributeError表明你试图访问的对象没有你请求的属性或方法。如果你遇到了AttributeError: list object has no attribute shape的错误&#xff0c;这…