Spring MVC 配置器:WebMvcConfigurer 详解、应用场景和示例代码

一、概述

1.1 什么是 WebMvcConfigurer?

WebMvcConfigurer 是 Spring MVC 提供的一个接口,允许开发者在不使用 XML 配置的情况下,通过 Java 配置方式自定义 Spring MVC 的行为。它提供了多个默认方法,用于配置各种 MVC 相关组件。

1.2 核心作用

  • 替代传统的 XML 配置(如 mvc-config.xml)

  • 以类型安全的方式配置 Spring MVC

  • 提供细粒度的 MVC 配置控制

  • 支持跨域、拦截器、视图解析器等配置

二、核心配置方法详解

2.1 跨域配置(CORS)

java 复制代码
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 全局 CORS 配置
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:3000", "https://example.com")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
        
        // 多个 CORS 配置
        registry.addMapping("/public/**")
                .allowedOrigins("*")
                .allowedMethods("GET");
    }
}

2.2 拦截器配置

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private AuthenticationInterceptor authInterceptor;
    @Autowired
    private LoggingInterceptor loggingInterceptor;
    
    /**
     * 添加拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 1. 日志拦截器 - 拦截所有请求
        registry.addInterceptor(loggingInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/health", "/favicon.ico");
        
        // 2. 认证拦截器 - 拦截 API 请求
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/auth/login", "/api/auth/register");
        
        // 3. 顺序配置(可选,按添加顺序执行)
        // registry.addInterceptor(new FirstInterceptor()).order(1);
        // registry.addInterceptor(new SecondInterceptor()).order(2);
    }
}

/**
 * 自定义拦截器示例
 */
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (!isValidToken(token)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, 
                          HttpServletResponse response, 
                          Object handler, 
                          ModelAndView modelAndView) {
        // 请求处理后调用
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) {
        // 请求完成后的清理工作
    }
}

2.3 视图控制器和视图解析器

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 配置视图控制器(用于简单的页面映射)
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 将URL路径映射到视图名称
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/error/403").setViewName("error/403");
        
        // 设置状态码
        registry.addStatusController("/not-found", HttpStatus.NOT_FOUND);
    }
    
    /**
     * 配置视图解析器
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        // 使用 Thymeleaf
        registry.jsp("/WEB-INF/views/", ".jsp");
        
        // 或者配置多个解析器
        // registry.enableContentNegotiation(new MappingJackson2JsonView());
        // registry.freeMarker().prefix("classpath:/templates/").suffix(".ftl");
    }
}

2.4 静态资源处理

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 配置静态资源映射
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 1. WebJars 资源
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/")
                .resourceChain(true)
                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
        
        // 2. 自定义静态资源
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/", "file:/opt/app/static/")
                .setCachePeriod(3600) // 缓存1小时
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS));
        
        // 3. 上传文件访问路径
        registry.addResourceHandler("/uploads/**")
                .addResourceLocations("file:/var/uploads/");
        
        // 4. 图标文件
        registry.addResourceHandler("/favicon.ico")
                .addResourceLocations("classpath:/static/favicon.ico");
    }
}

2.5 消息转换器配置

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 配置消息转换器
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 1. 添加 FastJson 转换器
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
            SerializerFeature.PrettyFormat,
            SerializerFeature.WriteMapNullValue,
            SerializerFeature.WriteDateUseDateFormat
        );
        fastConverter.setFastJsonConfig(fastJsonConfig);
        fastConverter.setSupportedMediaTypes(
            Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8)
        );
        converters.add(0, fastConverter);
        
        // 2. 添加 XML 转换器
        converters.add(new Jaxb2RootElementHttpMessageConverter());
    }
    
    /**
     * 扩展消息转换器(不覆盖默认转换器)
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 修改现有转换器的配置
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jsonConverter = 
                    (MappingJackson2HttpMessageConverter) converter;
                ObjectMapper objectMapper = jsonConverter.getObjectMapper();
                objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
            }
        }
    }
}

2.6 参数解析器

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 添加自定义参数解析器
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        // 1. 自定义用户信息解析器
        resolvers.add(new CurrentUserArgumentResolver());
        
        // 2. Pageable 参数解析器
        resolvers.add(new PageableHandlerMethodArgumentResolver());
        
        // 3. 日期参数解析器
        resolvers.add(new DateArgumentResolver());
    }
}

/**
 * 自定义参数解析器示例
 */
