抢先一步
VMware 提供培训和认证,以加速您的进步。
了解更多欢迎来到新一期的 You May Not Need Another Library For That (YMNNALFT)!自 2016 年以来,我花了很多时间在 我的 Spring Tips 视频 中阐明(或者试图这样做!)Spring 生态系统中的一些巨大的机会。 然而,今天,我以不同的精神来到这里,想要专注于那些小而有时隐藏的宝石,它们可以做很棒的事情,并且可以让你免于额外的第三方依赖及其隐含的复杂性。
您的用户是否希望有一种方便、简短的方式来自定义应用程序的行为? 表达式语言是专门用于允许对应用程序行为进行低接触自定义的。 表达式语言有很多应用。 它们可以帮助您评估事物! 也许他们可以运行用户配置的简单谓词逻辑。 表达式语言可以取消引用环境变量、将事物粘合在一起、支持模板、自定义访问控制和授权谓词、支持自定义消息流路由和工作流事件处理程序逻辑等等。 一个好的表达式语言非常有用,以至于我们构建了 Spring 表达式语言 (SpEL) 并将其与 Spring Framework 3.0 一起发布,早在 2009 年!
我永远不会忘记那次添加! 我当时在 Subversion 中浏览源代码,我观察到 Andy Clement(我们的一位常驻疯狂科学家,也是我认识的最优秀的人之一)已经向 Spring 添加了一种全新的表达式语言。
当然,他确实这样做了。
值得注意的是,该表达式是现有表达式语言(如 OGNL 和 JBoss EL)的超集(两者都比 Java Server Pages 或 Java Server Faces 中的表达式语言提供更多功能)。 Andy 在几周内开发了这种新的表达式语言。 所以,当我告诉你 Andy Clement 在(几周内!)签入了一种超越 X 品牌的全新表达式语言时,这应该告诉你的是,Andy Clement 可以做任何事情,我们都应该高兴当机器攻击时,他会在我们这边!
这种新的表达式语言使用了 ANTLR,这是一种很棒、功能强大的解析器生成器,给定一个语法定义,它将生成知道如何解析该语法中定义的任何内容的 Java 代码。 所以,你可以使用 ANTLR 语法来教 ANTLR 如何解析,比如说,一个标签(#
+ A_LABEL
)或一个 ISO 8601 日期或 Java 源代码,或者一个 SQL 查询,ANTLR 将生成 Java 代码来解析符合该语法的文本。 当它遇到该语法的元素时,它会调用回调。 它基本上是 JVM 生态系统中相当于经典 Yacc / Lex 工具链的工具,毫无疑问,您正在使用的软件反过来使用 ANTLR 来提供解析器。 ANTLR 很棒。 你可以编写一个 Java 编译器。 一个 SQL 解析器。 一个电子邮件验证器。 一个 HTML 解析器。 天空才是极限! 它被最优秀、最聪明的人使用,因此您可以放心它会工作! 它用于 Groovy、Jython、Hibernate、MySQL Workbench、Apache Cassandra、Processing、Presto、Salesforce 的 Apex 以及其他无数项目中。
我印象深刻。
这种表达式语言看起来很棒,ANTLR 很棒,而且它在各个方面都是可靠的工程。
所以,很自然地,Andy 删除了 ANTLR 并用他自己手写的递归下降解析器取代了它! 惊人!(谁会这么做?)
我非常印象深刻!
朋友们,SpEL 是非常棒的软件。 它被广泛应用于 Spring 生态系统中,在 Spring Framework 中用于评估目的; 在 Spring Security 中用于某些类型的访问控制规则; 在 Spring Integration 中用于评估消息的表达式; 在 Spring Data 中将特定查询与其他上下文(如 Spring Security)联系起来。 这样的例子不胜枚举。
在过去的十年中,SpEL 变得更加神奇。 它甚至还有一个编译器! 这是很棒的,因为该编译步骤对用户来说可以完全透明。 而且你可以将它用于你想要的任何用途,无论是在 Spring 配置中还是单独使用,作为一个独立的库。
让我们看一个例子。
您需要以下依赖项。
org.springframework.boot
: spring-boot-starter
这是代码
package bootiful.el;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@SpringBootApplication
public class BootifulApplication {
@Bean
Bar bar(@Value("#{ foo.name }") String name) {
return new Bar(name);
}
@Bean
Foo foo() {
return new Foo();
}
@Bean
ApplicationListener<ApplicationReadyEvent> ready() {
return event -> {
SpelParserConfiguration configuration = new SpelParserConfiguration(//
SpelCompilerMode.IMMEDIATE, ClassLoader.getSystemClassLoader());
ExpressionParser expressionParser = new SpelExpressionParser(configuration);
double randomProperty = evaluate(expressionParser, "randomProperty", new MyContext());
System.out.println("randomProperty: " + randomProperty);
String uppercase = evaluate(expressionParser, "'andy clement for president'.toUpperCase()", null);
System.out.println("uppercase: " + uppercase);
};
}
@SuppressWarnings("unchecked")
private static <T> T evaluate(ExpressionParser expressionParser, String expression, Object context) {
Expression expression2 = expressionParser.parseExpression(expression);
if (context != null) {
EvaluationContext evaluationContext = new StandardEvaluationContext(context);
return (T) expression2.getValue(evaluationContext);
} //
else {
return (T) expression2.getValue();
}
}
public static void main(String[] args) {
System.setProperty("spring.profiles.active", "el");
SpringApplication.run(BootifulApplication.class, args);
}
}
@Data
class Foo {
private String name = getClass().getName();
}
@Data
class Bar {
Bar(@Value("#{ foo.name }") String name) {
System.out.println("name: " + name);
}
}
@Data
class MyContext {
private final double randomProperty = Math.random();
public int factorial(int n) {
if (n == 0)
return 1;
else
return (n * factorial(n - 1));
}
}
这是我放入 application.properties
中的内容
spring.main.web-application-type=none
在这个应用程序中,有两件事需要注意:在独立上下文中使用 SpEL,以及将 SpEL 用作 Spring 应用程序的一部分。
在 ApplicationListener<ApplicationReadyEvent>
中,我手动实例化一个 SpelExpressionParser
实例,我可以针对它评估 SpEL 表达式。 我展示了如何配置一个自定义上下文(一个表达式可以在其中调用方法和取消引用属性的对象),并使用表达式语言来调用 String
字面量上的一个方法。
我还配置了两个 bean,Foo
和 Bar
。 Bar
依赖于 Foo
中的一个属性 name
,它使用 SpEL 取消引用该属性,然后引用 Spring 应用程序上下文中的其他 bean。
你喜欢这种一目了然的方法吗? 你学到什么了吗? 与往常一样,我很想收到你的来信,所以请在 Twitter 上发表评论 (@starbuxman) ! 我会带着另一期 YMNNALFT 回来,所以一定要记得不要错过。