👊 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));
}
}
表达式的处理原理
除了编写字符串之外还可以编写数字.甚至各种字符串的数据.
一定要首先对其一个判断,判断表达式应该由那些组成,而后要拆分组成的内容,最后要进行字符串的相关数据类型的转换,从而得出最终的计算结果.
- 首先明确的给出表达式,例如1+2
- 随后需要准备出spel的表达式解析器,而进行解析的时候要按照如下的步骤.
- 使用一个专门的断词器,将给定的表达式字符串拆分为spring可以认可的数据格式.
- 随后要根据断词器的处理生成抽象语法树
- 将已经处理后的表达式定义到一个专门的表达式对象之中等待进行最终的结果计算
- 考虑到表达式里面可能会存在部分的占位变量的内容,所以在进行表达式计算之前,需要设置一个表达式上下文对象进行占位变量内容的处理.
- 最后设置了变量内容,并且利用表达式对象就可以计算出表达式最终所执行的结果
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}"):注意这样是错误的.$符号不是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技术原理系列-从零开始教你SpringEL表达式使用和功能分析讲解指南(上篇)的更多相关文章
- 👊 Spring技术原理系列(7)带你看看那些可能你还不知道的Spring特性技巧哦!
前提介绍 本文主要介绍相关Spring框架的一些新特性问题机制,包含了一些特定注解方面的认识. @Lazy可以延迟依赖注入 @Lazy注解修饰在类层面! @Lazy @Service public c ...
- 【Spring技术原理】Aspectj和LoadTimeWeaving的动态代理技术实现指南
前提介绍 当我们聊到Spring框架的项目实际开发中,用的强大的功能之一就是(面向切面编程)的这门AOP技术.如果使用得当,它的最大的作用就是侵入性比较少并且简化我们的工作任务(节省大量的重复性编码) ...
- 【转】Spring Boot干货系列:(三)启动原理解析
前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开Sprin ...
- Spring Boot干货系列:(三)启动原理解析
Spring Boot干货系列:(三)启动原理解析 2017-03-13 嘟嘟MD 嘟爷java超神学堂 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说 ...
- Spring技术内幕:SpringIOC原理学习总结
前一段时候我把Spring技术内幕的关于IOC原理一章看完,感觉代码太多,不好掌握,我特意又各方搜集了一些关于IOC原理的资料,特加深一下印象,以便真正掌握IOC的原理. IOC的思想是:Spring ...
- Spring源码系列 — 注解原理
前言 前文中主要介绍了Spring中处理BeanDefinition的扩展点,其中着重介绍BeanDefinitionParser方式的扩展.本篇文章承接该内容,详解Spring中如何利用BeanDe ...
- Spring原理系列一:Spring Bean的生命周期
一.前言 在日常开发中,spring极大地简化了我们日常的开发工作.spring为我们管理好bean, 我们拿来就用.但是我们不应该只停留在使用层面,深究spring内部的原理,才能在使用时融汇贯通. ...
- Android 虚拟多开系列二——技术原理
目录 Android虚拟多开应用有哪些? Android虚拟多开应用技术原理有哪几类? Android虚拟多开需求分析 反虚拟多开技术 ...
- Spring:源码解读Spring IOC原理
Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...
随机推荐
- 【mysql】截取查询分析
1. 慢查询日志 1.1 是什么 (1)MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL ...
- SpringBoot中的thymeleaf引擎报错
关于:thymeleaf报错: An error happened during template parsing (template: "class path resource [temp ...
- 【MATLAB】常用命令快速入门,国赛加油
矩阵运算 矩阵的基本生成 m1 = 1:5 % 生成行矩阵[1,2,3,4,5] m2 = 1:2:10 % 起点:步长:终点 [1,3,5,7,9] linspace(x1,x2,n) % 生成 n ...
- JAVA 之 每日一记 之 算法( 给你一个Excel表列序号,返回出它对应的数字 )
代码结果:(只想要代码的可以离开了,代码给你了,绝对能用的.想要思路的往下看.) class Solution { public int titleToNumber(String s) { int a ...
- MyBatis-Plus 代码生成器模板
MyBatis-Plus 代码生成器模板 maven 依赖 <!--Mysql--> <dependency> <groupId>mysql</groupId ...
- 20210712考试-2021noip11
这篇总结比我写的好多了建议直接去看 T1 简单的序列 考场:愣了一会,想到以最大值分治.每次枚举最大值两侧更小的区间,st表预处理前缀和和最大值,用桶统计答案. 注意分治时要去掉最大值. const ...
- NOIP模拟14「队长快跑·影魔·抛硬币」
T1:队长快跑 基本思路: 离散化·DP·数据结构优化DP 这三个我都没想到....气死. 定义状态数组:\(c[i][j]\)表示在i时最小的a值是j时可以摧毁的最多的水晶数. 那么 ...
- Appium自动化(3) - adb无线连接手机的方法
如果你还想从头学起Appium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1693896.html 前言 除了USB方式连接Andro ...
- Appium问题解决方案(9)- Original error: Failed to launch Appium Settings app: Condition unmet after 5090 ms
背景 执行代码报错 解决方法 该问题并不常见,主要是手机操作系统的问题 程序无法无法自动打开appiumsettings,那么我们可以手动打开appiumsettings服务(程序没有界面,会出现打开 ...
- 路由懒加载---Vue Router
一.什么是懒加载? 懒加载也就是延迟加载或者按需加载,即在需要的时候进行加载. 二.为什么在Vue路由中使用懒加载? 像vue这种单页面应用,如果没有应用懒加载,运用webpack打包后的文件将会异常 ...