@Component
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
    
    @Autowired
    private UserService userService;
    
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(CurrentUser.class) 
            && parameter.getParameterType().equals(User.class);
    }
    
    @Override
    public Object resolveArgument(MethodParameter parameter, 
                                 ModelAndViewContainer mavContainer,
                                 NativeWebRequest webRequest, 
                                 WebDataBinderFactory binderFactory) {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        String token = request.getHeader("Authorization");
        return userService.getUserByToken(token);
    }
}

/**
 * 自定义注解
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
}

2.7 返回值处理器

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 添加自定义返回值处理器
     */
    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        handlers.add(new GlobalResponseWrapper());
    }
}

/**
 * 全局响应包装器
 */
@Component
public class GlobalResponseWrapper implements HandlerMethodReturnValueHandler {
    
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return !returnType.getParameterType().equals(ResponseEntity.class);
    }
    
    @Override
    public void handleReturnValue(Object returnValue,
                                 MethodParameter returnType,
                                 ModelAndViewContainer mavContainer,
                                 NativeWebRequest webRequest) throws Exception {
        
        ApiResponse<Object> response = ApiResponse.success(returnValue);
        
        // 设置响应
        HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
        servletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
        
        ObjectMapper mapper = new ObjectMapper();
        servletResponse.getWriter().write(mapper.writeValueAsString(response));
        mavContainer.setRequestHandled(true);
    }
}

/**
 * 统一响应格式
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {
    private Integer code;
    private String message;
    private T data;
    private Long timestamp;
    
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "success", data, System.currentTimeMillis());
    }
}

2.8 路径匹配配置

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 配置路径匹配选项
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // 1. 是否使用后缀模式匹配
        configurer.setUseSuffixPatternMatch(false);
        
        // 2. 是否使用尾随斜杠匹配
        configurer.setUseTrailingSlashMatch(true);
        
        // 3. 注册自定义路径匹配器
        configurer.addPathPrefix("/api", 
            HandlerTypePredicate.forAnnotation(RestController.class));
        
        // 4. 配置 URL 路径帮助器
        configurer.setUrlPathHelper(new UrlPathHelper());
        
        // 5. 配置路径匹配器
        AntPathMatcher pathMatcher = new AntPathMatcher();
        pathMatcher.setCaseSensitive(false);
        configurer.setPathMatcher(pathMatcher);
    }
}

2.9 异步支持配置

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 配置异步请求支持
     */
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        // 设置超时时间(毫秒)
        configurer.setDefaultTimeout(30000);
        
        // 设置线程池
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setMaxPoolSize(50);
        taskExecutor.setQueueCapacity(100);
        taskExecutor.setThreadNamePrefix("async-");
        taskExecutor.initialize();
        configurer.setTaskExecutor(taskExecutor);
        
        // 设置可调用的拦截器
        configurer.registerCallableInterceptors(new TimeoutCallableProcessingInterceptor());
        
        // 设置 DeferredResult 拦截器
        configurer.registerDeferredResultInterceptors(new DeferredResultProcessingInterceptor() {
            @Override
            public <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) {
                // 在并发处理前调用
            }
        });
    }
}

2.10 内容协商配置

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 配置内容协商
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        // 1. 基于 URL 后缀
        configurer.favorPathExtension(true)
                  .favorParameter(false)
                  .parameterName("format")
                  .ignoreAcceptHeader(false)
                  .useRegisteredExtensionsOnly(false)
                  .defaultContentType(MediaType.APPLICATION_JSON)
                  .mediaType("json", MediaType.APPLICATION_JSON)
                  .mediaType("xml", MediaType.APPLICATION_XML)
                  .mediaType("html", MediaType.TEXT_HTML);
        
        // 2. 基于请求头
        configurer.defaultContentTypeStrategy(new HeaderContentNegotiationStrategy());
    }
}

2.11 验证器和格式化

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 添加自定义验证器
     */
    @Override
    public Validator getValidator() {
        LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.setValidationMessageSource(messageSource());
        return validator;
    }
    
    /**
     * 添加格式化器
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        // 日期格式化
        registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
        registry.addFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
        
        // 自定义格式化器
        registry.addFormatterForFieldType(LocalDate.class, new TemporalAccessorFormatter());
        
        // 添加转换器
        registry.addConverter(new StringToUserConverter());
    }
    
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = 
            new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
}

/**
 * 自定义转换器示例
 */
@Component
public class StringToUserConverter implements Converter<String, User> {
    
    @Autowired
    private UserRepository userRepository;
    
    @Override
    public User convert(String userId) {
        return userRepository.findById(Long.parseLong(userId))
                .orElseThrow(() -> new IllegalArgumentException("Invalid user ID"));
    }
}

