DEV Community

Truman
Truman

Posted on

WEB MVC的一些基础配置

先上代码

@Configuration
@ConditionalOnClass(org.springframework.web.servlet.config.annotation.WebMvcConfigurer.class)
public class DefaultWebMvcConfig implements WebMvcConfigurer {

    // 注入用于处理JSON转换的消息转换器
    @Autowired
    private MappingJackson2HttpMessageConverter jackson2HttpMessageConverter;

    // 注入自定义的拦截器注册器,允许在运行时动态添加拦截器
    @Autowired
    private ObjectProvider<InterceptorRegistryCustomizer> interceptorRegistryCustomizers;

    // 配置消息转换器列表
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 添加字节数组消息转换器,处理字节数组类型的请求和响应
        converters.add(new ByteArrayHttpMessageConverter());
        // 添加字符串消息转换器,处理字符串类型的请求和响应
        converters.add(new StringHttpMessageConverter());
        // 添加资源消息转换器,处理文件资源类型的请求和响应
        converters.add(new ResourceHttpMessageConverter());
        // 添加表单消息转换器,处理表单类型的数据
        converters.add(new AllEncompassingFormHttpMessageConverter());
        // 添加Jackson消息转换器,处理JSON类型的请求和响应
        converters.add(jackson2HttpMessageConverter);
    }

    // 创建并配置Jackson消息转换器Bean
    @Bean
    public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper mapper = new ObjectMapper();

        // 配置ObjectMapper,忽略未知属性,设置日期格式和时区,指定命名策略
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN));
        mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        mapper.setPropertyNamingStrategy(new SpeedNamingStrategy());

        // 将配置后的ObjectMapper设置到消息转换器中
        converter.setObjectMapper(mapper);

        return converter;
    }

    /**
     * 设置静态资源文件目录
     *
     * @param registry 资源处理注册器
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/resources/") // 设置资源目录为resources
                .addResourceLocations("classpath:/static/") // 设置资源目录为static
                .addResourceLocations("classpath:/META-INF/resources/") // 设置资源目录为META-INF/resources
                .addResourceLocations("classpath:/public/"); // 设置资源目录为public
    }

    /**
     * 添加自定义的参数解析器,用于解析特定的参数(如Token)
     *
     * @param argumentResolvers 参数解析器列表
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new RequestContextArgumentResolver()); // 添加自定义的参数解析器
    }

    /**
     * 添加自定义拦截器
     *
     * @param registry 拦截器注册器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 通过自定义的拦截器注册器动态添加拦截器
        for (InterceptorRegistryCustomizer interceptorRegistryCustomizer : interceptorRegistryCustomizers) {
            interceptorRegistryCustomizer.custom(registry);
        }
    }

    @Configuration
    @ConditionalOnClass({RedisTemplate.class, InterceptorRegistry.class})
    @EnableConfigurationProperties(ApiControlProperties.class)
    static class InspectConfiguration {
        // 配置ApiManager Bean,负责管理API统计数据
        @Bean
        public ApiManager handlerMethodRedisRegistry(
                RedisTemplate<String, Object> redisTemplate,
                RequestMappingHandlerMapping requestMappingHandlerMapping) {
            return new ApiManager(redisTemplate, requestMappingHandlerMapping);
        }

        // 配置ApiControlInterceptor的自定义拦截器注册器,添加拦截器到注册器
        @Bean
        InterceptorRegistryCustomizer apiControlInterceptorCustomizer(ApiControlProperties apiControlProperties,
                                                                      @Autowired(required = false) CacheControl cacheControl,
                                                                      ApplicationContext applicationContext){
            return registry -> registry.addInterceptor(new ApiControlInterceptor(apiControlProperties, cacheControl, applicationContext))
                    .order(Ordered.HIGHEST_PRECEDENCE + 11) // 设置拦截器的优先级
                    .addPathPatterns("/**"); // 设置拦截的路径模式
        }

    }

    @Configuration
    @ConditionalOnClass({InterceptorRegistry.class})
    @EnableConfigurationProperties(ApiGuardProperties.class)
    static class ApiGuardConfiguration {
        // 配置ApiGuardInterceptor的自定义拦截器注册器,添加拦截器到注册器
        @Bean
        InterceptorRegistryCustomizer apiGuardInterceptorCustomizer(ApiGuardProperties apiGuardProperties){
            return registry -> registry.addInterceptor(new ApiGuardInterceptor(apiGuardProperties))
                    .order(Ordered.HIGHEST_PRECEDENCE + 10) // 设置拦截器的优先级
                    .addPathPatterns("/**"); // 设置拦截的路径模式
        }
    }

    // 拦截器注册器自定义接口,允许动态注册拦截器
    public interface InterceptorRegistryCustomizer {
        void custom(InterceptorRegistry registry);
    }

    @ConditionalOnClass({SentinelWebTotalInterceptor.class, InterceptorRegistry.class})
    @Configuration
    static class SentinelConfig {

        // 配置Sentinel的自定义拦截器注册器,添加拦截器到注册器
        @Bean
        InterceptorRegistryCustomizer sentinelWebTotalInterceptorCustomizer(){
            return registry -> {
                SentinelWebMvcTotalConfig conf = new SentinelWebMvcTotalConfig();
                conf.setTotalResourceName(SpringContextHolder.getApplicationName()); // 设置总资源名
                conf.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置默认的阻塞异常处理器
                SentinelWebTotalInterceptor interceptor = new SentinelWebTotalInterceptor(conf);

                registry.addInterceptor(interceptor) // 添加拦截器
                        .order(Ordered.HIGHEST_PRECEDENCE) // 设置拦截器的优先级
                        .addPathPatterns("/**"); // 设置拦截的路径模式
            };
        }

    }
}

Enter fullscreen mode Exit fullscreen mode

详细解释

  1. DefaultWebMvcConfig 类
    • 这是一个Spring的配置类,使用@Configuration@ConditionalOnClass注解,条件加载WebMvc配置。
    • configureMessageConverters方法配置了一组消息转换器,用于处理不同类型的请求和响应数据格式,如字节数组、字符串、资源和JSON。
    • jackson2HttpMessageConverter是一个Bean方法,配置了一个Jackson消息转换器,用于将Java对象与JSON之间进行转换,包含了日期格式、时区和命名策略的定制。
    • addResourceHandlers方法配置了静态资源的处理路径,支持从多个目录加载资源。
    • addArgumentResolvers方法配置了一个自定义的参数解析器,用于在请求处理时解析自定义参数类型。
    • addInterceptors方法通过自定义的拦截器注册器动态添加拦截器,允许扩展和定制拦截器的行为。
  2. InspectConfiguration 类
    • 这个内部配置类用于配置API管理和控制的相关Bean。
    • handlerMethodRedisRegistry方法定义了一个ApiManager Bean,负责管理API统计数据,将其存储在Redis中,并与其他组件共享数据。
    • apiControlInterceptorCustomizer方法配置了一个ApiControlInterceptor拦截器,用于对API请求进行监控和控制。
  3. ApiGuardConfiguration 类
    • 这个配置类配置了API安全控制的相关拦截器。
    • apiGuardInterceptorCustomizer方法配置了ApiGuardInterceptor拦截器,用于对API请求进行安全控制。
  4. SentinelConfig 类
    • 这个配置类配置了基于Sentinel的API请求拦截器。
    • sentinelWebTotalInterceptorCustomizer方法配置了SentinelWebTotalInterceptor拦截器,用于对API请求进行流量控制和异常处理。
  5. InterceptorRegistryCustomizer 接口
    • 这个接口允许自定义拦截器的注册,提供了灵活的拦截器配置机制。

InterceptorRegistryCustomizer接口和其他代码的关系

1. 接口定义与实现

public interface InterceptorRegistryCustomizer {
    void custom(InterceptorRegistry registry);
}
Enter fullscreen mode Exit fullscreen mode
  • 作用:InterceptorRegistryCustomizer接口定义了一个方法custom(InterceptorRegistry registry),该方法允许实现者自定义对InterceptorRegistry(拦截器注册表)的操作。通过实现这个接口,开发者可以在运行时动态地向Spring的拦截器链中添加、移除或配置拦截器。

2. 与配置类的关系

DefaultWebMvcConfig类中,通过注入一个ObjectProvider对象,Spring允许多个InterceptorRegistryCustomizer实现被注入并按需执行:

@Autowired
private ObjectProvider<InterceptorRegistryCustomizer> interceptorRegistryCustomizers;
Enter fullscreen mode Exit fullscreen mode

DefaultWebMvcConfig类的addInterceptors方法中,使用了这个interceptorRegistryCustomizers来动态执行所有的InterceptorRegistryCustomizer实现:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    for (InterceptorRegistryCustomizer interceptorRegistryCustomizer : interceptorRegistryCustomizers) {
        interceptorRegistryCustomizer.custom(registry);
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 作用:通过这种设计,DefaultWebMvcConfig类可以收集并应用所有已定义的InterceptorRegistryCustomizer实现,以动态地、按顺序地将不同的拦截器添加到Spring的拦截器链中。

3. 具体实现与使用

在内部配置类InspectConfigurationApiGuardConfiguration
中,定义了具体的InterceptorRegistryCustomizer实现:

@Bean
InterceptorRegistryCustomizer apiControlInterceptorCustomizer(ApiControlProperties apiControlProperties,
                                                               @Autowired(required = false) CacheControl cacheControl,
                                                               ApplicationContext applicationContext) {
    return registry -> registry.addInterceptor(new ApiControlInterceptor(apiControlProperties, cacheControl, applicationContext))
            .order(Ordered.HIGHEST_PRECEDENCE + 11).addPathPatterns("/**");
}

Enter fullscreen mode Exit fullscreen mode
@Bean
InterceptorRegistryCustomizer apiGuardInterceptorCustomizer(ApiGuardProperties apiGuardProperties) {
    return registry -> registry.addInterceptor(new ApiGuardInterceptor(apiGuardProperties))
            .order(Ordered.HIGHEST_PRECEDENCE + 10).addPathPatterns("/**");
}
Enter fullscreen mode Exit fullscreen mode
  • 关系:
    • 这些具体实现负责将自定义的拦截器(例如ApiControlInterceptor和ApiGuardInterceptor)添加到Spring的拦截器链中。
    • 每个实现都通过custom方法将一个新的拦截器注册到InterceptorRegistry中,并且可以自定义拦截器的顺序和拦截路径。

4. 动态性与扩展性

  • InterceptorRegistryCustomizer接口提供了一种高度灵活和可扩展的机制,允许开发者在不修改核心配置类的情况下,通过定义多个实现类来自定义拦截器的注册逻辑。
  • 这种设计模式遵循了依赖注入和面向接口编程的原则,使得代码更具扩展性和模块化,符合Spring框架的设计哲学。

5. 总结

  • InterceptorRegistryCustomizer接口 是连接配置类(如DefaultWebMvcConfig)和具体拦截器实现之间的桥梁。
  • 它使得拦截器的添加与配置过程更加动态化和可定制,满足了复杂应用场景下对拦截器灵活配置的需求。
  • 每个具体的InterceptorRegistryCustomizer实现都会在应用启动时被收集和执行,从而在拦截器链中注册不同的拦截器,这些拦截器将按照配置的优先级处理应用的所有请求。

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.