Bean 引用 (Bean References)
如果评估上下文(Evaluation Context)已配置了 Bean 解析器(Bean Resolver),你可以使用 @ 符号前缀在表达式中查找 Bean。
以下示例演示了如何引用 Bean:
java
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
// 设置自定义的 Bean 解析器
context.setBeanResolver(new MyBeanResolver());
// 在评估期间,这将导致调用 MyBeanResolver 上的 resolve(context, "someBean")
Object bean = parser.parseExpression("@someBean").getValue(context);kotlin
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
// 设置自定义的 Bean 解析器
context.setBeanResolver(MyBeanResolver())
// 在评估期间,这将调用 MyBeanResolver 上的 resolve(context, "someBean")
val bean = parser.parseExpression("@someBean").getValue(context)注意
如果 Bean 的名称包含点号(.)或其他特殊字符,你必须将 Bean 名称作为字符串字面量提供——例如:@'order.service'。
若要访问 FactoryBean 本身(而不是由它创建的 Bean),应在 Bean 名称前使用 & 符号前缀。
java
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// 这将在评估期间调用 MyBeanResolver 上的 resolve(context, "&someFactoryBean")
Object factoryBean = parser.parseExpression("&someFactoryBean").getValue(context);kotlin
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setBeanResolver(MyBeanResolver())
// 这将调用 MyBeanResolver 上的 resolve(context, "&someFactoryBean")
val factoryBean = parser.parseExpression("&someFactoryBean").getValue(context)补充教学
1. 核心原理:BeanResolver
在普通的 SpEL 独立环境(Standalone)中,SpEL 并不知道什么是 Spring 容器。
- 桥梁:当你使用
@beanName时,SpEL 会查阅其配置的BeanResolver。 - 标准实现:在 Spring 环境中,默认使用的是
BeanFactoryResolver,它会直接从ApplicationContext中根据名称取出 Bean。
2. @ 符号的精妙之处
使用 @ 引用 Bean 后,你可以直接链式调用该 Bean 的方法或访问其属性:
java
// 在表达式中直接调用 Bean 的方法
@Value("#{@authService.hasRole('ADMIN')}")
private boolean isAdmin;这使得 SpEL 成为连接 Spring 容器内各个组件的“粘合剂”。
3. @bean vs #bean:千万不要混淆
@someName:表示去 Spring 容器 中找一个名为someName的对象(Bean 引用)。#someName:表示从 评估上下文变量池 中找一个名为someName的变量(变量引用)。- 场景建议:虽然可以通过
setVariable("myService", service)将服务设为变量,但推荐始终使用@来显式表达这是一个 Spring Bean,以提高代码意图的清晰度。
4. 安全性:SimpleEvaluationContext 的保护
由于通过 SpEL 访问 Bean 后可以执行任何公共方法,这在处理不受信任的输入时存在巨大风险。
- 默认限制:
SimpleEvaluationContext默认 不包含BeanResolver。这意味着即使你在表达式中写了@myBean,它也会抛出异常。 - 原则:如果你需要处理用户输入的表达式(例如动态筛选条件),绝不要给它配置
BeanResolver。
5. FactoryBean 的处理逻辑
如果你不熟悉 Spring 的 FactoryBean,可以这样记忆:
@myService:拿到的通常是业务对象。&myService:拿到的是产生该业务对象的“工厂”对象。 这对于在 SpEL 表达式中配置复杂的工厂依赖非常有用,但在一般的业务代码中较少直接使用。