thumbdata4怎样处理


起因

近期线上出现了一个分页功能失效的问题,具体表现为某个列表页面的分页无法生效。经过初步调查,该问题似乎与后端代码有关。

可能性分析

1. 我们检查了是否是因为后端数据未正确处理分页请求。经过确认,问题确实存在于后端返回的数据中,分页信息并未正确返回。

2. 后端返回的总页数只有一页,这表明分页功能没有起作用。

3. 通过代码分析,我们发现分页管理是基于内部封装的一个切面处理实现的,这部分代码可能存在问题。由于涉及到多个服务的数据构造,我们决定通过本地调试来定位问题。

代码分析

1. vjooq的PaginationAspect

该切面用于处理分页逻辑,它获取事先存放在PageBuilder中的dsl对象、sql语句等,执行sql获取到一个临时表,并统计总数。对于以ByPage结尾的方法,该切面可以发挥作用。它以PageBuilder作为校验条件,仅作用于Repository层的方法。

2. 实际调用的分页方法

我们查看了实际调用的分页方法,该方法符合切面的调用规则,并且在近几个月内无改动。但不知为何,分页功能突然失效。经过初步检查,代码本身并无问题。

3. ClinicDoctorUserRepository类

在调试过程中,我们发现本应被切面增强的Repository类并未产生代理类,因此无法享受切面的功能。由于前段时间研究了Spring容器启动的源码,我们决定探究为什么ClinicDoctorUserRepository在初始化时未被切面增强。

初始化分析

1. 设置断点

我们知道Spring Aop的功能是基于Spring Aop的jar包和BeanPostProcessor实现的。在Spring Boot中开启Aop功能时,Spring会在启动时注入一个AnnotationAwareAspectJAutoProxyCreator的Bean。这个Bean在生成代理对象时起到关键作用。为了追踪代理对象的生成过程,我们在相关代码中设置了断点。

2. 开始debug

启动项目后,进入第一个断点处,发现ClinicDoctorUserRepository并未生成代理对象。继续调试,查看bean的完整初始化过程,发现如果bean在初始化过程中被其他Bean依赖,那么它可能在这个依赖关系中被提前初始化,从而错过被切面增强的机会。

经过进一步调试,我们发现MethodValidationPostProcessor和AnnotationAwareAspectJAutoProxyCreator都实现了Ordered接口,它们的初始化顺序可能会影响切面的增强效果。而ClinicDoctorUserRepository被MethodValidationPostProcessor所依赖,因此在MethodValidationPostProcessor初始化时,ClinicDoctorUserRepository也被提前初始化,错过了被AnnotationAwareAspectJAutoProxyCreator增强的机会。

问题的根本原因是MethodValidationPostProcessor和AOP功能的AnnotationAwareAspectJAutoProxyCreator的初始化顺序问题。被MethodValidationPostProcessor所依赖的Bean(如ClinicDoctorUserRepository)无法被AOP增强,甚至无法被MethodValidationPostProcessor增强。这个情况可能在以前是因为之前的项目中可能采取了其他方式处理这个问题。但近期由于某些改动,导致了这个问题的出现。

处理方法

1. 为MethodValidationPostProcessor的声明独立出一个Config类,避免与其他Bean的依赖关系。

这个用户数据缓存对应的实体类是 com.xxxx.clinic.domain.tables.pojos.User。在数据中,我们可以看到一些基本的用户信息,如ID、登录ID、用户类型、姓名、密码、头像链接等。值得注意的是,该实体类中的“@class”字段是用来存储实体类的类型信息的。

在进行JSON反序列化时,由于默认的Jackson反序列化器会将数据反序列化为LinkedHashMap,因此我们需要使用自定义的ObjectMapper来确保数据能够正确地反序列化为我们的POJO对象。为了实现这一点,我们需要进行以下配置:

使用 mapper.enableDefaultTyping 方法启用默认的类型信息支持。具体来说,我们将 ObjectMapper.DefaultTyping 设置为 NON_FINAL 并使用 JsonTypeInfo.As.PROPERTY 模式来指定类型信息的存储方式。这样配置后,生成的JSON数据中会包含一份POJO的类型信息,这就是上述JSON数据中的“@class”字段。这样做能够确保我们的自定义ObjectMapper正确地将JSON数据反序列化为指定的POJO对象。这个配置也是GenericJackson2JsonRedisSerializer内部默认的配置。