主頁 >  其他 > 揭秘Spring依賴注入和SpEL運算式

揭秘Spring依賴注入和SpEL運算式

2023-06-13 07:50:20 其他

摘要:在本文中,我們深入探討了Spring框架中的屬性注入技術,包括setter注入、構造器注入、注解式屬性注入,以及使用SpEL運算式進行屬性注入,

本文分享自華為云社區《Spring高手之路3——揭秘Spring依賴注入和SpEL運算式》,作者:磚業洋__ ,

在本文中,我們深入探討了Spring框架中的屬性注入技術,包括setter注入、構造器注入、注解式屬性注入,以及使用SpEL運算式進行屬性注入,我們通過XML和注解兩種方式,詳細講解了如何進行屬性注入,并給出了完整的代碼示例,無論你是Spring新手,還是有一定經驗的開發者,本文都將幫助你理解并掌握Spring中的屬性注入技術,

1. setter屬性注入

1.1 使用XML進行setter方法注入

我們在前面的文章中已經使用過XML進行setter方法的屬性注入了,下面讓我們再來回顧一下:

<bean id="userSetter" class="com.example.demo.bean.User">
 <property name="username" value=https://www.cnblogs.com/huaweiyun/p/"example-username-setter"/>
 <property name="age" value=https://www.cnblogs.com/huaweiyun/p/"25"/>
</bean>

1.2 使用@Bean注解進行setter方法注入

我們在前面的文章中也學習過如何在bean創建時通過編程方式設定屬性:

@Bean
public User user() {
    User user = new User();
 user.setUsername("example-username-anno-setter");
 user.setAge(25);
 return user;
}

1.3 setter方法注入完整代碼示例

  • 使用XML進行setter方法注入

首先,我們需要創建一個User類,并在其中包含username和age兩個屬性,以及相應的getter、setter方法和構造器,

public class User {
 private String username;
 private Integer age;
 public User() {
 }
 // 為了節省篇幅,getter和setter方法省略......
    @Override
 public String toString() {
 return "User{username='" + username + "', age=" + age + "}";
 }
}

對于XML方式的setter注入和構造器注入,我們需要創建一個組態檔,比如叫applicationContext.xml,

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
 <!-- setter方法注入 -->
 <bean id="userSetter" class="com.example.demo.bean.User">
 <property name="username" value=https://www.cnblogs.com/huaweiyun/p/"example-username-setter"/>
 <property name="age" value=https://www.cnblogs.com/huaweiyun/p/"25"/>
 </bean>
</beans>

然后,我們需要創建一個DemoApplication類,使用ApplicationContext來加載組態檔并獲取Bean:

import com.example.demo.bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User userSetter = (User) context.getBean("userSetter");
 System.out.println(userSetter);
 }
}

運行結果如下:

  • 使用@Bean注解進行setter方法注入

我們需要創建一個配置類,例如叫AppConfig.java:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
    @Bean
 public User userSetter() {
        User user = new User();
 user.setUsername("example-username-anno-setter");
 user.setAge(25);
 return user;
 }
}

使用@Bean注解來定義Bean,每個@Bean方法對應于XML配置中的一個<bean>元素,這個方法的名稱就是Bean的id,方法的回傳值就是Bean的型別

然后修改主程式,這里使用AnnotationConfigApplicationContext來創建Spring的應用背景關系,并加載配置類,Spring會自動從配置類中獲取所有的Bean定義,并創建相應的Bean實體,

package com.example.demo;
import com.example.demo.bean.User;
import com.example.demo.configuration.AppConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        User userSetter = (User) context.getBean("userSetter");
 System.out.println(userSetter);
 }
}

運行結果如下

注意:XML配置方式已經相對陳舊,而且在Spring Boot專案中,主流的做法是使用注解和Java配置方式,對于setter注入,有時會引發回圈依賴的問題,在Spring中,可以使用構造器注入來避免這種情況,這里了解即可,

2. 構造器注入

setter注入是一種在物件被實體化之后(通過呼叫無參構造器創建實體)再通過setter方法注入依賴的方式,構造器注入則是在創建物件實體的時候就通過構造器引數來注入依賴,

為了演示構造器注入,我們需要給User添加一個全引數構造器:

public User(String username, Integer age) {
 this.username = username;
 this.age = age;
}

