如何在处理器映射注册前自动配置控制器?自定义ErrorController实例化优先级问题
我之前也遇到过类似的场景,单纯用@AutoConfigureBefore有时候确实达不到预期,因为Spring Boot的自动配置顺序还有一些隐藏的依赖关系,比如ErrorMvcAutoConfiguration本身就标注了@AutoConfigureAfter(WebMvcAutoConfiguration.class),所以直接指定@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)其实会让你的配置在WebMvc之后加载,这就导致handler mapping的初始化还是先执行了。下面给你一套可靠的解决方案:
1. 调整自动配置的优先级与顺序
首先,你的自定义自动配置类需要确保是最早被加载的一批配置,同时明确在WebMvc自动配置之前执行:
@Configuration(proxyBeanMethods = false) // 设置最高优先级,确保比所有内置自动配置先加载 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 明确在WebMvc和ErrorMvc自动配置之前执行 @AutoConfigureBefore({WebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class}) public class CustomErrorAutoConfiguration { // 定义你的自定义ErrorController,强制非懒加载 @Bean(name = "customErrorController") @Lazy(false) public CustomErrorController customErrorController() { return new CustomErrorController(); } // 注册BeanPostProcessor修改RequestMappingHandlerMapping的依赖关系 @Bean public RequestMappingHandlerMappingDependsOnProcessor requestMappingHandlerMappingDependsOnProcessor() { return new RequestMappingHandlerMappingDependsOnProcessor(); } }
2. 用BeanPostProcessor强制调整bean初始化顺序
核心问题在于AbstractHandlerMethodMapping(比如RequestMappingHandlerMapping)的initHandlerMethods是在它自身的afterPropertiesSet阶段执行的,而这个阶段默认没有依赖你的CustomErrorController。我们可以通过BeanPostProcessor修改它的bean定义,让它依赖你的自定义ErrorController,这样Spring会先初始化你的bean,再处理handler mapping:
import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.util.Arrays; public class RequestMappingHandlerMappingDependsOnProcessor implements BeanPostProcessor, ApplicationContextAware { private ConfigurableListableBeanFactory beanFactory; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.beanFactory = (ConfigurableListableBeanFactory) applicationContext; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 针对所有RequestMappingHandlerMapping实例修改依赖 if (bean instanceof RequestMappingHandlerMapping) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); String[] currentDependsOn = beanDefinition.getDependsOn(); // 合并原有依赖与新依赖 String[] newDependsOn; if (currentDependsOn == null) { newDependsOn = new String[]{"customErrorController"}; } else { newDependsOn = Arrays.copyOf(currentDependsOn, currentDependsOn.length + 1); newDependsOn[currentDependsOn.length] = "customErrorController"; } beanDefinition.setDependsOn(newDependsOn); } return bean; } }
3. 确保自动配置类被Spring Boot发现
最后,一定要让Spring Boot识别到你的自定义自动配置类:在项目的src/main/resources/META-INF/spring目录下创建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,内容为你的自动配置类的全限定名,比如:
com.yourpackage.autoconfigure.CustomErrorAutoConfiguration
为什么之前的@AutoConfigureBefore没生效?
主要原因有两个:
ErrorMvcAutoConfiguration本身依赖WebMvcAutoConfiguration,所以直接指定@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)并不会让你的配置在WebMvc之前加载;- 即使你的自动配置类先加载,
CustomErrorController的bean初始化顺序也不一定早于RequestMappingHandlerMapping,因为Spring默认只处理显式的bean依赖,而handler mapping和你的ErrorController之间没有天然依赖关系。
通过上面的步骤,既保证了自动配置的加载顺序,又强制调整了bean的初始化依赖,就能确保CustomErrorController在initHandlerMethods执行前完全实例化了。
内容的提问来源于stack exchange,提问作者destan

