Skip to content

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 表达式中配置复杂的工厂依赖非常有用,但在一般的业务代码中较少直接使用。

Based on Spring Framework.