添加這個構造器后,Java不再提供默認的無參構造器,這會導致我們之前的<bean>標簽創建時失敗,因為它找不到默認的構造器,

2.1 使用XML進行構造器注入

我們可以在<bean>標簽內部宣告一個子標簽:constructor-arg,它用于指定構造器的引數,來進行屬性注入,constructor-arg標簽的撰寫規則如下:

<bean id="userConstructor" class="com.example.demo.bean.User">
 <constructor-arg index="0" value=https://www.cnblogs.com/huaweiyun/p/"example-username-constructor"/>
 <constructor-arg index="1" value=https://www.cnblogs.com/huaweiyun/p/"25"/>
</bean>

index屬性表示建構式引數的位置,它的值是一個非負整數,其中0表示第一個引數,1表示第二個引數,以此類推,雖然value屬性的值總是一個字串,但是Spring會嘗試將它轉換為建構式引數所需的型別,例如建構式的第二個引數是int型別,那么Spring會嘗試將字串"25"轉換為整數25,

使用index屬性來指定建構式引數的位置在大多數情況下是可以的,但是如果建構式的引數數量或者順序發生了改變,就可能會出錯,另外一種更為可靠的方式是使用name屬性來指定引數的名稱,如:

<bean id="userConstructor" class="com.example.demo.bean.User">
 <constructor-arg name="username" value=https://www.cnblogs.com/huaweiyun/p/"example-username-constructor"/>
 <constructor-arg name="age" value=https://www.cnblogs.com/huaweiyun/p/"25"/>
</bean>

這樣無論引數的順序如何,只要引數名稱不變,就不會出錯,

2.2 使用@Bean注解進行構造器屬性注入

在注解驅動的bean注冊中,我們也可以直接使用編程方式賦值:

@Bean
public User user() {
 return new User("example-username-anno-constructor", 25);
}

2.3 構造器注入的完整代碼示例

  • 使用XML進行構造器注入

首先,我們需要創建一個User類,并在其中包含username和age兩個屬性,以及相應的getter、setter方法和構造器,

public class User {
 private String username;
 private Integer age;
 public User() {
 }
 public User(String username, Integer age) {
 this.username = username;
 this.age = age;
 }
 // 為了節省篇幅,getter和setter方法省略......
    @Override
 public String toString() {
 return "User{username='" + username + "', age=" + age + "}";
 }
}

對于XML方式的構造器注入,我們需要創建一個組態檔,比如叫applicationContext.xml,這里保留setter注入方便大家對比

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
 <!-- setter方法注入 -->
 <!-- setter方法注入 -->
<!-- <bean id="userSetter" class="com.example.demo.bean.User">-->
<!-- <property name="username" value=https://www.cnblogs.com/huaweiyun/p/"example-username-setter"/>-->
<!-- <property name="age" value=https://www.cnblogs.com/huaweiyun/p/"25"/>-->
<!-- </bean>-->
 <!-- 構造器注入 -->
 <bean id="userConstructor" class="com.example.demo.bean.User">
 <constructor-arg name="username" value=https://www.cnblogs.com/huaweiyun/p/"example-username-constructor"/>
 <constructor-arg name="age" value=https://www.cnblogs.com/huaweiyun/p/"25"/>
 </bean>
</beans>

然后,我們需要創建一個DemoApplication類,使用ApplicationContext來加載組態檔并獲取Bean:

package com.example.demo;
import com.example.demo.bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//        User userSetter = (User) context.getBean("userSetter");
//        System.out.println(userSetter);
        User userConstructor = (User) context.getBean("userConstructor");
 System.out.println(userConstructor);
 }
}

運行結果如下:

  • 使用@Bean注解進行構造器屬性注入

我們需要創建一個配置類,例如叫AppConfig.java:

import com.example.demo.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
//    @Bean
//    public User userSetter() {
//        User user = new User();
//        user.setUsername("example-username-anno-setter");
//        user.setAge(25);
//        return user;
//    }
    @Bean
 public User userConstructor() {
 return new User("example-username-anno-constructor", 25);
 }
}

同樣,我們需要創建一個DemoApplication類,使用AnnotationConfigApplicationContext來加載配置類并獲取Bean:

