SpringBoot SpringEL表達式的使用

一、SpringEL-基礎介紹

什麼是SpringEL(SpEL)?

  • Spring3中引入瞭Spring表達式語言—SpringEL,SpEL是一種強大,簡潔的裝配Bean的方式
  • SpringEL可以通過運行期間執行的表達式將值裝配到我們的屬性或構造函數當中
  • SpringEL可以調用JDK中提供的靜態常量,獲取外部Properties文件中的的配置

為什麼要使用SpringEL?

  • 平常通過配置文件或Annotaton註入的Bean,其實都可以稱為靜態性註入
  • 如Bean A中有變量A,它的值需要根據Bean B的B變量為參考,在這場景下靜態註入就對這樣的處理顯得非常無力
  • 而Spring3增加的SpringEL就可以完全滿足這種需求,而且還可以對不同Bean的字段進行計算再進行賦值,功能非常強大

如何使用SpringEL?

  • SpringEL從名字來看就能看出和EL是有點關系的,SpringEL的使用和EL表達式的使用非常相似
  • EL表達式在JSP頁面更方便的獲取後臺中的值,而SpringEL就是為瞭更方便獲取Spring容器中的Bean的值
  • EL使用${},而SpringEL使用#{}進行表達式的聲明

兩者主要區別

  • $是去找外部配置的參數,將值賦過來
  • #是SpEL表達式,去尋找對應變量的內容
  • 也可以直接使用@value(“常量”)註入不使用EL,這樣寫法與直接賦值等價

如果是在Spring中使用可以使用**@PropertySource(“classpath:my.properties”)**加載對應配置文件

二、EL表達式-基礎使用

# 配置文件
com:
  codecoord:
    el:
      num: 1001
      name: el
      language:
        - java
        - spring
        - mysql
        - linux
      # 逗號分隔可以註入列表
      language02: java,spring,mysql,linux

使用EL註入簡單值

/**
 * 註入簡單值,直接註入不使用EL,EL不支持直接指定常量
 * 直接在EL中指定的常量會當做配置處理,和直接賦值等價
 */
@Value("1432516744")
private Integer no;

註入配置文件屬性值

/**
 * 註入整型屬性值
 */
@Value("${com.codecoord.el.num}")
private Integer num;
/**
 * 註入字符屬性值
 */
@Value("${com.codecoord.el.name}")
private String name;

註入默認值

/**
 * 註入字符不存在屬性值並指定默認值,默認值使用過冒號分隔 :
 * 註入常量其實就可以指定一個不存在的配置然後使用默認值,此處skill的值為java
 */
@Value("${com.codecoord.el.skill:java}")
private String skill;

註入列表

  • 不支持直接配置文件中數組語法格式註入列表
  • 可以識別使用逗號,分隔的配置,spring默認以,分隔
// 錯誤寫法:不支持直接註入yml列表格式語法列表
@Value("${com.codecoord.el.language}")
private List<String> listLanguage;
@Value("${com.codecoord.el.language}")
private String[] strLanguage;
/**
 * 支持,分隔的註入列表
 */
@Value("${com.codecoord.el.language02}")
private List<String> listLanguage02;
@Value("${com.codecoord.el.language02}")
private String[] strLanguage02;

完整參考如下

配置文件

server:
  port: 8888
com:
  codecoord:
    el:
      num: 1001
      name: el
      language:
        - java
        - spring
        - mysql
        - linux
      # 逗號分隔可以註入列表
      language02: java,spring,mysql,linux

屬性配置類

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;

@Data
@Component
public class ElConfig {
    /**
     * 註入簡單值,直接註入不使用EL,EL不支持直接指定常量
     * 直接在EL中指定的常量會當做配置處理,和直接賦值等價
     */
    @Value("1432516744")
    private Integer no;
    /**
     * 註入整型屬性值
     */
    @Value("${com.codecoord.el.num}")
    private Integer num;
    /**
     * 註入字符屬性值
     */
    @Value("${com.codecoord.el.name}")
    private String name;
    /**
     * 註入字符不存在屬性值並指定默認值,默認值使用過冒號分隔 :
     * 註入常量其實就可以指定一個不存在的配置然後使用默認值,此處skill的值為java
     */
    @Value("${com.codecoord.el.skill:java}")
    private String skill;
    /// 不支持直接註入列表
    /*@Value("${com.codecoord.el.language}")
    private List<String> listLanguage;
    @Value("${com.codecoord.el.language}")
    private String[] strLanguage;*/
    /**
     * 支持,分隔的註入列表
     */
    @Value("${com.codecoord.el.language02}")
    private List<String> listLanguage02;
    @Value("${com.codecoord.el.language02}")
    private String[] strLanguage02;
}

向controller中註入配置類,然後訪問接口測試結果如下

