Spring Boot
Migration
EvaluationContextExtensionSupport
PermissionEvaluator
Bean Definition

'Invalid bean definition' when migrating Spring Boot 2.0.6 to 2.1.0 with EvaluationContextExtensionSupport and custom PermissionEvaluator

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A migration from Spring Boot 2.0.x to 2.1.x can surface bean-registration errors that never appeared before. One common reason is that Spring Boot 2.1 disables bean definition overriding by default. If your security setup exposes custom beans through EvaluationContextExtensionSupport, a custom PermissionEvaluator, or both, a duplicate bean name that used to be silently overridden can now fail application startup.

What Changed In Spring Boot 2.1

Before Spring Boot 2.1, two beans with the same name could be registered and the later one would often replace the earlier one. In Spring Boot 2.1, bean overriding is disabled by default, so duplicate bean names now cause startup failure instead of being silently accepted.

That means an upgrade can break code that was already ambiguous.

This matters in security configuration because custom evaluators and evaluation-context extensions are often declared with framework-adjacent names such as permissionEvaluator or similar helper bean names.

A Typical Duplicate-Bean Scenario

Suppose you have a custom permission evaluator and a configuration class that also exposes related beans.

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.security.access.PermissionEvaluator;
4
5@Configuration
6public class SecurityConfig {
7
8    @Bean
9    public PermissionEvaluator permissionEvaluator() {
10        return new CustomPermissionEvaluator();
11    }
12
13    @Bean
14    public CustomSecurityExtension securityExtension() {
15        return new CustomSecurityExtension(permissionEvaluator());
16    }
17}

If another configuration, auto-configuration path, or imported module also registers a bean with the same name, Boot 2.1 can fail with an invalid bean definition or bean override exception instead of allowing one to replace the other.

The problem is usually not EvaluationContextExtensionSupport itself. It is the collision that becomes visible during migration.

Exposing Custom Security Expressions Safely

A typical extension based on EvaluationContextExtensionSupport looks like this.

java
1import java.util.Collections;
2import java.util.Map;
3import org.springframework.data.spel.spi.EvaluationContextExtensionSupport;
4import org.springframework.security.access.PermissionEvaluator;
5
6public class CustomSecurityExtension extends EvaluationContextExtensionSupport {
7    private final PermissionEvaluator permissionEvaluator;
8
9    public CustomSecurityExtension(PermissionEvaluator permissionEvaluator) {
10        this.permissionEvaluator = permissionEvaluator;
11    }
12
13    @Override
14    public String getExtensionId() {
15        return "security";
16    }
17
18    @Override
19    public Map<String, Object> getProperties() {
20        return Collections.singletonMap("permissionEvaluator", permissionEvaluator);
21    }
22}

This is fine as long as the beans involved are uniquely named and intentionally wired.

Preferred Fixes

The best fix is usually to remove the ambiguity rather than simply re-enable bean overriding.

Practical options are:

  • rename one of the conflicting beans
  • inject by type with @Primary only when multiple beans are intentionally present
  • isolate your security extension in one configuration path so it is registered exactly once

Example with explicit bean names:

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.security.access.PermissionEvaluator;
4
5@Configuration
6public class SecurityConfig {
7
8    @Bean("customPermissionEvaluator")
9    public PermissionEvaluator customPermissionEvaluator() {
10        return new CustomPermissionEvaluator();
11    }
12
13    @Bean
14    public CustomSecurityExtension securityExtension(PermissionEvaluator customPermissionEvaluator) {
15        return new CustomSecurityExtension(customPermissionEvaluator);
16    }
17}

This makes the wiring intent clearer and avoids silent name collisions.

Compatibility Fallback

If you need a short-term migration bridge, you can re-enable the old override behavior.

properties
spring.main.allow-bean-definition-overriding=true

That may get the application running, but it should be treated as a compatibility fallback, not the ideal final state. Silent overriding can hide configuration mistakes that are better fixed explicitly.

Common Pitfalls

A common mistake is assuming the upgrade broke PermissionEvaluator or EvaluationContextExtensionSupport directly. In many cases the real issue is duplicate bean registration becoming visible.

Another mistake is solving the problem only by enabling bean overriding. That restores old behavior, but it also restores the ambiguity that caused the surprise.

Developers also often focus on injection by type while the actual failure is bean naming. Boot 2.1 made name collisions much less forgiving.

Finally, when multiple security configuration classes are imported conditionally, verify that they are not both creating the same bean under different migration paths.

Summary

  • Spring Boot 2.1 disables bean definition overriding by default.
  • Migrations can therefore expose duplicate bean names that used to be silently overridden.
  • Custom PermissionEvaluator and EvaluationContextExtensionSupport setups are common places where this appears.
  • Prefer unique bean names and explicit wiring over silent override behavior.
  • 'spring.main.allow-bean-definition-overriding=true is a short-term compatibility fallback, not usually the best long-term fix.'
  • Treat the migration error as a signal to clean up ambiguous configuration.

Course illustration
Course illustration

All Rights Reserved.