import com.example.demo.bean.User;
import com.example.demo.configuration.AppConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//        User userSetter = (User) context.getBean("userSetter");
//        System.out.println(userSetter);
        User userConstructor = (User) context.getBean("userConstructor");
 System.out.println(userConstructor);
 }
}

運行結果:

注意:如果在類中同時使用構造器注入和setter注入,需要注意它們注入的順序:先進行構造器注入,然后是setter注入,

3. 注解式屬性注入

上面我們已經說過注解式的setter和構造器注入,我們又是如何處理那些通過@Component掃描而注冊的bean的屬性的呢?我們來仔細說說這個問題,同時展示如何在xml中進行相同的操作,

3.1 @Value注解式屬性注入的應用

首先,讓我們從最簡單的屬性注入方法:@Value開始,創建一個新的White類,并宣告一些欄位,但是這次我們不會設定setter方法:

@Component
public class White {
    @Value("white-value-annotation")
 private String title;
    @Value("1")
 private Integer rank;
    @Override
 public String toString() {
 return "White{" + "title='" + title + '\'' + ", rank=" + rank + '}';
 }
}

要實作注解式屬性注入,我們可以直接在需要注入的欄位上添加@Value注解:

@Value("white-value-annotation")
private String title;
@Value("1")
private Integer rank;

要注意的是,如果使用 @Value 注解來注入一個不存在的屬性,那么應用程式會在啟動時拋出例外,

然后,我們將通過組件掃描方式將這個White類掃描到IOC容器中,并將其取出并列印:

public class DemoApplication {
 public static void main(String[] args) throws Exception {
 ApplicationContext ctx = new AnnotationConfigApplicationContext(White.class);
        White white = ctx.getBean(White.class);
 System.out.println("Injected value : " + white);
 }
}

運行main方法會看到White的欄位已經成功注入:

Injected value : White{
   title='white-value-annotation', rank=1}

3.2 引入外部組態檔@PropertySource

如果我們需要在Spring中使用properties檔案,我們應該怎么辦呢?Spring考慮到了這一點,并擴展了一個用于匯入外部組態檔的注解:@PropertySource,

  • 創建Bean和組態檔

創建一個新的Blue類,其結構與White類完全相同,然后在專案的resources目錄下創建一個新的blue.properties檔案,用于存盤Blue類的屬性配置:

blue.title=blue-value-properties
blue.rank=2
  • 引入組態檔

使用@PropertySource注解將properties檔案匯入到配置類:

@Configuration
@ComponentScan("com.example")
@PropertySource("classpath:blue.properties")
public class InjectValueConfiguration {
}

這個blue.properties檔案是一個鍵值對的串列,Spring 將這些鍵值對加載到 Environment 中,我們可以通過 @Value 注解或者 Environment 類的方法來獲取這些屬性值,

@Value 注解和 Environment 類都可以用于讀取 Spring 背景關系中的屬性值,這些屬性值可能來自于多個不同的源,包括但不限于:

  • Spring Boot 的默認組態檔(application.properties 或 application.yml),
  • 通過 @PropertySource 注解加載的屬性檔案,
  • 系統環境變數,
  • Java 系統屬性(可以通過 -D 命令列引數設定),

如果你想通過 @Value 注解來獲取屬性值,如下:

@Component
public class BlueConfig {
    @Value("${blue.title}")
 private String title;
    @Value("${blue.rank}")
 private int rank;
 // getters and setters...
}

在 Spring 應用中使用 @PropertySource 注解來加載一個 .properties 檔案時,這個檔案中的所有配置項都會被讀取,并存盤在一個內部的 Map 結構中,這個 Map 的鍵是配置項的名稱,值是配置項的值,Spring 中的一些內置配置項也會被添加到這個 Map 中,

當我們使用 ${...}` 占位符語法來參考一個配置項時,`Spring` 會查找這個 `Map`,取出與占位符名稱相應的配置項的值,例如有一個配置項 `blue.title=blue-value-properties`,我們可以在代碼中使用 `${blue.title} 占位符來參考這個配置項的值,

如果想通過 Environment 類的方法來獲取屬性值,可以像下面這樣做:

@Component
public class SomeComponent {
    @Autowired
 private Environment env;
 public void someMethod() {
        String title = env.getProperty("blue.title");
        int rank = Integer.parseInt(env.getProperty("blue.rank"));
 // ...
 }
}

