解决Long类型到web端失精度引发的API接口空响应

Spring MVC 拦截器问题
本文探讨了Spring MVC项目中出现的空响应问题,分析了继承WebMvcConfigurationSupport类导致拦截器未注册的原因,并提供了三种解决方案:添加@EnableWebMvc注解、实现addInterceptors方法或实现WebMvcConfigurer接口。

事故代码
@Configuration
public class LongToStringJsonConfig extends WebMvcConfigurationSupport {

    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(jackson2HttpMessageConverter);
    }
}
  • 我最开始项目里,单纯解决Long转String需求时,写的是如上代码。好吧,诚实一点,抄的是以上代码。然而这段代码导致我所有的接口,都是空响应。
触发的问题点
  • 1,Spring的request和response的stream只能读一次,再次读取的时候,stream就为空了。
  • 2,通过debug,项目引入了一个底层包,经过了一个filter。而这个filter做了一件事:
        ContentCachingResponseWrapper wrapperResponse = new ContentCachingResponseWrapper(response);
        chain.doFilter(wrapperRequest, wrapperResponse);
  • filter将response包入到了ContentCachingResponseWrapper里边
  • 3,在底层包里,后续有一个interceptor 在afterCompletion方法中,会将ContentCachingResponseWrapper的内容写出去。
  • 4,再次debug,发现写出ContentCachingResponseWrapper的interceptor并未被注入。
解决空响应
  • 最开始的尝试chain.doFilter(wrapperRequest, wrapperResponse);将filter的这行操作,换成chain.doFilter(wrapperRequest, response);此时,接口正常响应。问题是:我项目中的空响应解决了,但底层包里interceptor想要拦截读取一些信息记录的需求实现就被破坏了。PS:虽然我连这个interceptor都没注册上吧。 所以,这个方案,是行不通的。 问题又指向于:为什么没有触发后续的interceptor去调用responseWrapper.copyBodyToResponse()方法写出响应。
  • 慢慢回顾,发现就是多了我新增的一个类,就出现空响应。去掉的话,就是正常响应。仔细看,我的类没什么特别,唯一特别的就是 extends WebMvcConfigurationSupport。 那么WebMvcConfigurationSupport被继承,会发生啥呢?
为什么写出response的拦截器没注册上
  • 我的config代码唯一的不通,就是继承了WebMvcConfigurationSupport。那么这个WebMvcConfigurationSupport是怎么工作的呢?
  • 仔细查代码,或者debug,就可以发现,在WebMvcConfigurationSupport里有个接口获取拦截器:
	/**
	 * Provide access to the shared handler interceptors used to configure
	 * {@link HandlerMapping} instances with.
	 * <p>This method cannot be overridden; use {@link #addInterceptors} instead.
	 */
	protected final Object[] getInterceptors() {
		if (this.interceptors == null) {
			InterceptorRegistry registry = new InterceptorRegistry();
			addInterceptors(registry);
			registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
			registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
			this.interceptors = registry.getInterceptors();
		}
		return this.interceptors.toArray();
	}
  • 这个接口不可以被重写。而中间有个addInterceptors(registry)方法,所以,interceptor要注册上,至少也得是实现了这个addInterceptors(registry)方法的。 PS:这里还可以看一下WebMvcConfigurationSupport类的作用说明:

This is the main class providing the configuration behind the MVC Java config. It is typically imported by adding {@link EnableWebMvc @EnableWebMvc} to an application {@link Configuration @Configuration} class. An alternative more advanced option is to extend directly from this class and override methods as necessary, remembering to add {@link Configuration @Configuration} to the subclass and {@link Bean @Bean} to overridden {@link Bean @Bean} methods.

  • 在WebMvcConfigurationSupport里,可以看到的是It is typically imported by adding {@link EnableWebMvc @EnableWebMvc} to an application {@link Configuration @Configuration} class。所以,第一个解决拦截器未注册上的方案,也就出来了。就是在我的类上,除了@Configuration外,再加一个注解@EnableWebMvc。 至此,空响应问题解决。第二个解决方案,在我的类中,实现addInterceptors(registry)的方法注入底层包里可以打印出response的interceptor。
  • 通过WebMvcConfigurationSupport 找到@EnableWebMvc和接口WebMvcConfigurer。仔细看WebMvcConfigurer接口的定义。第三个解决方案出来了,让我的类不再继承WebMvcConfigurationSupport类,而改为implement WebMvcConfigurer。
