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.pswp.cn/web/23611.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/23611.shtml
英文地址,請注明出處:http://en.pswp.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;這…