In SpEL, how do you access static members/types with T(), and how do you reference other Spring beans by name?
answer
- T(pkg.Type) -> Class, static members
- java.lang auto-imported; else fully-qualify
- @beanName references a bean; chain properties/methods
- &beanName = the FactoryBean itself
- needs a BeanResolver in the context
basics
~10 sT(fully.qualified.Type) gives you a type reference so you can call its static methods, read static fields, or use enum constants — e.g. T(java.lang.Math).PI. To use another bean, write @beanName in the expression, e.g. @configService.timeout.
solid answer
~40 sThe **`T()` operator** yields a `java.lang.Class` reference for a type, letting you access **static** members: static methods `T(java.lang.Math).max(a,b)`, static fields/constants `T(java.lang.Math).PI`, and enum values `T(com.acme.Status).ACTIVE`. Fully-qualify the class unless it is in `java.lang`, which is imported by default. To reference another **Spring bean** by its bean name inside SpEL, prefix the name with `@`: `@beanName` resolves the bean, and you can chain onto it — `@appProperties.maxUsers` or `@clock.instant()`. The `@` form works only when the context has a `BeanResolver` wired to the `BeanFactory`, which Spring provides for `@Value` and other container-evaluated expressions (it is *not* available in a bare `SimpleEvaluationContext`). There is also `&beanName` to get a `FactoryBean` itself rather than its product. These are common in `@Value` to pull config from other beans or invoke a static factory.
code
java · 19 lines@Component
class FeatureConfig {
// Static field via T() (java.lang auto-imported)
@Value("#{T(Math).PI}")
private double pi;
// Enum constant via fully-qualified T()
@Value("#{T(java.time.DayOfWeek).FRIDAY.name()}")
private String day;
// Reference another bean by name and read a property
@Value("#{@appProperties.maxUsers}")
private int maxUsers;
// Bean method + static factory combined
@Value("#{@featureFlags.enabled ? T(java.time.Instant).now() : null}")
private java.time.Instant enabledAt;
}go deeper
Know T() reaches static/enum members and @beanName references another bean.
Explain the java.lang auto-import rule, full qualification, chaining onto @beanName, and the BeanResolver requirement.
Add the security implication of T(), &beanName for FactoryBeans, custom TypeLocator imports, and why over-reaching into beans from @Value is a smell.
Set conventions favoring @ConfigurationProperties/injection over ad-hoc @beanName reach-ins and ban T() in any user-influenced expression.
## `T()` — type / static access SpEL treats a plain identifier as a property/variable, so to talk about a **type** you wrap it in `T(...)`: - `T(java.lang.Math)` evaluates to the `Class` object for `Math`. - From there you reach **static methods** — `T(java.lang.Math).random()`, `T(java.lang.Integer).parseInt('42')`. - **Static fields / constants** — `T(java.lang.Math).PI`, `T(java.lang.Integer).MAX_VALUE`. - **Enum constants** — `T(java.time.DayOfWeek).MONDAY`, and comparisons like `status == T(com.acme.Status).ACTIVE`. ### Import rules Types in the **`java.lang`** package are auto-imported, so `T(String)`, `T(Math)`, `T(Integer)` work unqualified. Everything else must be **fully qualified** (`T(java.util.UUID)`, `T(com.acme.Status)`). You can register extra imports via a `StandardTypeLocator` on a `StandardEvaluationContext`. ### Security note `T()` is exactly what makes SpEL dangerous over untrusted input — `T(java.lang.Runtime).getRuntime().exec(...)`. `SimpleEvaluationContext` **disables** `T()` for this reason. ## `@beanName` — bean references Inside a SpEL expression, `@` followed by a bean name resolves that bean from the container: - `@dataSource` → the `dataSource` bean. - `@appProperties.region` → read a property off it. - `@clock.instant()` → call a method on it. This requires a `BeanResolver` in the `EvaluationContext`. Spring installs one (`BeanFactoryResolver` inside `StandardBeanExpressionResolver`) when it evaluates `@Value`, `@Cacheable`, security expressions, etc. In a hand-built context with no bean resolver, `@name` throws. ### `&beanName` — the FactoryBean itself Prefix with `&` to obtain the underlying `FactoryBean` rather than the object it produces: `@&myFactoryBean`. Rarely needed, but it mirrors the `BeanFactory.getBean("&name")` convention. ## Putting them together `@Value("#{ @featureFlags.enabled ? T(java.time.Instant).now() : null }")` references a bean and a static factory in one expression. ## Gotchas - **Forgetting `T()`** — `Math.PI` (without `T()`) tries to read a `PI` property off a `Math` variable/root and fails. - **Not fully-qualifying** non-`java.lang` types. - **`@name` outside the container** — no `BeanResolver`, so it errors; this is a frequent surprise in unit tests that hand-build contexts. - **Case sensitivity** — bean names and static member names are exact. - **Overuse** — reaching into arbitrary beans from `@Value` couples config to bean internals; prefer `@ConfigurationProperties` or constructor injection for anything non-trivial. ## When to use `T()` for enum comparisons and static factory/util calls in guard/condition expressions; `@beanName` to pull a single value or invoke a helper bean where full injection would be overkill.
- Why does T(Math).PI work but T(java.util.UUID).randomUUID() require full qualification?Only the java.lang package is auto-imported by SpEL's type locator, so Math resolves unqualified. Any other package, like java.util, must be fully qualified unless you register an extra import on a StandardTypeLocator.
- When does @beanName fail to resolve?When the EvaluationContext has no BeanResolver, e.g. a hand-built SimpleEvaluationContext or a plain StandardEvaluationContext with no BeanFactoryResolver set. Spring wires one automatically for container-evaluated expressions like @Value.
saying these in an interview costs you the question
- Writing Math.PI instead of T(Math).PI
- Assuming all types are auto-imported (only java.lang is)
- Thinking @beanName works in any standalone context without a BeanResolver
- Confusing @beanName (the bean) with &beanName (its FactoryBean)