【Spring原理探索】从零开始教你SpringEL表达式使用和功能分析讲解指南(上篇)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 【Spring原理探索】从零开始教你SpringEL表达式使用和功能分析讲解指南(上篇)
  • Spring EL表达式语言,这种语言jsp中学到的el,但是在整个spring之中其表达式语言要更加的复杂,而且支持度更加的广泛,最重要的是他可以进行方法的调用,对象的实例化,集合操作等等,但是唯一的难点就是:代码太复杂了,表达式太复杂了。


  • 深刻领会,spring中针对于字符串的改进,程序员使用字符串开发,绝对要比使用那些类简单,所以在spring里面无时无刻提供的就是字符串的加强。




使用案例


字符串的截取


public class TestStringSub{
     public static void main(String [] args){
    String str = "hello world".subString(5,6);
    System.out.println(str);
   }
}
复制代码

整个spring表达式操作之中可以将一个完全的字符串变为了一个可以用于程序执行的语句,这系系列的执行语句需要有一系列的支持类完成,但是至少我可以发现字符串的功能又一次被加强了。



字符串的截取(SpringEL表达式)

public class TestStringSubExpression{
     public static void main(String [] args){
    String expression = "\"hello world\".subString(5,6)";
      ExpressionParser expressionParser = new SpelExpressionParser();// 指定spelExpressionParser解析器实现类
    Expression expression = parser.parseExpression(expression);//解析表达式 
    EvaluationContext context = new StandardEvaluationContext(expression);//设置对象模型基础。
    System.out.println(expression.getValue(evaluationContext);
   }
}
复制代码




封装一个工具方法:


专门解析传入对象的指定属性数值,这里做了下处理,如果获取失败,则直接返回当前传入的属性名称,方便大家做静态解析和动态解析,比如你传入一个8,因为8的这个属性肯定会不对,所以最后会返回一个8。

private static ExpressionParser expressionParser = new SpelExpressionParser();
public static Object getValueByExpression(Object targetObject,String propertyName){
        try {
            EvaluationContext evaluationContext = new StandardEvaluationContext();
            evaluationContext.setVariable("model",targetObject);
            Expression expression =expressionParser.parseExpression(String.format("#{#model.%s}",propertyName),new TemplateParserContext());
            return expression.getValue(evaluationContext,Object.class);
        } catch (ParseException e) {
            log.warn("ParseException analysis the parameter is {}",propertyName);
        } catch (EvaluationException e) {
            log.warn("EvaluationException analysis the parameter is {}",propertyName);
        }
        return propertyName;
    }
复制代码



表达式解析器

org.springframework.expression.expressionparser
复制代码



主要负责根据给定的表达式字符串内容对解析器进行处理.

解析器处理类

org.springframework.expression.spel.standard.spelexpressionparsr
复制代码



Expressionparser本身只是一个操作的标准,但是它对应的处理类必须单独设置,本次使用的是spel的标准处理

表达式

org.springframework.expression.Expression
复制代码



将字符串根据指定的解析器进行解析,而后使用这个生成表达式:

设置表达式的一些属性信息:

org.springframework.expression.evaluationcontext
复制代码



因为表达式操作之中可能会存在有某些占位符需要进行处理

参数动态化传入+解析操作


public class TestStringSubExpression{
     public static void main(String [] args){
    String expression = "\"hello world\".subString(#start,#end)";
      ExpressionParser expressionParser = new SpelExpressionParser();// 指定spelExpressionParser解析器实现类
    Expression expression = parser.parseExpression(expression);//解析表达式 
    EvaluationContext context = new StandardEvaluationContext(expression);//设置对象模型基础。
      context.setVariable("start",1);
      context.setVariable("end",2);
    System.out.println(expression.getValue(evaluationContext);
   }
}
复制代码



套用模板方法解析器


import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Test {
    public static void main(String[] args) {
        //测试SpringEL解析器
        String template = "#{#name},早上好";//设置文字模板,其中#{}表示表达式的起止,#user是表达式字符串,表示引用一个变量。
        ExpressionParser paser = new SpelExpressionParser();//创建表达式解析器
        //通过evaluationContext.setVariable可以在上下文中设定变量。
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("name","Alex");
        //解析表达式,如果表达式是一个模板表达式,需要为解析传入模板解析器上下文。
        Expression expression = paser.parseExpression(template,new TemplateParserContext());
        //使用Expression.getValue()获取表达式的值,这里传入了Evalution上下文,第二个参数是类型参数,表示返回值的类型。
        System.out.println(expression.getValue(context,String.class));
    }
}
复制代码



表达式的处理原理


除了编写字符串之外还可以编写数字.甚至各种字符串的数据. 一定要首先对其一个判断,判断表达式应该由那些组成,而后要拆分组成的内容,最后要进行字符串的相关数据类型的转换,从而得出最终的计算结果.

image.png



