一、概述
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 的优势
-
类型安全:Java 配置相比 XML 更加类型安全
-
灵活性强:可以根据条件动态配置
-
可维护性高:配置逻辑集中,便于管理
-
功能全面:覆盖 Spring MVC 所有配置需求
-
易于测试:配置类易于单元测试
6.2 选择建议
-
简单项目 :使用
@EnableWebMvc+WebMvcConfigurer -
复杂项目:拆分多个配置类,按功能模块组织
-
Spring Boot 项目 :使用
WebMvcConfigurer而非WebMvcConfigurationSupport -
需要完全控制 :继承
WebMvcConfigurationSupport
6.3 注意事项
-
避免在多个配置类中重复配置同一功能
-
注意配置的加载顺序
-
生产环境需要合理配置缓存策略
-
安全相关的配置要谨慎
-
定期审查和优化配置