在上述代碼中,Environment 類的 getProperty 方法用于獲取屬性值,注意,getProperty 方法回傳的是 String,所以如果屬性是非字串型別(如 int),則需要將獲取的屬性值轉換為適當的型別,

注意:@PropertySource 無法加載 YAML 格式的檔案,只能加載 properties 格式的檔案,如果需要加載 YAML 格式的檔案,而且使用的是 Spring Boot框架,那么可以使用@ConfigurationProperties或@Value注解,例如以下的YAML檔案:

application.yml

appTest:
  name: MyApp
  version: 1.0.0

可以使用@ConfigurationProperties來加載這些屬性:

@Configuration
@ConfigurationProperties(prefix = "appTest")
public class AppConfig {
 private String name;
 private String version;
 // getters and setters...
}

@ConfigurationProperties注解主要用于指定配置屬性的前綴,@ConfigurationProperties注解本身并不直接指定組態檔的位置, 而是由Spring Boot的自動配置機制處理的,

這樣,name欄位就會被自動系結到appTest.name配置屬性,version欄位就會被自動系結到appTest.version配置屬性,

默認情況下,Spring Boot會在啟動時自動加載src/main/resources目錄下的application.properties或application.yml檔案,我們可以通過設定spring.config.name和spring.config.location屬性來改變默認的組態檔名或位置,

注意:@ConfigurationProperties注解需要配合@EnableConfigurationProperties注解或@Configuration注解使用,以確保Spring能夠發現并處理這些注解,

或者,你也可以使用@Value注解來加載這些屬性:

@Component
public class AppConfig {
    @Value("${appTest.name}")
 private String name;
    @Value("${appTest.version}")
 private String version;
 // getters and setters...
}
  • Blue類的屬性注入

對于properties型別的屬性,我們這里選擇@Value注解和占位符來注入屬性:

@Value("${blue.title}")
private String title;
@Value("${blue.rank}")
private Integer rank;

如果你熟悉jsp的el運算式,會發現這和它非常相似!

  • 測驗啟動類

修改啟動類,將配置類引入,然后取出并列印Blue:

public static void main(String[] args) throws Exception {
 ApplicationContext ctx = new AnnotationConfigApplicationContext(InjectValueConfiguration.class);
    Blue blue = ctx.getBean(Blue.class);
 System.out.println("Properties value : " + blue);
}

運行main方法會看到控制臺已經成功列印出了組態檔的屬性:

Properties value : Blue{
   title='blue-value-properties', rank=2}

3.3 在XML中引入外部組態檔

在xml中,我們可以和@Value相同的方式使用占位符:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd"
 xmlns:context="http://www.springframework.org/schema/context">
 <!-- 相當于注解中的 @PropertySource("classpath:blue.properties") -->
 <context:property-placeholder location="classpath:blue.properties"/>
 <bean class="com.example.demo.bean.Blue">
 <property name="title" value=https://www.cnblogs.com/huaweiyun/p/"${blue.title}"/>
 <property name="rank" value=https://www.cnblogs.com/huaweiyun/p/"${blue.rank}"/>
 </bean>
</beans>

3.4 注解式屬性注入完整代碼示例

  • @Value注解式屬性注入的應用

創建White類:

package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class White {
    @Value("white-value-annotation")
 private String title;
    @Value("1")
 private Integer rank;
    @Override
 public String toString() {
 return "White{" + "title='" + title + '\'' + ", rank=" + rank + '}';
 }
}

創建啟動類InjectValueAnnotationApplication:

package com.example.demo;
import com.example.demo.bean.White;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
 public static void main(String[] args) throws Exception {
 ApplicationContext ctx = new AnnotationConfigApplicationContext(White.class);
        White white = ctx.getBean(White.class);
 System.out.println("Injected value : " + white);
 }
}

運行結果如下:

  • 引入外部組態檔@PropertySource

創建Blue類和組態檔,沒有setter和getter方法:

package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Blue {
    @Value("${blue.title}")
 private String title;
    @Value("${blue.rank}")
 private Integer rank;
    @Override
 public String toString() {
 return "Blue{" + "title='" + title + '\'' + ", rank=" + rank + '}';
 }
}

resources目錄下的blue.properties檔案:

blue.title=blue-value-properties
blue.rank=2

創建配置類InjectValueConfiguration:

package com.example.demo.configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@ComponentScan("com.example")
@PropertySource("classpath:blue.properties")
public class InjectValueConfiguration {
}

修改啟動類,引入配置類:

package com.example.demo;
import com.example.demo.bean.Blue;
import com.example.demo.configuration.InjectValueConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
 public static void main(String[] args) throws Exception {
 ApplicationContext ctx = new AnnotationConfigApplicationContext(InjectValueConfiguration.class);
        Blue blue = ctx.getBean(Blue.class);
 System.out.println("Properties value : " + blue);
 }
}

運行結果如下:

  • 在xml中引入外部組態檔

在使用XML配置的情況下,我們需要創建一個XML檔案來替代InjectValueConfiguration類,我們可以先注釋掉InjectValueConfiguration類的所有內容

下面是相應的XML檔案內容:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd"
 xmlns:context="http://www.springframework.org/schema/context">
 <!-- 相當于注解中的 @PropertySource("classpath:blue.properties") -->
 <context:property-placeholder location="classpath:blue.properties"/>
 <bean class="com.example.demo.bean.Blue">
 <property name="title" value=https://www.cnblogs.com/huaweiyun/p/"${blue.title}"/>
 <property name="rank" value=https://www.cnblogs.com/huaweiyun/p/"${blue.rank}"/>
 </bean>
</beans>

在這里我們使用了context:property-placeholder標簽來匯入外部的properties檔案,然后使用${...}占位符語法來參考組態檔中的屬性值,這樣無論是選擇用注解方式還是XML方式,都可以方便地在Spring中使用外部組態檔,

這里還需要修改下Blue類,因為通過XML方法注入屬性需要提供相應的setter方法,修改后的Blue類如下:

package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Blue {
    @Value("${blue.title}")
 private String title;
    @Value("${blue.rank}")
 private Integer rank;
 public String getTitle() {
 return title;
 }
 public void setTitle(String title) {
 this.title = title;
 }
 public Integer getRank() {
 return rank;
 }
 public void setRank(Integer rank) {
 this.rank = rank;
 }
    @Override
 public String toString() {
 return "Blue{" + "title='" + title + '\'' + ", rank=" + rank + '}';
 }
}

然后,我們需要修改啟動類,使用XmlApplicationContext代替AnnotationConfigApplicationContext:

package com.example.demo;
import com.example.demo.bean.Blue;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@ComponentScan("com.example")
public class DemoApplication {
 public static void main(String[] args) throws Exception {
 ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:injectValueContext.xml");
        Blue blue = ctx.getBean(Blue.class);
 System.out.println("Properties value : " + blue);
 }
}

運行結果如下:

4. SpEL運算式

當我們談到屬性注入的時候,我們可能會遇到一些復雜的需求,例如我們需要參考另一個Bean的屬性,或者我們需要動態處理某個屬性值,這種需求無法通過使用${}的占位符方式實作,我們需要一個更強大的工具:SpEL運算式,

Spring Expression Language(SpEL)是從Spring框架 3.0開始支持的強大工具,SpEL不僅是Spring框架的重要組成部分,也可以獨立使用,它的功能豐富,包括呼叫屬性值、屬性引數、方法呼叫、陣列存盤以及邏輯計算等,它與開源專案OGNL(Object-Graph Navigation Language)相似,但SpEL是Spring框架推出的,并默認內嵌在Spring框架中,

4.1 使用@Value注解和SpEL運算式實作屬性注入

SpEL的運算式用#{}表示,花括號中就是我們要撰寫的運算式,

我們創建一個Bean,命名為Azure,同樣地,我們宣告屬性name和priority,并提供getter和setter方法以及toString()方法,然后我們使用@Component注解標注它,

使用@Value配合SpEL完成屬性注入,如下:

@Component
public class Azure {
    @Value("#{'spel-for-azure'}")
 private String name;
    @Value("#{10}")
 private Integer priority;
}

我們修改啟動類,從IOC容器中獲取Azure并列印,可以看到屬性被成功注入:

Azure{
   name='spel-for-azure', priority=10}

SpEL的功能遠不止這些,它還可以獲取IOC容器中其他Bean的屬性,讓我們來展示一下,