添加@EnableWebMvc和implement WebMvcConfigurer的区别
  • 区别主要从WebMvcConfigurer的addInterceptors(InterceptorRegistry registry)的方法声明定义中可以看出:

Add Spring MVC lifecycle interceptors for pre- and post-processing of controller method invocations. Interceptors can be registered to apply to all requests or be limited to a subset of URL patterns.

Note that interceptors registered here only apply to controllers and not to resource handler requests.To intercept requests for static resources either declare a {@link org.springframework.web.servlet.handler.MappedInterceptor MappedInterceptor} bean or switch to advanced configuration mode by extending {@link org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport WebMvcConfigurationSupport} and then override {@code resourceHandlerMapping}.

  • 通过WebMvcConfigurer的方式,无法拦截静态资源,但可以通过其他方式进行扩展实现。
为什么单纯extends WebMvcConfigurationSupport会引发拦截器未注册
  • 在@SpringBootApplication中,会引入@EnableAutoConfiguration,在什么都不做的时候,指:即不加@EnableWebMvc也不搞implement WebMvcConfigurer的时候,那些拦截器是怎么注册的呢?
  • 通过@EnableWebMvc找到了如下类:
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
  • 可以看到假如没有自定义的WebMvcConfigurationSupport类,类WebMvcAutoConfiguration就会执行。在此类中通过extends DelegatingWebMvcConfiguration ,再有DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport,在DelegatingWebMvcConfiguration类中,实现了addInterceptors(registry)方法。
  • 所以,关键就是WebMvcConfigurationSupport类中的addInterceptors(registry)一定要有实现。如果不继承此类,那么有默认的实现方式,如果用户自己实现类此类,就需要处理addInterceptors(registry)的实现。
其他
  • 我准备再搞搞Spring boot的自动装配,为啥@Configuration一加就妥了呢
### DeepSpeech语音识别实现教程 #### 一、环境搭建 为了顺利运行DeepSpeech模型,需先配置好相应的开发环境。对于`deepspeech.pytorch`而言,建议使用Anaconda来管理Python环境以及安装依赖包[^1]。 ```bash # 创建并激活新的虚拟环境 conda create -n ds_env python=3.8 conda activate ds_env # 安装必要的库 pip install torch torchvision torchaudio pip install deepspeech.pytorch ``` #### 二、数据准备 高质量的数据集是训练有效模型的基础。通常情况下,可以采用公开可用的语音数据库如LibriSpeech等作为初始资源;而对于特定应用场景,则可能需要收集定制化的音频样本集合。预处理阶段涉及将原始录音文件转换成适合输入给神经网络的形式,比如提取梅尔频谱特征图(Mel-Spectrogram)。 #### 三、模型架构概述 DeepSpeech系列模型属于典型的卷积神经网络(CNN)+循环神经网络(RNN)结构组合,在此基础上加入了CTC(Connectionist Temporal Classification)损函数用于解决序列标注问题。具体来说: - **前部分**:由多个时间维度上的二维CNN层构成,负责从声学信号中抽取局部时特性; - **中间部分**:多层双向LSTM(Long Short-Term Memory),增强对长期依赖关系的学习能力; - **后解码器**:通过Beam Search算法寻找最有可能的文字表达形式。 #### 四、训练过程指导 当一切准备工作就绪之后就可以着手于实际的训练工作了。这里需要注意几个要点: - 设置合理的超参数(学习率、批量大小等),这直接影响着收敛速度与最终性能表现; - 使用混合精度训练(Half Precision Training, HPT)加速计算效率而不牺牲太多准确性; - 定期保存checkpoint以便随时恢复中断的任务继续执行下去。 #### 五、推理服务部署 完成离线训练后的下一步就是如何让这套系统能够在生产环境中稳定运作起来。一种常见做法是以RESTful API的方式对外提供在线预测接口,允许客户发送请求获取实时响应结果。为此可借助Flask/Django这类Web框架快速构建起微服务体系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值