深入浅出spring原理及实战「原理分析专题」从零开始教你springel表达式使用和功能分析讲解指南(上篇)(代码片段)

洛神灬殇 洛神灬殇     2022-12-16     511

关键词:

  • 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));
    

表达式的处理原理

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

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

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(" t e s t . u s e r n a m e " ) : 注 意 这 样 是 错 误 的 . 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);
    

深入浅出spring原理及实战「原理分析专题」不看源码就带你剖析mvc容器核心流程以及运作原理

前提回顾之前已经写了很多问斩针对于SpringMVC的的执行原理和核心流程,在此再进行冗余介绍就没有任何意义了,所以我们主要考虑的就是针对于SpringMVC还没但大框架有介绍的相关内容解析分析和说明,那么接下来就... 查看详情

深入浅出spring原理及实战「源码原理实战」从底层角度去分析研究propertysourcesplaceholderconfigurer的原理及实战注入机制

Spring提供配置解析功能主要有一下xml文件占位符解析和Java的属性@Value的占位符解析配置这两种场景进行分析和实现解析,如下面两种案例。xml文件的占位符解析配置<beanid="dataSource"class="com.alibaba.druid.pool.DruidDataSource"init-method="i... 查看详情

深入浅出sentinel原理及实战「基础实战专题」零基础探索分析sentinel控制台开发指南

Sentinel控制台Sentinel提供了一个轻量级的开源控制台SentinelDashboard,它提供了机器发现与健康情况管理、监控(单机和集群)、规则管理与推送等多种功能。Sentinel控制台提供的功能如下查看机器列表以及健康情况:Sentnel控制台能... 查看详情

深入浅出spring原理及实战「原理分析专题」不看源码就带你剖析aop容器核心流程以及运作原理(代码片段)

前提回顾前一篇文章主要介绍了spring核心特性机制的IOC容器机制和核心运作原理,接下来我们去介绍另外一个较为核心的功能,那就是AOP容器机制,主要负责承接前一篇代理模式机制中动态代理:JDKProxy和CglibProxy... 查看详情

深入浅出spring原理及实战「源码调试分析」结合datasourceregister深入分析importbeandefinitionregistrar的源码运作流程

每日一句人的一生中不可能会一帆风顺,总会遇到一些挫折,当你对生活失去了信心的时候,仔细的看一看、好好回想一下你所遇到的最美好的事情吧,那会让你感觉到生活的美好。注入案例代码如何通过实现SpringBoot框架带有... 查看详情

深入浅出sentinel原理及实战「基础实战专题」零基础实现服务流量控制实战开发指南

你若要喜爱你自己的价值,你就得给世界创造价值。Sentinel的组成部分Sentinel主要由以下两个部分组成。Sentinel核心库(Java客户端):Sentinel的核心库不依赖任何框架或库,能够运行于Java8及以上的版本的运行时环境中,同时对Spri... 查看详情

深入浅出spring原理及实战「源码调试分析」结合datasourceregister深入分析importbeandefinitionregistrar的源码运作流程(代码片段)

每日一句人的一生中不可能会一帆风顺,总会遇到一些挫折,当你对生活失去了信心的时候,仔细的看一看、好好回想一下你所遇到的最美好的事情吧,那会让你感觉到生活的美好。注入案例代码如何通过实现Spri... 查看详情

jvm技术专题深入分析内存布局及gc原理分析「下卷」(代码片段)

...了,详细可见【JVM技术专题】深入分析内存布局及GC原理分析「上卷」)和【JVM技术专题】深入分析内存布局及GC原理分析「中卷」,目前我相信已经会有相关的对GC的原理和虚拟机的运作机制有了一定的了解了,... 查看详情

深入浅出seata原理及实战「入门基础专题」带你透析认识seata分布式事务服务的原理和流程

分布式事务的背景随着业务的不断发展,单体架构已经无法满足我们的需求,分布式微服务架构逐渐成为大型互联网平台的首选,但所有使用分布式微服务架构的应用都必须面临一个十分棘手的问题,那就是“分布式事务”问题... 查看详情

深入浅出seata原理及实战「入门基础专题」带你透析认识seata分布式事务服务的原理和流程

分布式事务的背景随着业务的不断发展,单体架构已经无法满足我们的需求,分布式微服务架构逐渐成为大型互联网平台的首选,但所有使用分布式微服务架构的应用都必须面临一个十分棘手的问题,那就是“分... 查看详情

spring事务源码分析专题mybatis的使用及跟spring整合原理分析

...如下:本文要解决的是第二点,Mybatis的使用、原理及跟Spring整合原理分析。Mybatis的简单使用搭建项目pom文件添加如下依赖<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.6& 查看详情

深入浅出seata原理及实战「入门基础专题」探索seata服务的at模式下的分布式开发实战指南

承接上文上一篇文章说到了Seata为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。那么接下来我们将要针对于AT模式下进行分布式事务开发的原理进行介绍以及实战。SeataAT模式在AT、TCC、SAGA和XA这四种... 查看详情

jvm技术专题深入分析内存布局及gc原理分析「中卷」(代码片段)

...;希望可以先看一下【JVM技术专题】深入分析内存布局及GC原理分析「上卷」),接下来我们会侧重点去讲解GC回收机制的运作流程以及回收期(暂时不包含最新的ZGC),小 查看详情

深入浅出sentinel原理及实战「框架整合专题」sentinel服务框架对接dubbo服务框架整合开发指南(代码片段)

开源框架适配为了减少开发的复杂程度,Sentinel对大部分的主流框架都进行了适配,例如:WebServlet、Dubbo、SpringCloud、gRPC、SpringWebFlux和Reactor等。云原生微服务体系SpringBoot/SpringCloudQuarkusWeb适配WebServletSpringWebSpringWebFluxJ... 查看详情

精华推荐|深入浅出sentinel原理及实战「原理探索专题」完整剖析alibaba微服务架构体系之轻量级高可用流量控制组件sentinel

Sentinel是什么?不要概念混淆啊!注意:本Sentinel与Redis服务Sentinel是两回事,压根不是一个概念,请大家不要混肴。Alibaba的SentinelSentinel是由阿里巴巴中间件团队开发的开源项目,是一种面向分布式微服务... 查看详情

深入浅出seata原理及实战「入门基础专题」探索seata服务的at模式下的分布式开发实战指南(代码片段)

承接上文上一篇文章说到了Seata为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。那么接下来我们将要针对于AT模式下进行分布式事务开发的原理进行介绍以及实战。SeataAT模式在AT、TCC、SAGA和XA这... 查看详情

jvm技术专题深入分析内存布局及gc原理分析「上卷」(代码片段)

...式,本文暂时不考虑和介绍。“java的内存布局以及GC原理”是java开发人员绕不开的话题,也是 查看详情

深入浅出nacos原理及调优「原理分析专题」配置中心加载原理和配置实时更新原理分析(代码片段)

官方资源官方资源带着问题去思考客户端长轮询的响应时间会受什么影响为什么更改了配置信息后客户端会立即得到响应客户端的超时时间为什么要设置为30s带着以上这些问题我们从服务端的代码中去探寻结论。配置中心(Configur... 查看详情