  1. 首先明确的给出表达式,例如1+2
  2. 随后需要准备出spel的表达式解析器,而进行解析的时候要按照如下的步骤.
  • 使用一个专门的断词器,将给定的表达式字符串拆分为spring可以认可的数据格式.
  • 随后要根据断词器的处理生成抽象语法树
  1. 将已经处理后的表达式定义到一个专门的表达式对象之中等待进行最终的结果计算
  2. 考虑到表达式里面可能会存在部分的占位变量的内容,所以在进行表达式计算之前,需要设置一个表达式上下文对象进行占位变量内容的处理.
  3. 最后设置了变量内容,并且利用表达式对象就可以计算出表达式最终所执行的结果




Spring自动的进行了操作的隐藏,用起来很方便


自定义分隔符


任何的表达式其组成之中一定会包含相应的边界形式,例如:在jsp中的el里面使用${表达式},在spring里面,如果用户有需要也可以定义我们的边界.首先观察解析表达式的操作类:Expressionparser


就是用户自己设计边界符的:


是否使用模板
boolean isTemplate();
复制代码



边界开始
String getExpressionPrefix();
复制代码



边界结束
String getExpressionSubffix(); 
复制代码



使用匿名内部类的方式完成.可以在这步定义表达式的边界.定义的表达式在计算的过程中都会自动的忽略掉,大部分我们不会用到自定义边界。

Expression parser = new SpelExpressionParser().parseExpression(str,new ParserContext(){
      public boolean isTemplate() {
            return true;
        }
        public String getExpressionPrefix() {
            return "[";
        }
        public String getExpressionSuffix() {
            return "]";
        }
});
// 默认的ParserContext
public interface ParserContext {
    ParserContext TEMPLATE_EXPRESSION = new ParserContext() {
        public boolean isTemplate() {
            return true;
        }
        public String getExpressionPrefix() {
            return "#{";
        }
        public String getExpressionSuffix() {
            return "}";
        }
    };
    boolean isTemplate();
    String getExpressionPrefix();
    String getExpressionSuffix();
}
复制代码




基本表达式


字面表达式


  • 数值型
  • 布尔型
  • 字符串
  • null就是null



数学表达式


  • 四则运算
Expression exp = parser.parseExpression("1+2+4+5/2");
复制代码


  • 求模
Expression exp = parser.parseExpression("1%2");
Expression exp = parser.parseExpression("1 MOD 2");
复制代码


  • 幂运算
Expression exp = parser.parseExpression("1 ^ 2");
复制代码


  • 除法
Expression exp = parser.parseExpression("1 / 2");
复制代码


关系表达式


  • 等于
Expression exp = parser.parseExpression("1 == 2");
Expression exp = parser.parseExpression("1 EQ 2");
复制代码


  • 不等于
Expression exp = parser.parseExpression("1 != 2");
Expression exp = parser.parseExpression("1 NE 2");
复制代码
  • 大于
Expression exp = parser.parseExpression("1 > 2");
Expression exp = parser.parseExpression("1 GT 2");
复制代码
  • 大于等于
Expression exp = parser.parseExpression("1 >= 2");
Expression exp = parser.parseExpression("1 GE 2");
复制代码
  • 区间
Expression exp = parser.parseExpression("1  BETWEEN {5,20}");
复制代码


其他的以此类推就不过多赘述了!





逻辑表达式


  • 与操作
Expression exp = parser.parseExpression("1 = 1  AND 2 = 2");
复制代码


  • 或操作
Expression exp = parser.parseExpression("1 = 1  or 2 = 2");
复制代码


  • 三目运算符
Expression exp = parser.parseExpression("1 > 1? 1:2);
复制代码

Spring中#{}与${}的区别


#{}是SrpingEl表达式的语法规则.

例如:#{bean.属性?:默认值},注意bean.属性必须是要存在的,当为null时匹配


${}是Spring占位符的语法规则

请注意它是否能用,跟bean的初始化时间有关.



例如:${属性:默认值},如果属性为null或者不存在的话,就是用默认值填充






使用案例大全


  • @Value("test.username"):注意这样是错误的.{test.username}"):注意这样是错误的.test.username").符号不是El表达式的取值方式.
  • @Value("#{test.xxx.other}"):注意这样是错误的,"."这个点不能连续使用.不知道怎么转义
  • @Value("#{test.usernameXX?:'默认的username'}"):注意这样是错误的,如果想使用默认值你需要确保test这类以及usernameXX这个属性必须存在
@Value("#{test.username?:'默认的username'}")
   private String userName;
   @Value("#{test.password}")
   private Integer password;
  @Value("${username}") 
  private String userName;
  @Value("${other.undefindValue}") // 直接使用属性名称输入
   @Value("${xxxxx:other.undefindValue}") // 当xxxxx不存在时,使用other.undefindValue进行赋值
  @Value("${xxxxx:123}") // 当xxxxx不存在时,使用123进行赋值
  private Integer password;
  @Value("${other.undefindValue}")
  private String undefindValue;
复制代码




当然你还可以通过Environment对象进行获取哦!

// 申明bean需要使用的resource
@PropertySource("classpath:config.properties") 
public class ClazzName{
  // 获取resource
    @Autowired private Environment environment; 
    /** 获取config配置文件 */
  public String getStr(String key){
    return environment.getProperty(key);
    }
}







相关文章
|
2月前
|
XML 安全 Java
|
3月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
57 0
|
3月前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
79 0
|
15天前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
76 25
|
2月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
153 14
|
2月前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
74 14
|
3月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
3月前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
64 3
|
Java 数据库 数据安全/隐私保护
《Spring 3.0就这么简单》——1.2 实例功能概述
Spring拥有持久层、业务层和展现层的“原生技术”,分别是Spring JDBC、声明式事务和Spring MVC。为了充分展现Spring本身的魅力,在本章中仅使用Spring的这些原生技术,在以后的章节中,我们将学习其他的持久层和展现层技术,只要用户愿意,就可以平滑地将其过渡到其他技术实现中。
2109 0