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实现都会在应用启动时被收集和执行,从而在拦截器链中注册不同的拦截器,这些拦截器将按照配置的优先级处理应用的所有请求。

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

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

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more