用户名转真实姓名,springboot+自定义注解

517 阅读3分钟

上回用注解搞定了数据字典转换后,小伙伴们把目光看向了用户名。我们业务场景要求,列表数据要展示编辑人,再加上某些业务会需要展示各种操作人,但是给客户展示用户ID或者username不太好,所以要将username转成realName,在给客户展示。这些人懒得自己写逻辑,希望我用注解方式解决下。

基本逻辑跟之前数据字典差不多,废话不多说直接上代码吧

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = UserSerializer.class)
public @interface User {

}

这里声明了一个用户注解,不需要任何属性,他的作用就是标记用户名转换

public class UserSerializer extends StdSerializer<Object> implements ContextualSerializer {

    /** 用户注解 */
    private User user;

    public UserSerializer() {
        super(Object.class);
    }
    public UserSerializer(User user) {
        super(Object.class);
        this.user = user;
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (Objects.isNull(value)) {
            gen.writeObject(value);
            return;
        }

        String label = UserDataCache.getLabel(value.toString());
        gen.writeObject(value);
        if (label instanceof String){
            gen.writeFieldName(gen.getOutputContext().getCurrentName()+"Name");
        }else {
            gen.writeFieldName(gen.getOutputContext().getCurrentName()+"Object");
        }
        gen.writeObject(label);
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty beanProperty) throws JsonMappingException {
        if (Objects.isNull(beanProperty)){
            return prov.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        User user = beanProperty.getAnnotation(User.class);
        if (Objects.nonNull(user)){
            return this;
        }
        return prov.findNullValueSerializer(null);
    }
}

createContextual 方法主要作用是字段的注解,因为字段的上下文信息在运行期不会改变,所以只会在第一次序列化字段时调用,因此不用担心影响性能。
代码中逻辑可编写为,当字段为拥有@User注解时,就取出注解中的value值,这样在serialize方法中便可以得到这个value值了。
serialize主要逻辑是,根据传入的value值,调用方法获取realName,将结果写到Json中。我的逻辑是原先的字段不动,我再加一个字段,用来存放realName。例,原先字段为updateUser,我会添加一个字段:updateUserName,存放中文名。

public class UserDataCache {
    public static String getLabel(String username) {
        // 首先从字典加载中获取
        UserDataOptions dataOptions = SpringUtil.getBean(UserDataHandler.class).getRealname(username);
        if(Objects.nonNull(dataOptions)) {
            return dataOptions.getRealname();
        }
        return "";
    }

}

这里就是调用获取的逻辑

@Component
public class UserDataHandler extends AbstractUserHandler {

    @Autowired
    private UserHandlerAware userHandlerAware;
    @Autowired
    private RedisTemplate redisTemplate;

    public UserDataHandler() {
        cacheData = CacheUtil.newFIFOCache(10000,1000*60*60*24);
    }

    @Override
    public UserDataOptions getRealname(String username) {
        if(Objects.nonNull(cacheData.get(username))){
            return cacheData.get(username);
        }
        Boolean aBoolean = redisTemplate.hasKey("user:" + username);
        if(!aBoolean){
            UserDataOptions userDataOptions = getUserDataHandler().getRealnameByUsername(username);
            put(username,userDataOptions);
        }
        Object o = redisTemplate.opsForValue().get("user:" + username);
        cacheData.put(username,(UserDataOptions)o);
        return (UserDataOptions)o;
    }

    private IUserDataHandler getUserDataHandler(){
        return userHandlerAware.getUserHandler();
    }
}

这里跟之前不同的是,添加了本地缓存。之前数据字典,是直接从Redis获取,获取不到直接查数据库,后来提出一个效率问题,一个json串中有N个注解就要查N次Redis,有M条数据就要查N*M次Redis,这样设计会浪费一部分性能,导致查询时间变长。
后来添加了本地缓存,暂时规定大小10000,有效时间一天。先从本地缓存获取,没有在查Redis等,明显感觉使用本地缓存后查询效率增加了。但同时也会存在数据更新不及时问题,我修改了用户信息,但查询的数据仍然没变,这个我暂时没有处理,因为我们的用户基本很少会修改,具体看项目取舍吧。

public class UserHandlerAware implements ApplicationContextAware {
    //默认实现
    private IUserDataHandler userHandler = new DefaultUserDataHandler();

    /**
     * 功能描述: 获取应用上下文并获取相应的接口实现类
     * @param applicationContext 上下文
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 根据抽象类,返回字典实现
        Map<String, AbstractUserDataHandler> userMap = applicationContext.getBeansOfType(AbstractUserDataHandler.class);
        if(MapUtil.isNotEmpty(userMap)) {
            userHandler = userMap.entrySet().iterator().next().getValue();
        }
    }

}
public class DefaultUserDataHandler extends AbstractUserDataHandler{
    @Override
    public UserDataOptions getRealnameByUsername(String username) {
        return new UserDataOptions();
    }
}

这一部分主要是注册查询接口类,我提供了接口,也提供了默认实现,默认返回空,避免报错影响逻辑。微服务架构下,用户解析等可以放到common下,但数据获取只能在本服务中,本服务调用用户服务获取到数据后返回。
以上为全部内容,新手写文,欢迎指正。