三、实际应用场景

3.1 微服务 API 网关配置

java 复制代码
@Configuration
public class ApiGatewayConfig implements WebMvcConfigurer {
    
    @Autowired
    private JwtTokenInterceptor jwtInterceptor;
    @Autowired
    private RateLimitInterceptor rateLimitInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 1. 速率限制拦截器
        registry.addInterceptor(rateLimitInterceptor)
                .addPathPatterns("/api/**")
                .order(1);
        
        // 2. JWT 认证拦截器
        registry.addInterceptor(jwtInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/auth/**")
                .order(2);
        
        // 3. 日志拦截器
        registry.addInterceptor(new RequestLogInterceptor())
                .addPathPatterns("/**")
                .order(3);
    }
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .maxAge(3600);
    }
    
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 统一响应格式
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        converter.setObjectMapper(objectMapper);
        converters.add(converter);
    }
}

3.2 前后端分离项目配置

java 复制代码
@Configuration
public class FrontendBackendSeparationConfig implements WebMvcConfigurer {
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 前端静态资源
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/")
                .setCachePeriod(31536000); // 一年缓存
        
        // 单页应用:所有未匹配的路径返回 index.html
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/index.html")
                .resourceChain(true)
                .addResolver(new PathResourceResolver() {
                    @Override
                    protected Resource getResource(String resourcePath, 
                                                  Resource location) throws IOException {
                        Resource requestedResource = location.createRelative(resourcePath);
                        return requestedResource.exists() && requestedResource.isReadable() 
                            ? requestedResource 
                            : new ClassPathResource("/static/index.html");
                    }
                });
    }
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowCredentials(true)
                .maxAge(3600);
    }
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 重定向到前端路由
        registry.addViewController("/").setViewName("forward:/index.html");
        registry.addViewController("/login").setViewName("forward:/index.html");
        registry.addViewController("/dashboard/**").setViewName("forward:/index.html");
    }
}

3.3 文件上传下载配置

java 复制代码
@Configuration
public class FileUploadConfig implements WebMvcConfigurer {
    
    /**
     * 配置文件上传
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 文件访问路径映射
        registry.addResourceHandler("/files/**")
                .addResourceLocations("file:/var/uploads/")
                .setCachePeriod(3600);
    }
    
    /**
     * 配置文件上传解析器
     */
    @Bean
    public MultipartResolver multipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setMaxUploadSize(10485760); // 10MB
        resolver.setMaxUploadSizePerFile(5242880); // 5MB
        resolver.setDefaultEncoding("UTF-8");
        resolver.setResolveLazily(true); // 延迟解析
        return resolver;
    }
    
    /**
     * 添加参数解析器处理文件上传
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new MultipartFileArgumentResolver());
    }
}

/**
 * 文件上传参数解析器
 */
@Component
public class MultipartFileArgumentResolver implements HandlerMethodArgumentResolver {
    
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(UploadFile.class) 
            && (parameter.getParameterType().equals(MultipartFile.class) 
                || parameter.getParameterType().equals(MultipartFile[].class));
    }
    
    @Override
    public Object resolveArgument(MethodParameter parameter, 
                                 ModelAndViewContainer mavContainer,
                                 NativeWebRequest webRequest, 
                                 WebDataBinderFactory binderFactory) {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        if (request instanceof MultipartHttpServletRequest) {
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            UploadFile uploadFile = parameter.getParameterAnnotation(UploadFile.class);
            String paramName = uploadFile.value();
            
            if (parameter.getParameterType().equals(MultipartFile.class)) {
                return multipartRequest.getFile(paramName);
            } else if (parameter.getParameterType().equals(MultipartFile[].class)) {
                return multipartRequest.getFiles(paramName);
            }
        }
        return null;
    }
}

四、高级配置和最佳实践

4.1 多配置类组合

java 复制代码
// 主配置类
@Configuration
@EnableWebMvc
@Import({ SecurityConfig.class, SwaggerConfig.class, CacheConfig.class })
public class MainWebConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 全局 CORS 配置
    }
}

// 安全相关配置
@Configuration
public class SecurityConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 安全拦截器
    }
}

// API 文档配置
@Configuration
public class SwaggerConfig implements WebMvcConfigurer {
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // Swagger UI 资源
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // API 文档重定向
        registry.addRedirectViewController("/api-docs", "/swagger-ui.html");
    }
}

4.2 条件化配置

java 复制代码
@Configuration
public class ConditionalWebConfig implements WebMvcConfigurer {
    