{
 "no": 1432516744,
 "num": 1001,
 "name": "el",
 "skill": "java",
 "listLanguage02": [
  "java",
  "spring",
  "mysql",
  "linux"
 ],
 "strLanguage02": [
  "java",
  "spring",
  "mysql",
  "linux"
 ]
}

三、SpringEL-基礎使用

1、使用SpEL註入簡單值和普通EL註入使用基本一致
2、SpEl註入map

  • 配置文件中需要使用雙引號括起來,否則將會註入失敗,key為單引號
# SpEl
spEl:
  mapInject: "{'name': 'SpEl', 'website': 'http://www.codeocord.com'}"

java類中先使用${spEl.mapInject}註入字符串值,#{}會解析字符串的值轉為map

@Value("#{${spEl.mapInject}}")
private Map<String, String> mapInject;

3、SpEl註入list

  • 除瞭可以通過EL註入listI外,也可以使用#{${}.split(‘分隔符’)}的方式註入List
  • 配置文件中例如使用#分隔
spEl:
  listInject: "44#11#99#100"

java類中先使用${spEl.listInject}註入字符串值,內容使用單引號括起來,然後對字符串使用split方法分隔
提示:避免為空情況,可以給一個默認值空串

@Value("#{'${spEl.listInject:}'.split('#')}")
 private List<String> listInject;

4、動態註入

上述註入都是靜態註入,SpEl支持從Spring容器中註入信息,稱為動態註入。動態註入類如下 

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
@Data
public class SpElConstant {
    private String name = "SpElConstant-name";
    private String nickname = "tianxin";
    private int num = 100;
    private List<String> product = new ArrayList<String>() {{
        add("huaweiMate30Pro");
        add("xiaomi10x5g");
    }};
    private Map<String, String> productMap = new HashMap<String, String>() {{
        put("huaweiMate30Pro", "5999");
        put("xiaomi10x5g", "4999");
    }};
    private List<City> cityList = new ArrayList<City>() {{
        add(new City("深圳", 1000L));
        add(new City("杭州", 2000L));
        add(new City("貴陽", 900L));
    }};

    public String showProperty() {
        return "showProperty-無參數";
    }

    public String showProperty(String name) {
        return "showProperty-" + name;
    }

    @Data
    @AllArgsConstructor
    static class City {
        private String name;
        private long population;
    }
}

