skip to content

What requirement does @PreFilter place on the collection it modifies, and how do you tell it which argument to filter when a method has several collection parameters?

level: middleimportance: should knowfreq 30%

answer

  1. @PreFilter mutates in place -> needs MUTABLE collection
  2. List.of / unmodifiable -> UnsupportedOperationException
  3. filterTarget names the arg by parameter name
  4. filterTarget is @PreFilter-only
  5. @PostFilter has one target = return value

basics

~20 s

@PreFilter removes elements in place, so the collection argument must be mutable — an immutable one (like List.of(...)) throws UnsupportedOperationException. When a method has multiple collection parameters, name the one to filter with the filterTarget attribute.

solid answer

~50 s

@PreFilter mutates the incoming collection by removing disallowed elements before the method body runs, so the argument you pass must be a mutable collection. If a caller passes an immutable collection such as List.of(...) or Collections.unmodifiableList(...), the removal fails with UnsupportedOperationException. When a method takes more than one collection argument, Spring cannot guess which to filter, so you must disambiguate with filterTarget: @PreFilter(filterTarget = "ids", value = "filterObject != 4"). filterTarget is a @PreFilter-only concern — @PostFilter always has a single target (the return value), so it has no filterTarget attribute. Because @PreFilter edits the argument in place, the effect is visible to the method body and, since collections are passed by reference, to the caller's collection object too. This mutation requirement is the classic gotcha: switching a test fixture from a mutable ArrayList to List.of silently breaks the method.

code

java · 17 lines
java
@Service
public class OrderService {

    // Two collection arguments -> must say which one to filter.
    // filterTarget selects the 'ids' parameter by name; 'value' holds the SpEL.
    @PreFilter(filterTarget = "ids", value = "filterObject != 4")
    public void archive(List<Integer> ids, List<String> reasons) {
        // 'ids' no longer contains 4 here.
        ids.forEach(repository::archive);
    }
}

// Caller:
// OK  -> mutable, pruning works:
service.archive(new ArrayList<>(List.of(1, 2, 3, 4)), reasons);
// BREAKS -> immutable, throws UnsupportedOperationException when @PreFilter removes 4:
service.archive(List.of(1, 2, 3, 4), reasons);

go deeper

for a junior

Just know @PreFilter changes the collection you pass in, so it must be modifiable.

for a middle

Explain the UnsupportedOperationException gotcha and correctly use filterTarget with the exact parameter name.

for a senior

Note the in-place mutation is visible to the caller and that parameter-name resolution needs -parameters; know @PostFilter has no filterTarget.

for a principal

Weigh the side-effecting mutation and immutable-collection fragility when deciding whether method-level filtering is an acceptable pattern in your codebase.

## Why mutability matters for @PreFilter `@PreFilter` works by iterating the target collection and **removing** elements that fail the SpEL expression (conceptually `collection.removeIf(el -> !expression)`). Removal mutates the collection **in place**. Therefore the argument must be a **mutable** collection. If a caller passes an **immutable** collection — `List.of(1, 2, 3)`, `Collections.unmodifiableList(...)`, `Arrays.asList(...)` (fixed-size), or `List.copyOf(...)` — the removal throws `java.lang.UnsupportedOperationException`. This is a frequent, surprising failure: code works with `new ArrayList<>(...)` and breaks the moment someone "tidies up" the call site to `List.of(...)`. Because collections are passed by reference, the pruning is also visible to the **caller's** object, not just inside the method — a subtle side effect to be aware of. (For arrays, Spring builds a new pruned array and — where possible — reflectively copies it back; for `Stream`, it returns a filtered stream. The mutable-collection rule is specifically about `Collection` arguments.) ## filterTarget: choosing which argument With a **single** collection parameter, `@PreFilter` filters it automatically. With **multiple** collection parameters, Spring cannot infer the target and you must name it explicitly: ```java @PreFilter(filterTarget = "ids", value = "filterObject != 4") public void update(List<Integer> ids, List<String> names) { ... } ``` Here `filterTarget = "ids"` selects the `ids` parameter by its **name**, and `value` (or the default `value` position) holds the SpEL expression. If you omit `filterTarget` on a multi-collection method, Spring throws an `IllegalArgumentException` at invocation time because it cannot determine the target. > Parameter-name resolution requires names to be available at runtime — compile with `-parameters` (Spring Boot enables this) or the names may not resolve. ## Why @PostFilter has no filterTarget `@PostFilter` filters exactly one thing — the method's **return value** — so there is never ambiguity and there is **no** `filterTarget` attribute on `@PostFilter`. Expecting a `filterTarget` on `@PostFilter` is a red flag. ## Practical guidance - Return/accept `ArrayList`, `HashSet`, etc. (mutable) when a method is annotated with `@PreFilter`. - Be explicit with `filterTarget` for multi-arg methods and match the parameter name exactly. - Remember the in-place mutation is observable by the caller.

  • Your @PreFilter method suddenly throws UnsupportedOperationException in production. What is the most likely cause?
    A caller is passing an immutable collection (List.of(...), Collections.unmodifiableList, List.copyOf, or Arrays.asList). @PreFilter removes elements in place and cannot mutate an immutable collection. Fix by wrapping the argument in a mutable collection such as new ArrayList<>(...).
  • Does @PostFilter have a filterTarget attribute?
    No. @PostFilter always operates on the single return value, so there is nothing to disambiguate. filterTarget exists only on @PreFilter, for methods with more than one collection argument.

saying these in an interview costs you the question

  • Claiming @PreFilter works on any collection including List.of (it needs a mutable one).
  • Saying @PostFilter has a filterTarget attribute (it does not).
  • Thinking Spring auto-picks the argument when there are several collections (it throws unless you set filterTarget).

context