我们在开发中, 会经常需要需要对返回数据做处理的情况, 比如数据脱敏, 字段转换等
由于项目使用的序列化工具为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<? extends IAkScannerConsumer> 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<String, Object> 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<Class<?>, 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<Class<?>, IAkScannerConsumer> cache;
private final Set<Class<?>> scannerTypes;
public AkClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, Map<Class<?>, IAkScannerConsumer> cache) {
super(registry, false);
scannerTypes = cache.keySet();
this.cache = cache;
}
@NonNull
@Override
protected Set<BeanDefinitionHolder> 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<Field> fields = BeanUtil.getAllFields(clazz, false);
for (Field field : fields) {
Annotation[] annotations = field.getAnnotations();
if (ArrayUtil.isEmpty(annotations)) {
continue;
}
for (Annotation annotation : annotations) {
Class<? extends Annotation> 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<HttpMessageConverter<?>> 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的配置即可