SpEl支持和不支持操作

  • 支持動態註入實例,類似於對象自動註入
  • SPL不支持直接註入配置文件中的配置
  • 支持調用靜態和實例方法
    • 靜態方法:@Value(“#{T(package.ClassName).ConstFieldName”)
  • 支持調用靜態類或常量
  • 支持運算符運算
  • 支持操作集合
  • 支持查詢篩選集合和投影

註入完整操作如下

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Data
@Component
public class SpElConfig {
    /// 不支持直接註入配置文件值
    /*@Value("#{com.codecoord.el.num}")
    private Integer num;*/
    /**
     * 對象註入
     */
    @Value("#{spElConstant}")
    private SpElConstant spElConstant;
    /**
     * 註入ID為spElConstant Bean中的STR常量/變量
     */
    @Value("#{spElConstant.name}")
    private String name;
    /**
     * 調用無參方法
     */
    @Value("#{spElConstant.showProperty()}")
    private String method1;
    /**
     * 有參接收字符串的方法
     */
    @Value("#{spElConstant.showProperty('Hell SpringEL')}")
    private String method2;
    /**
     * 方法返回的String為大寫
     */
    @Value("#{spElConstant.showProperty().toUpperCase()}")
    private String method3;
    /**
     * 若使用method3這種方式,若果showProperty返回為null
     * 將會拋出NullPointerException,可以使用以下方式避免
     * 使用?.符號代表若然左邊的值為null,將不執行右邊方法
     */
    @Value("#{spElConstant.showProperty()?.toUpperCase()}")
    private String method4;
    /**
     * 註入math常量
     */
    @Value("#{T(java.lang.Math).PI}")
    private double pi;
    /**
     * 用random方法獲取返回值
     */
    @Value("#{T(java.lang.Math).random()}")
    private double random;
    /**
     * 獲取文件路徑符號
     */
    @Value("#{T(java.io.File).separator}")
    private String separator;
    /**
     * 拼接字符串
     */
    @Value("#{spElConstant.nickname + ' ' + spElConstant.name}")
    private String concatString;
    /**
     * 對數字類型進行運算,spElConstant擁有num屬性
     */
    @Value("#{3 * T(java.lang.Math).PI + spElConstant.num}")
    private double operation;
    /**
     * 進行邏輯運算
     */
    @Value("#{spElConstant.num > 100 and spElConstant.num <= 200}")
    private boolean logicOperation;
    /**
     * 進行或非邏輯操作
     */
    @Value("#{not (spElConstant.num == 100) or spElConstant.num <= 200}")
    private boolean logicOperation2;
    /**
     * 使用三元運算符
     */
    @Value("#{spElConstant.num > 100 ? spElConstant.num : spElConstant.num + 100}")
    private Integer logicOperation3;
    /**
     * 獲取下標為0的元素
     */
    @Value("#{spElConstant.product[0]}")
    private String str;
    /**
     * 獲取下標為0元素的大寫形式
     */
    @Value("#{spElConstant.product[0]?.toUpperCase()}")
    private String upperStr;
    /**
     * 獲取map中key為hello的value
     */
    @Value("#{spElConstant.productMap['hello']}")
    private String mapValue;
    /**
     * 根據product下標為0元素作為key獲取testMap的value
     */
    @Value("#{spElConstant.productMap[spElConstant.product[0]]}")
    private String mapStrByproduct;
    /**
     * 註入人口大於等於1000人口的城市
     */
    @Value("#{spElConstant.cityList.?[population >= 1000]}")
    private List<SpElConstant.City> cityList;
    /**
     * 註入人口等於900人口的城市
     */
    @Value("#{spElConstant.cityList.?[population == 900]}")
    private SpElConstant.City city;
    /**
     * 註入人口大於等於1000人口的城市,且隻保留城市名稱
     */
    @Value("#{spElConstant.cityList.?[population >= 1000].![name]}")
    private List<String> cityName;
}

註入結果

{
 "spElConstant": {
  "name": "SpElConstant-name",
  "nickname": "tianxin",
  "num": 100,
  "product": [
   "huaweiMate30Pro",
   "xiaomi10x5g"
  ],
  "productMap": {
   "xiaomi10x5g": "4999",
   "huaweiMate30Pro": "5999"
  },
  "cityList": [
   {
    "name": "深圳",
    "population": 1000
   },
   {
    "name": "杭州",
    "population": 2000
   },
   {
    "name": "貴陽",
    "population": 900
   }
  ]
 },
 "name": "SpElConstant-name",
 "method1": "showProperty-無參數",
 "method2": "showProperty-Hell SpringEL",
 "method3": "SHOWPROPERTY-無參數",
 "method4": "SHOWPROPERTY-無參數",
 "pi": 3.141592653589793,
 "random": 0.19997238292235787,
 "separator": "\\",
 "concatString": "tianxin SpElConstant-name",
 "operation": 109.42477796076938,
 "logicOperation": false,
 "logicOperation2": true,
 "logicOperation3": 200,
 "str": "huaweiMate30Pro",
 "upperStr": "HUAWEIMATE30PRO",
 "mapValue": null,
 "mapStrByproduct": "5999",
 "cityList": [
  {
   "name": "深圳",
   "population": 1000
  },
  {
   "name": "杭州",
   "population": 2000
  }
 ],
 "city": {
  "name": "貴陽",
  "population": 900
 },
 "cityName": [
  "深圳",
  "杭州"
 ]
}

Spring操作外部Properties文件

<!-- 首先通過applicaContext.xml中<util:properties>增加properties文件 -->
<!-- 註意需要引入Spring的util schemea命名空間和註意id屬性,id屬性將在SpringEL中使用 -->
<util:properties id="db" location="classpath:application.properties"/>

public class TestSpringEL {
 // 註意db為xml文件中聲明的id
 @Value("#{db['jdbc.url']}")
 private String propertiesValue;
}

SpringEL在使用時僅僅是一個字符串,不易於排錯與測試,也沒有IDE檢查我們的語法,當出現錯誤時較難檢測,復雜的表達式不建議通過SpringEL方式註入。在非必要情況下,不推薦使用SpEl的復雜註入,清晰可讀的代碼更為重要且有利於排查問題

四、屬性自動註入

上述都是通過指定字段進行註入,可以通過@ConfigurationProperties指定前綴進行自動註入

org.springframework.boot.context.properties.ConfigurationProperties

配置類

user:
  id: ${random.uuid}
  name: autowire
  address: unknown
  website: www.codecoord.com
  age: ${random.int}

自動屬性註入類

  • 通過prefix指定前端為user,然後將會把user.後的類型按照名稱進行註入
  • 註意必須要提供setter方法
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "user")
@Data
public class UserConfig {
    private String id;
    private String name;
    private String address;
    private String website;
    private Integer age;
}

可以通過@EnableConfigurationProperties(value = UserConfig.class)將UserConfig再次強制註入,問題出現在如果UserConfig為第三方jar包內的配置類,則可能出現屬性沒有註入情況,所以可以指定註入

到此這篇關於SpringBoot SpringEL表達式的使用的文章就介紹到這瞭,更多相關SpringEL表達式內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: