Skip to content

构造函数 (Constructors)

你可以使用 new 运算符来调用构造函数。除了位于 java.lang 包(如 IntegerFloatString 等)中的类型外,你应该对所有其他类型使用全限定类名。SpEL 也支持可变参数 (Varargs)

以下示例演示了如何使用 new 运算符调用构造函数:

java
// 创建一个新的 Inventor 实例
Inventor einstein = parser.parseExpression(
	"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
		.getValue(Inventor.class);

// 在 List 的 add() 方法调用中创建一个新的 Inventor 实例
parser.parseExpression(
	"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
		.getValue(societyContext);
kotlin
// 创建一个新的 Inventor 实例
val einstein = parser.parseExpression(
	"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
		.getValue(Inventor::class.java)

// 在 List 的 add() 方法调用中创建一个新的 Inventor 实例
parser.parseExpression(
	"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
		.getValue(societyContext)

补充教学

1. 构造函数 vs Bean 引用

这是开发者最容易混淆的地方:

  • new com.example.MyBean():这将绕过 Spring 容器,直接在 JVM 堆内存中创建一个普通对象。该对象不具备 Spring 的任何特性(如 @Autowired 会失效,没有任何切面增强)。
  • @myBean:这是引用由 Spring 容器管理的 Bean。
  • 建议:除非你需要创建一个临时的、纯碎的数据承载对象(如 DTO),否则应优先使用 Bean 引用。

2. 安全性警告:SpEL 注入

在表达式中使用 new 是非常强大的功能,但也是最危险的。

  • 风险:如果 SpEL 表达式的一部分来自于用户输入,攻击者可能会构造类似 new java.lang.ProcessBuilder('calc.exe').start() 的表达式来执行任意命令。
  • 防御:在处理不可信输入时,务必使用 SimpleEvaluationContext。默认情况下,SimpleEvaluationContext 不支持 new 运算符,从而切断了此类攻击路径。

3. 应用场景:动态数据转换

构造函数在 SpEL 中最实用的场景通常是在 集合投影报表导出 中将原始数据转换为视图对象 (VO):

java
// 假设从数据库查出一组原始数据,直接在 SpEL 中转换为特定的简单的 DTO
@Value("#{allUsers.![new com.example.UserVo(username, email)]}")
private List<UserVo> userVos;

4. 关于类名简写的再次提醒

类似于 T() 运算符,只有 java.lang 包下的类可以简写。

  • 正确:new String('test')
  • 错误:new Date() (必须写成 new java.util.Date())
  • 如果你觉得全限定名太长,可以参考类型章节中的 TypeLocator 注册技巧来简化类名。

Based on Spring Framework.