How to Dynamically Register and Unregister Spring Boot Controllers at Runtime
This article demonstrates how to programmatically register and unregister Spring Boot controller methods, covering simple examples, adding request constraints, custom matching conditions, and endpoint removal, with complete code snippets and a test result illustration.
1. Introduction
While Spring Boot normally registers controllers using @Controller or @RestController together with @RequestMapping (or its shortcuts such as @GetMapping , @PostMapping ), it also provides a programmatic way to register handler methods dynamically for advanced scenarios.
2. Practical Examples
2.1 Simple Example
First define a handler class that processes a request.
<code>@Component
public class UserHandler {
@ResponseBody
public User getUser(@PathVariable Long id) {
return new User(id, "姓名 - " + id);
}
}
</code>Then register the handler method programmatically.
<code>@Configuration
public class MappingConfig {
@Autowired
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) throws NoSuchMethodException {
RequestMappingInfo info = RequestMappingInfo.paths("/reg/{id}")
.methods(RequestMethod.GET)
.build();
Method method = UserHandler.class.getMethod("getUser", Long.class);
mapping.registerMapping(info, handler, method);
}
}
</code>Result of the test request:
2.2 Adding Constraints
You can add additional request constraints such as required parameters or headers.
<code>RequestMappingInfo info = RequestMappingInfo.paths("/reg/{id}")
.params("type=1")
.build();
</code>This mapping only matches URLs like http://localhost:8888/reg/666?type=1 .
<code>RequestMappingInfo info = RequestMappingInfo.paths("/reg/{id}")
.headers("x-token")
.build();
</code>The request must contain the header x-token (value can be any).
2.3 Custom Matching Conditions
If built‑in metadata is insufficient, you can implement a custom RequestCondition to define your own matching logic.
<code>public class PackRequestCondition implements RequestCondition<PackRequestCondition> {
public PackRequestCondition combine(PackRequestCondition other) { return this; }
public PackRequestCondition getMatchingCondition(HttpServletRequest request) {
if (SecuritytHolder.hasRole(request)) {
return this;
}
return null;
}
public int compareTo(PackRequestCondition other, HttpServletRequest request) { return 0; }
}
</code> <code>RequestMappingInfo info = RequestMappingInfo.paths("/reg/{id}")
.customCondition(new PackRequestCondition())
.build();
</code>2.4 Unregistering Endpoints
Maintain a map of all dynamically registered mappings and provide an endpoint to unregister them.
<code>public static final Map<String, RequestMappingInfo> MAPPINGS = new ConcurrentHashMap<>();
// registration example
MAPPINGS.putIfAbsent("1", info);
mapping.registerMapping(info, handler, method);
</code> <code>@RestController
@RequestMapping("/mappings")
public class MappingController {
private final RequestMappingHandlerMapping mapping;
@GetMapping("/unregister/{key}")
public ResponseEntity<String> unregister(@PathVariable String key) {
this.mapping.unregisterMapping(MAPPINGS.get(key));
return ResponseEntity.ok("success");
}
}
</code>The above code completes the management of dynamic registration and removal of Spring Boot controller endpoints.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.