当前位置:

首页 > 编程开发 > SpringBoot SpringEL表达式怎么使用

SpringBoot SpringEL表达式怎么使用

一、SpringEL-基础介绍什么是SpringEL(SpEL)?Spring3中引入了Spring表达式语言―SpringEL,SpEL是一种强大,简洁的装配Bean的方式SpringEL可以通过运行期间执行的表达式将值装配到我们的属性或构造函数当中SpringEL可以调用JDK中提供的静态常量,获取外部Properties文件中的的配置为什么要使用SpringEL?平常通过配置文件或Annotaton注入的Bean,其实都可以称为静态性注入如BeanA中有变量A,它的值需要根据BeanB的B变量为参考

一、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 listLanguage;
@Value("${com.codecoord.el.language}")
private String[] strLanguage;
/**
 * 支持,分隔的注入列表
 */
@Value("${com.codecoord.el.language02}")
private List 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 listLanguage;
    @Value("${com.codecoord.el.language}")
    private String[] strLanguage;*/
    /**
     * 支持,分隔的注入列表
     */
    @Value("${com.codecoord.el.language02}")
    private List 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 mapInject;

3、SpEl注入list

  • 除了可以通过EL注入listI外,也可以使用#{${}.split("分隔符")}的方式注入List

  • 配置文件中例如使用#分隔

spEl:
  listInject: "44#11#99#100"

java类中先使用${spEl.listInject}注入字符串值,内容使用单引号括起来,然后对字符串使用split方法分隔
提示:避免为空情况,可以给一个默认值空串

@Value("#{"${spEl.listInject:}".split("#")}")
 private List 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 product = new ArrayList() {{
        add("huaweiMate30Pro");
        add("xiaomi10x5g");
    }};
    private Map productMap = new HashMap() {{
        put("huaweiMate30Pro", "5999");
        put("xiaomi10x5g", "4999");
    }};
    private List cityList = new ArrayList() {{
        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 cityList;
    /**
     * 注入人口等于900人口的城市
     */
    @Value("#{spElConstant.cityList.?[population == 900]}")
    private SpElConstant.City city;
    /**
     * 注入人口大于等于1000人口的城市,且只保留城市名称
     */
    @Value("#{spElConstant.cityList.?[population >= 1000].![name]}")
    private List 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文件



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包内的配置类,则可能出现属性没有注入情况,所以可以指定注入

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
C++动态数组初始化怎么写?常用语句与代码示例
C++动态数组初始化怎么写?常用语句与代码示例

深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。