    @Value("${app.cors.enabled:false}")
    private boolean corsEnabled;
    
    @Value("${app.cache.static-resources:true}")
    private boolean cacheStaticResources;
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        if (corsEnabled) {
            registry.addMapping("/api/**")
                    .allowedOrigins("*")
                    .allowedMethods("*");
        }
    }
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        ResourceHandlerRegistration registration = registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/");
        
        if (cacheStaticResources) {
            registration.setCachePeriod(31536000) // 一年
                        .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
        }
    }
}

4.3 性能优化配置

java 复制代码
@Configuration
public class PerformanceConfig implements WebMvcConfigurer {
    
    /**
     * 配置 GZip 压缩
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorParameter(false)
                  .ignoreAcceptHeader(false)
                  .defaultContentType(MediaType.APPLICATION_JSON);
    }
    
    /**
     * 启用 HTTP 缓存
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/")
                .setCachePeriod(31536000)
                .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic())
                .resourceChain(true)
                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
    }
    
    /**
     * 配置异步处理
     */
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setDefaultTimeout(30000);
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 2);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-executor-");
        executor.initialize();
        configurer.setTaskExecutor(executor);
    }
}

五、常见问题与解决方案

5.1 配置冲突问题

java 复制代码
@Configuration
public class ConflictResolutionConfig implements WebMvcConfigurer {
    
    /**
     * 解决多个配置类冲突
     * 使用 @Order 注解指定优先级
     */
    @Configuration
    @Order(1)
    public static class PrimaryConfig implements WebMvcConfigurer {
        // 高优先级配置
    }
    
    @Configuration
    @Order(2)
    public static class SecondaryConfig implements WebMvcConfigurer {
        // 低优先级配置
    }
}

5.2 静态资源访问404

java 复制代码
@Configuration
public class StaticResourceConfig implements WebMvcConfigurer {
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 正确配置静态资源路径
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("classpath:/static/", "file:/opt/app/static/")
                .setCachePeriod(3600);
        
        // 确保没有拦截器拦截静态资源
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/api/**")
                .excludePathPatterns("/resources/**", "/static/**");
    }
}

5.3 跨域配置不生效

java 复制代码
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 确保路径匹配正确
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("*")
                .allowedHeaders("*")
                .allowCredentials(true)  // 如果需要凭证
                .maxAge(3600);
    }
    
    /**
     * 同时配置 Filter 级别的 CORS(如果需要)
     */
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:3000");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        bean.setOrder(0); // 设置最高优先级
        return bean;
    }
}

六、总结

6.1 WebMvcConfigurer 的优势

  1. 类型安全:Java 配置相比 XML 更加类型安全

  2. 灵活性强:可以根据条件动态配置

  3. 可维护性高:配置逻辑集中,便于管理

  4. 功能全面:覆盖 Spring MVC 所有配置需求

  5. 易于测试:配置类易于单元测试

6.2 选择建议

  • 简单项目 :使用 @EnableWebMvc + WebMvcConfigurer

  • 复杂项目:拆分多个配置类,按功能模块组织

  • Spring Boot 项目 :使用 WebMvcConfigurer 而非 WebMvcConfigurationSupport

  • 需要完全控制 :继承 WebMvcConfigurationSupport

6.3 注意事项

  1. 避免在多个配置类中重复配置同一功能

  2. 注意配置的加载顺序

  3. 生产环境需要合理配置缓存策略

  4. 安全相关的配置要谨慎

  5. 定期审查和优化配置

相关推荐
LaLaLa_OvO6 分钟前
spring boot2.0 里的 javax.validation.Constraint 加入 service
java·数据库·spring boot
Solar20257 分钟前
构建高可靠性的机械设备企业数据采集系统:架构设计与实践指南
java·大数据·运维·服务器·架构
慧一居士10 分钟前
jdk1.8 及之后的新版本介绍,新特性示例总结
java
eybk12 分钟前
拖放pdf转化为txt文件多进程多线程合并分词版
java·python·pdf
BD_Marathon12 分钟前
SpringMVC入门案例及其工作流程
spring
D_FW18 分钟前
数据结构第四章:串
java·开发语言
Tao____18 分钟前
物联网平台二开
java·网络·物联网·mqtt·网络协议
Dylan的码园18 分钟前
JAVA中对象的几种比较
java·开发语言
emma_dd21 分钟前
final关键字
java
SimonKing23 分钟前
J人程序员的用屏技巧:软硬结合,让编码效率起飞
java·后端·程序员