It turns out the issue was the component scanning configuration. Even though the @ComponentScan annotation was used, the controller was under a separate package, so Spring never included it in the dispatcher.
Adding @ComponentScan(basePackages = "com.my.controller")) solved my issue.