Spring类扫描+fastJson实现返回数据处理

我们在开发中, 会经常需要需要对返回数据做处理的情况, 比如数据脱敏, 字段转换等
由于项目使用的序列化工具为fastJson, 固可以使用fastJson的SerializeFilter接口来实现返回时的数据修改
配合自定义的注解, 可以实现对制定字段的处理
为了提高代码效率, 使用Spring的自定义类扫描器在项目启动时对所有实体扫描, 并缓存需要处理的field

定义注解
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CovertUser {
    String source() default "";
}
定义处理器接口
public interface IAkScannerConsumer {
    /**
     * 包扫描回调接口
     *
     * @param clazz      Class定义
     * @param name       属性名
     * @param annotation 注解
     */
    void accept(Class clazz, String name, Annotation annotation);
}
定义注解处理类
public class CoverUserScannerConsumer implements IAkScannerConsumer {
    @Override
    public void accept(Class clazz, String name, Annotation annotation) {
        String key = clazz.getName() + ":" + name;
        JsonMessageConverterConfig.CACHE.put(key, (CovertUser) annotation);
    }
}
定义启动类注解

启动类注解用于配置扫描的包和注解, 并设置对应的处理类

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(AkScannerConfig.class)
public @interface AkScan {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    /**
     * 扫描注解配置
     */
    Matcher[] matches() default {};

    @interface Matcher {
        /**
         * 需要扫描的注解
         */
        @AliasFor("types")
        Class[] value() default {};

        /**
         * 需要扫描的注解
         */
        @AliasFor("value")
        Class[] types() default {};

        /**
         * 注解处理类
         */
        Class consumer();
    }
}
定义类扫描器
@Slf4j
public class AkScannerConfig implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanFactoryAware {
    private ResourceLoader resourceLoader;
    private BeanFactory beanFactory;

    @Override
    public void registerBeanDefinitions(@NonNull AnnotationMetadata annotationMetadata, @NonNull BeanDefinitionRegistry registry) {
        Map attributes = annotationMetadata.getAnnotationAttributes(AkScan.class.getName());
        if (Objects.isNull(attributes)) {
            log.warn("get AkScan attributes failed");
            return;
        }
        // 读取AkScan的配置
        String[] basePackages = (String[]) attributes.get("basePackages");
        AnnotationAttributes[] matches = (AnnotationAttributes[]) attributes.get("matches");
        if (ArrayUtil.isEmpty(matches)) {
            log.warn("scanner types is empty");
            return;
        }
        Map, IAkScannerConsumer> cache = Maps.newHashMap();
        for (AnnotationAttributes match : matches) {
            Class[] types = match.getClassArray("types");
            Class consumer = match.getClass("consumer");
            for (Class type : types) {
                if (!registry.containsBeanDefinition(consumer.getName())) {
                    // 初始化对应的处理器
                    RootBeanDefinition beanDefinition = new RootBeanDefinition(consumer);
                    registry.registerBeanDefinition(consumer.getName(), beanDefinition);
                }
                IAkScannerConsumer bean = (IAkScannerConsumer) beanFactory.getBean(consumer);
                cache.put(type, bean);
            }
        }
        AkClassPathBeanDefinitionScanner scanner = new AkClassPathBeanDefinitionScanner(registry, cache);
        scanner.setResourceLoader(resourceLoader);
        scanner.scan(basePackages);
    }

    @Override
    public void setResourceLoader(@NonNull ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setBeanFactory(@NonNull BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    // 扫描器的实现
    public static class AkClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
        private final Map, IAkScannerConsumer> cache;
        private final Set> scannerTypes;

        public AkClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, Map, IAkScannerConsumer> cache) {
            super(registry, false);
            scannerTypes = cache.keySet();
            this.cache = cache;
        }

        @NonNull
        @Override
        protected Set doScan(@NonNull String... basePackages) {
            // 扫描包下所有的类
            addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
            return super.doScan(basePackages);
        }

        @Override
        protected void postProcessBeanDefinition(@NonNull AbstractBeanDefinition beanDefinition, @NonNull String beanName) {
            super.postProcessBeanDefinition(beanDefinition, beanName);
            String className = beanDefinition.getBeanClassName();
            Class clazz;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                log.error("load class [" + className + "] error");
                return;
            }
            // 获取所有的类属性
            List fields = BeanUtil.getAllFields(clazz, false);
            for (Field field : fields) {
                Annotation[] annotations = field.getAnnotations();
                if (ArrayUtil.isEmpty(annotations)) {
                    continue;
                }
                for (Annotation annotation : annotations) {
                    Class a = annotation.annotationType();
                    if (!scannerTypes.contains(a)) {
                        continue;
                    }
                    IAkScannerConsumer consumer = cache.get(a);
                    consumer.accept(clazz, field.getName(), annotation);
                }
            }
        }

        @Override
        protected void registerBeanDefinition(@NonNull BeanDefinitionHolder definitionHolder, @NonNull BeanDefinitionRegistry registry) {
            // 禁止将扫描到的类注册到IOC容器中
        }
    }
}
启动类添加扫描注解
@AkScan(basePackages = "com.ak.model", matches = {
        @AkScan.Matcher(types = CovertUser.class, consumer = CoverUserScannerConsumer.class)
})
@SpringBootApplication
public class ServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class);
    }
}
自定义fastJson Filter
@Slf4j
@RequiredArgsConstructor
public class UserCovertSerializer implements ValueFilter {
    private final IUserService userService;

    @Override
    public Object process(Object object, String name, Object value) {
        Class clazz = object.getClass();
        String key = clazz.getName() + ":" + name;
        CovertUser annotation = CACHE.get(key);
        if (Objects.isNull(annotation)) {
            return value;
        }
        try {
            String source = annotation.source();
            if (StrUtil.isBlank(source)) {
                source = name;
            }
            Field field = ReflectionUtils.findField(clazz, source);
            if (Objects.isNull(field)) {
                log.warn("no field name is " + source);
                return value;
            }
            field.setAccessible(true);
            Object account = field.get(object);
            return userService.getNameByAccount(String.valueOf(account));
        } catch (IllegalAccessException e) {
            log.error("convert userName error", e);
            return value;
        }
    }
}
注册filter到fastJson中
public class JsonMessageConverterConfig implements WebMvcConfigurer {
    private final IUserService userService;
    @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
    private String dateFormat;

    @Override
    public void configureMessageConverters(List> converters) {
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat,
                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.DisableCircularReferenceDetect,
                SerializerFeature.WriteEnumUsingToString);
        fastJsonConfig.setCharset(StandardCharsets.UTF_8);
        fastJsonConfig.setDateFormat(dateFormat);
        fastJsonConfig.setSerializeFilters(new UserCovertSerializer(userService));
        fastConverter.setFastJsonConfig(fastJsonConfig);
        converters.add(4, fastConverter);
    }
}

如果有多个注解需要处理, 修改AkScan的配置即可

你可能感兴趣的:(Spring类扫描+fastJson实现返回数据处理)