LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1617 min read

Spring Expression Language (SpEL)

Learn the basics of Spring Expression Language syntax and how to use it inside @Value annotations to compute or inject dynamic values.

Introduction

Sometimes a hardcoded value or a simple property placeholder is not enough; you need to compute a value, reference another bean's property, or apply conditional logic right inside a configuration annotation. The Spring Expression Language, or SpEL, exists exactly for this purpose.

What You Will Learn
  • What SpEL is and when to reach for it
  • The difference between SpEL expressions and property placeholders
  • Core SpEL syntax: literals, arithmetic, and operators
  • How to reference other beans and their properties from an expression

What Is SpEL?

SpEL is a powerful expression language that supports querying and manipulating an object graph at runtime. It looks similar to the Unified Expression Language used in JSP, but it is Spring's own implementation, usable anywhere in the framework, most commonly inside @Value annotations and in XML bean definitions.

SpEL vs Property Placeholders

It is easy to confuse SpEL expressions, written with #{...}, and property placeholders, written with ${...}. They look alike but do different jobs: ${...} simply substitutes a value from a properties file or environment, while #{...} evaluates a full expression that can include arithmetic, method calls, and bean references.

SyntaxPurposeExample
${...}Look up a property value${app.timeout}
#{...}Evaluate a SpEL expression#{2 * 10}
Good to Know

You can combine the two: #{'${app.timeout}' as any expression fragment} lets a SpEL expression pull in a resolved property placeholder as one of its inputs.

Basic SpEL Syntax

SpEL supports literals, arithmetic and relational operators, the ternary operator, and access to system properties, all inside the #{...} delimiter.

AppSettings.java
@Component
public class AppSettings {
@Value("#{2 * 10}")
private int maxConnections;
@Value("#{10 > 5 ? 'high' : 'low'}")
private String priority;
@Value("#{systemProperties['user.name']}")
private String currentUser;
@Value("${app.timeout:30}")
private int timeoutSeconds;
@PostConstruct
public void printSettings() {
System.out.println("maxConnections = " + maxConnections);
System.out.println("priority = " + priority);
System.out.println("timeoutSeconds = " + timeoutSeconds);
}
}
Console Output

Click Run to see what this code prints.

The timeoutSeconds field shows a colon-based default: if the property app.timeout is not defined anywhere, Spring falls back to 30 instead of throwing an error.

Referencing Other Beans

A SpEL expression can reach into another bean by name and read one of its properties or call one of its methods, which is useful for deriving a value from existing configuration instead of duplicating it.

PricingConfig.java
@Component("pricingConfig")
public class PricingConfig {
public double getBasePrice() {
return 100.0;
}
}
DiscountService.java
@Component
public class DiscountService {
@Value("#{pricingConfig.basePrice * 0.9}")
private double discountedPrice;
@PostConstruct
public void print() {
System.out.println("discountedPrice = " + discountedPrice);
}
}
Console Output

Click Run to see what this code prints.

Common Mistakes

Avoid These Mistakes
  • Mixing up #{...} and ${...} and wondering why an expression is not evaluated as expected.
  • Writing complex, hard-to-read business logic inside a SpEL expression instead of a regular Java method.
  • Referencing a bean by name in SpEL before that bean has been defined or without checking the name matches exactly.
  • Forgetting a sensible default (using the colon syntax) for optional properties, causing startup failures when a property is missing.

Best Practices

  • Keep SpEL expressions short and readable; move anything complex into a plain Java method and call that instead.
  • Use ${...} for simple property lookups and reserve #{...} for cases that genuinely need computation or bean references.
  • Always provide default values for optional external properties using the colon syntax.
  • Prefer constructor injection with a plain Java default over deeply nested SpEL expressions when the logic gets complicated.

Frequently Asked Questions

It also appears in @ConditionalOnExpression-style conditions, in some Spring Security method annotations like @PreAuthorize, and in XML-based bean definitions through the value attribute with #{...}.

Yes, as long as the referenced bean is already fully constructed and registered under that name when the expression is evaluated.

At runtime, when the container processes the @Value annotation during bean initialization, not when the code is compiled.

Key Takeaways

  • SpEL expressions are written with #{...} and can include arithmetic, conditionals, and bean references.
  • Property placeholders use ${...} and simply substitute a configured value.
  • @Value is the most common place to use SpEL in everyday Spring code.
  • You can reference another bean's properties and methods directly inside a SpEL expression.
  • Keep expressions simple; push real logic into Java methods.

Summary

SpEL gives you a compact way to compute or derive values right where they are used, without writing extra boilerplate code. With configuration and value injection covered, it is time to look at a different capability altogether: how Spring lets you add behavior, like logging or security checks, across many classes at once through aspect-oriented programming.

Next Lesson →

Aspect-Oriented Programming (AOP) Basics