我們已經注冊了Azure Bean,現在我們再創建一個Bean,命名為Emerald,我們按照上述方法對欄位和方法進行宣告,然后使用@Component注解標注,

我們希望name屬性直接復制Azure的name屬性,而priority屬性則希望比Azure的priority屬性大1,我們可以這樣撰寫:

@Component
public class Emerald {
    @Value("#{'copy of ' + azure.name}")
 private String name;
    @Value("#{azure.priority + 1}")
 private Integer priority;
}

在Spring的SpEL中可以通過bean的名稱訪問到對應的bean,并通過.運算子訪問bean的屬性,在這個例子中,azure就是一個bean的名稱,它對應的bean就是Azure類的實體,所以,azure.name就是訪問Azure類實體的name屬性,

如果你在一個不涉及Spring的環境中使用SpEL,這個特性是不會生效的,這是因為這個特性依賴于Spring的IoC容器,

我們修改啟動類,測驗運行,可以看到Azure的屬性已經成功被復制:

use spel bean property : Emerald{
   name='copy of spel-for-azure', priority=11}

SpEL運算式不僅可以參考物件的屬性,還可以直接參考類的常量,以及呼叫物件的方法,下面我們通過示例進行演示,

我們新建一個Bean,命名為Ivory,我們按照上述方法初始化屬性、toString()方法、注解,

假設我們有一個需求,讓name取azure屬性的前3個字符,priority取Integer的最大值,那么我們可以使用SpEL這樣寫:

@Component
public class Ivory {
    @Value("#{azure.name.substring(0, 3)}")
 private String name;
    @Value("#{T(java.lang.Integer).MAX_VALUE}")
 private Integer priority;
}

注意,直接參考類的屬性,需要在類的全限定名外面使用T()包圍,

我們修改啟動類,測驗運行,可以看到Ivory的屬性已經是處理之后的值:

use spel methods : Ivory{
   name='spe', priority=2147483647}

4.2 在XML中使用SpEL運算式實作屬性注入:

<bean id="ivory" class="com.example.demo.bean.Ivory">
 <property name="name" value=https://www.cnblogs.com/huaweiyun/p/"#{azure.name.substring(0, 3)}" />
 <property name="priority" value=https://www.cnblogs.com/huaweiyun/p/"#{T(java.lang.Integer).MAX_VALUE}" />
</bean>

學習SpEL運算式不需要花費大量的精力,掌味訓礎的使用方法即可,

4.3 SpEL運算式屬性注入完整代碼示例

  • 使用@Value注解和SpEL運算式實作屬性注入

創建三個SpEL運算式屬性注入的Bean:Azure.java、Emerald.java和Ivory.java,

Azure.java:

package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Azure {
    @Value("#{'spel-for-azure'}")
 private String name;
    @Value("#{10}")
 private Integer priority;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Integer getPriority() {
 return priority;
 }
 public void setPriority(Integer priority) {
 this.priority = priority;
 }
    @Override
 public String toString() {
 return "Azure{" +
 "name='" + name + '\'' +
 ", priority=" + priority +
 '}';
 }
}

Emerald.java:

package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Emerald {
    @Value("#{'copy of ' + azure.name}")
 private String name;
    @Value("#{azure.priority + 1}")
 private Integer priority;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Integer getPriority() {
 return priority;
 }
 public void setPriority(Integer priority) {
 this.priority = priority;
 }
    @Override
 public String toString() {
 return "Emerald{" +
 "name='" + name + '\'' +
 ", priority=" + priority +
 '}';
 }
}

Ivory.java:

package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Ivory {
    @Value("#{azure.name.substring(0, 3)}")
 private String name;
    @Value("#{T(java.lang.Integer).MAX_VALUE}")
 private Integer priority;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Integer getPriority() {
 return priority;
 }
 public void setPriority(Integer priority) {
 this.priority = priority;
 }
    @Override
 public String toString() {
 return "Ivory{" +
 "name='" + name + '\'' +
 ", priority=" + priority +
 '}';
 }
}

MyBean.java

@Component
public class MyBean {
    @Autowired
 private Azure azure;
    @Autowired
 private Emerald emerald;
    @Autowired
 private Ivory ivory;
 public void init() {
 System.out.println(azure);
 System.out.println(emerald);
 System.out.println(ivory);
 }
}

