# How Maven Handles Plugin Parameter Expression Evaluation: Inside the ${...} Resolution Engine

> Discover how Maven resolves ${...} placeholders in plugin configurations using the PluginParameterExpressionEvaluator to navigate object graphs and fall back to properties.

- Repository: [The Apache Software Foundation/maven](https://github.com/apache/maven)
- Tags: internals
- Published: 2026-07-05

---

**Maven resolves `${...}` placeholders in plugin configurations at runtime using the `PluginParameterExpressionEvaluator` (or Maven 4's `PluginParameterExpressionEvaluatorV4`), which recursively evaluates magic expressions, navigates object graphs via reflection, and falls back to system and project properties.**

Maven plugin parameter expression evaluation is the core mechanism that transforms string placeholders like `${basedir}` or `${project.build.directory}` into concrete Java objects during Mojo execution. Implemented in the `apache/maven` repository, this process is handled by the **`PluginParameterExpressionEvaluator`** class and its Maven 4 counterpart **`PluginParameterExpressionEvaluatorV4`**, both of which implement the `TypeAwareExpressionEvaluator` interface to provide type-safe value injection.

## The Expression Evaluation Architecture

When Maven prepares to execute a Mojo, the **`DefaultMavenPluginManager`** instantiates the evaluator and passes it to the component configurator. According to the source code in [`DefaultMavenPluginManager.java`](https://github.com/apache/maven/blob/main/DefaultMavenPluginManager.java) (lines 760-775), the manager creates either the standard evaluator or the V4 variant depending on the Maven version, then hands it off to handle parameter injection. This evaluator acts as the bridge between XML configuration strings and the Java types expected by the plugin.

## Step-by-Step Expression Resolution Process

The evaluation follows a strict seven-step pipeline defined in [`PluginParameterExpressionEvaluator.java`](https://github.com/apache/maven/blob/main/PluginParameterExpressionEvaluator.java):

### 1. Token Stripping and Normalization

The process begins by removing delimiter characters. If the entire string is wrapped in `${}`, the **`stripTokens`** method (lines 38-42) removes the opening `${` and closing `}` characters, leaving the raw expression content for further processing.

### 2. Recursive Resolution of Nested Expressions

For expressions containing nested `${}` fragments, the evaluator applies **recursive resolution** (lines 36-59). It identifies the innermost placeholder first, evaluates it completely, substitutes the result back into the string, and continues until all placeholders are resolved. This ensures that `${outer.${inner}.property}` constructs are handled correctly.

### 3. Magic Expression Handling

Maven recognizes a fixed set of **magic identifiers** via an explicit `if...else` chain (lines 68-78). These provide direct access to core Maven objects:

- **`session`** → Returns the current `MavenSession` object.
- **`project`** → Returns the current `MavenProject` instance.
- **`basedir`** → Returns the project base directory, falling back to the execution root or `user.dir` system property.
- **`settings`**, **`plugin`**, and others → Map to their corresponding Maven core objects.

### 4. Reflection-Based Property Navigation

When an expression starts with a magic identifier followed by a path separator (e.g., `project/buildDirectory`), Maven uses **`ReflectionValueExtractor.evaluate`** (lines 71-90) to walk the object graph. This reflection-based navigation allows access to nested properties like `${project.build.outputDirectory}` by traversing the `MavenProject` object's getter methods and nested objects.

### 5. Property Fallback Chain

If the expression does not match a magic identifier, Maven queries the property hierarchy in order (lines 93-105):

1. **User-defined properties** via `session.getUserProperties()`
2. **System properties** via `session.getSystemProperties()`
3. **Project properties** from the `MavenProject`

If a property value itself contains `${}` placeholders, the evaluator applies **recursive evaluation** to resolve the nested reference.

### 6. Type Compatibility Validation

As a **`TypeAwareExpressionEvaluator`**, the implementation validates type compatibility when a target type is supplied (lines 119-126). If the resolved value's type is incompatible with the expected parameter type, the evaluator discards the value and falls back to property-based resolution, ensuring that type safety is maintained during injection.

### 7. File Path Alignment

For `File`-type parameters, the **`alignToBaseDirectory`** method (lines 145-152) converts relative paths to absolute ones. It resolves the file against the project's base directory, ensuring that `${basedir}/target/classes` points to the correct absolute path regardless of the working directory.

## Practical Examples of Expression Evaluation

### Example 1: Basic Directory Resolution in XML

```xml
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
    <configuration>
        <outputDirectory>${basedir}/target/classes</outputDirectory>
    </configuration>
</plugin>

```

During execution, the `${basedir}` magic expression resolves to the project's base directory through step 3, then `alignToBaseDirectory` ensures the resulting `File` object is absolute.

### Example 2: Session Injection in a Mojo

```java
@Mojo(name = "print-session")
public class PrintSessionMojo extends AbstractMojo {
    @Parameter(defaultValue = "${session}")
    private MavenSession session;

    public void execute() {
        getLog().info("User property: " + session.getUserProperties().getProperty("myProp"));
    }
}

```

The `${session}` placeholder triggers the magic expression case, injecting the full `MavenSession` object directly into the Mojo field.

### Example 3: Custom Properties with Nested Resolution

```xml
<properties>
    <my.custom.dir>${project.basedir}/custom</my.custom.dir>
</properties>

<plugin>
    <artifactId>my-plugin</artifactId>
    <configuration>
        <targetDir>${my.custom.dir}</targetDir>
    </configuration>
</plugin>

```

Here, `my.custom.dir` is resolved from project properties (step 5), then the nested `${project.basedir}` is recursively evaluated to construct the final path.

## Summary

- **Maven plugin parameter expression evaluation** converts `${...}` strings into Java objects via `PluginParameterExpressionEvaluator` ( Maven 3.x) or `PluginParameterExpressionEvaluatorV4` (Maven 4).
- **Token stripping** removes `${}` delimiters before processing, while **recursive resolution** handles nested placeholders.
- **Magic expressions** like `session`, `project`, and `basedir` map directly to Maven core objects, with **reflection-based navigation** supporting property paths like `project/buildDirectory`.
- **Property fallback** checks user, system, and project properties when magic expressions don't match, with recursive evaluation of property values.
- **Type compatibility checks** and **path alignment** ensure values match expected parameter types and file paths are absolute.

## Frequently Asked Questions

### What is the difference between PluginParameterExpressionEvaluator and PluginParameterExpressionEvaluatorV4?

**`PluginParameterExpressionEvaluator`** is the legacy implementation for Maven 3.x, while **`PluginParameterExpressionEvaluatorV4`** is the Maven 4 implementation that uses updated APIs. Both classes implement the same `TypeAwareExpressionEvaluator` interface and follow identical resolution logic, but the V4 variant aligns with Maven 4's internal API changes.

### How does Maven handle nested ${...} expressions in plugin parameters?

Maven evaluates nested expressions recursively, starting with the innermost `${}` fragment. The evaluator extracts and resolves the inner placeholder first, substitutes the result back into the containing string, and repeats the process until all placeholders are resolved. This allows constructs like `${project.${property.name}}` to work correctly.

### What happens if a plugin parameter expression cannot be resolved?

If an expression does not match a magic identifier and is not found in user properties, system properties, or project properties, the evaluator returns the original `${...}` string unchanged (or `null` in some contexts, depending on the type requirement). This typically results in the literal placeholder being passed to the plugin, which may then fail validation or handle the unresolved value according to its own logic.

### Can I access arbitrary project properties using expression evaluation?

Yes. You can reference any property defined in the POM's `<properties>` section, settings.xml, or command line using `${property.name}`. Additionally, **reflection-based navigation** allows access to any readable property on the `project` object, such as `${project.artifactId}`, `${project.build.directory}`, or `${project.parent.basedir}`.