我們在開發(fā)中, 會經(jīng)常需要需要對返回數(shù)據(jù)做處理的情況, 比如數(shù)據(jù)脫敏, 字段轉(zhuǎn)換等
由于項(xiàng)目使用的序列化工具為fastJson, 固可以使用fastJson的SerializeFilter接口來實(shí)現(xiàn)返回時的數(shù)據(jù)修改
配合自定義的注解, 可以實(shí)現(xiàn)對制定字段的處理
為了提高代碼效率, 使用Spring的自定義類掃描器在項(xiàng)目啟動時對所有實(shí)體掃描, 并緩存需要處理的field
定義注解
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CovertUser {
String source() default "";
}
定義處理器接口
public interface IAkScannerConsumer {
/**
* 包掃描回調(diào)接口
*
* @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);
}
}
定義啟動類注解
啟動類注解用于配置掃描的包和注解, 并設(shè)置對應(yīng)的處理類
@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())) {
// 初始化對應(yīng)的處理器
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;
}
// 掃描器的實(shí)現(xiàn)
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的配置即可