MyBean是一個用于展示如何在Spring中通過SpEL運算式來注入屬性的類,它聚合了三個物件Azure, Emerald和Ivory,并通過Spring的依賴注入機制將這三個物件注入到了MyBean類的實體中

主程式DemoApplication

@SpringBootApplication
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
 MyBean myBean = applicationContext.getBean(MyBean.class);
 myBean.init();
 }
}

運行結果:

  • 在XML中使用SpEL運算式實作屬性注入

對于XML配置,Spring還支持在bean定義中使用SpEL,

首先,需要創建一個Spring XML組態檔,我們將其命名為app-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
 <context:component-scan base-package="com.example" />
 <bean id="azure" class="com.example.demo.bean.Azure">
 <property name="name" value=https://www.cnblogs.com/huaweiyun/p/"#{
 'spel-for-azure'}" />
 <property name="priority" value=https://www.cnblogs.com/huaweiyun/p/"#{10}" />
 </bean>
 <bean id="emerald" class="com.example.demo.bean.Emerald">
 <property name="name" value=https://www.cnblogs.com/huaweiyun/p/"#{
 'copy of ' + azure.name}" />
 <property name="priority" value=https://www.cnblogs.com/huaweiyun/p/"#{azure.priority + 1}" />
 </bean>
 <bean id="ivory" class="com.example.demo.bean.Ivory">
 <property name="name" value=https://www.cnblogs.com/huaweiyun/p/"#{azure.name.substring(0, 3)}" />
 <property name="priority" value=https://www.cnblogs.com/huaweiyun/p/"#{T(java.lang.Integer).MAX_VALUE}" />
 </bean>
</beans>

注意:在XML中使用SpEL需要使用#{},而不是${},

然后修改這3個Bean,如果是使用XML來配置Spring的Bean的話,那么在Java類中就不需要使用@Component注解了,因為XML組態檔已經明確地告訴Spring這些類是Spring Bean,

同樣的,如果在XML檔案中定義了Bean的屬性值,那么在Java類中就不需要使用@Value注解來注入這些值了,因為XML組態檔已經明確地為這些屬性賦了值,

Azure.java

package com.example.demo.bean;
public class Azure {
 private String name;
 private Integer priority;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Integer getPriority() {
 return priority;
 }
 public void setPriority(Integer priority) {
 this.priority = priority;
 }
    @Override
 public String toString() {
 return "Azure{" +
 "name='" + name + '\'' +
 ", priority=" + priority +
 '}';
 }
}

Emerald.java

package com.example.demo.bean;
public class Emerald {
 private String name;
 private Integer priority;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Integer getPriority() {
 return priority;
 }
 public void setPriority(Integer priority) {
 this.priority = priority;
 }
    @Override
 public String toString() {
 return "Emerald{" +
 "name='" + name + '\'' +
 ", priority=" + priority +
 '}';
 }
}

Ivory.java

package com.example.demo.bean;
public class Ivory {
 private String name;
 private Integer priority;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Integer getPriority() {
 return priority;
 }
 public void setPriority(Integer priority) {
 this.priority = priority;
 }
    @Override
 public String toString() {
 return "Ivory{" +
 "name='" + name + '\'' +
 ", priority=" + priority +
 '}';
 }
}

然后需要在主程式中匯入這個XML組態檔,這可以通過在主程式中添加@ImportResource注解實作:

package com.example.demo;
import com.example.demo.bean.MyBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
@ImportResource("classpath:app-config.xml")
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
 MyBean myBean = applicationContext.getBean(MyBean.class);
 myBean.init();
 }
}

這樣就可以在Spring的XML組態檔中使用SpEL了,

運行結果如下:

 

點擊關注,第一時間了解華為云新鮮技術~

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/554970.html

標籤:其他

上一篇:如何成為一名全職創作者——程式員篇

下一篇:返回列表

標籤雲
其他(160838) Python(38222) JavaScript(25492) Java(18225) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7247) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5874) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4589) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2435) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) 功能(1967) HtmlCss(1962) Web開發(1951) C++(1933) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 揭秘Spring依賴注入和SpEL運算式

    摘要:在本文中,我們深入探討了Spring框架中的屬性注入技術,包括setter注入、構造器注入、注解式屬性注入,以及使用SpEL運算式進行屬性注入。 本文分享自華為云社區《Spring高手之路3——揭秘Spring依賴注入和SpEL運算式》,作者:磚業洋__ 。 在本文中,我們深入探討了Sprin ......

    uj5u.com 2023-06-13 07:50:20 more
  • 如何成為一名全職創作者——程式員篇

    哈嘍大家好,我是咸魚 今天跟大家分享一篇文章,這篇文章的作者 **Gergely Orosz** 是一名程式員,他從 Uber 辭職以后,就當起了全職創作者 他通過寫文章、賣課程、做視頻等謀生,今天這篇文章是他對這種商業模式的思考,我把它主要部分翻譯了出來(想要看全文的原文鏈接在文末) **譯文如下 ......

    uj5u.com 2023-06-13 07:49:48 more
  • WMIC遠程執行命令橫向移動

    # WMIC遠程執行命令橫向移動 [TOC] ## 一、wmic介紹 wmic擴展wmi(Windows Management Instrumentation,Windows管理工具) ,提供了從命令列介面和批處理腳本執行系統管理的支持。 wmic命令需要本地管理員或域管理員才可以進行正常使用,普通 ......

    uj5u.com 2023-06-13 07:49:23 more
  • 決議汽車APP面臨的18種攻擊風險

    近日,頂象發布《車企App安全研究白皮書》。該白皮書總結了目前汽車公司App所面臨的主要技術威脅和合規風險,詳細分析了這些風險產生的原因,并提供了相應的安全解決方案。 現在,自有App已成為各汽車品牌的標配。這些汽車廠商的App不僅可以幫助用戶實作遠程開啟空調、門鎖、啟動車輛等常用功能,還提供購車、 ......

    uj5u.com 2023-06-13 07:49:15 more
  • 讀改變未來的九大演算法筆記10_讀后總結與感想

    ![](https://img2023.cnblogs.com/blog/3076680/202306/3076680-20230610222138666-254271297.png) # 1. 基本資訊 改變未來的九大演算法 [美]約翰·麥考密克(John MacCormick ) 中信出版集團,2 ......

    uj5u.com 2023-06-12 08:00:18 more
  • debug.exe的使用

    debug.exe 是 Windows 作業系統自帶的一個命令列除錯工具,用于在 DOS 環境下進行匯編語言級別的除錯操作。它可以讓程式員以來自底層的方式逐步執行代碼并檢查每個暫存器中的值和記憶體地址。 ......

    uj5u.com 2023-06-12 08:00:10 more
  • ms14-068漏洞

    # ms14-068漏洞 [TOC] ## 一、ms14-068漏洞介紹 ms14-068漏洞主要通過偽造域管的TGT,將普通用戶權限提權為域管權限,以此來控制域控。只要服務器未打ms14-068補丁(KB3011780),在server 2000以上的域控服務器中,都可進行利用。 ## 二、ms1 ......

    uj5u.com 2023-06-12 08:00:06 more
  • ChatGPT 五個寫論文的神技巧,讓你的老師對你刮目相看!

    ChatGPT這款AI工具在推出兩個月內就累積了超過1億用戶。我們向您展示如何使用ChatGPT進行寫作輔助,以及其他一些有用的寫作技巧。 ......

    uj5u.com 2023-06-12 07:59:52 more
  • 如何看待深圳城中村房子被統租?

    天涯神帖:kkndme房產觀 原作者:kkndme 原文PDF:https://pan.quark.cn/s/fc259fdda34b 天涯kk真的是大神,預言一項項成真了。(據說陳雨露是kk的老師) 不知道知乎上多少人看過十幾年前天涯論壇上kk的房地產貼…基本都實作了。早期將大量民營kfs清除出市 ......

    uj5u.com 2023-06-12 07:59:42 more
  • SYCTF2023 WEB writeup

    ## CarelessPy 一進來就是個任意檔案下載功能,不過做了些限制,這題從頭到尾都在罵雜魚。。。(雖然我確實是(bushi) 查看頁面源代碼,給了個`/eval /login` 兩個路由,/eval是個目錄遍歷,/login嘗試登錄無果,有session,應該需要偽造session,利用/ev ......

    uj5u.com 2023-06-12 07:59:31 more