• 利用注解和反射,在Java后端开发里偷一个不该偷的懒


    需求

    我是Mybatis-plus重度依赖患者,本着能不写sql语句就不写的原则,损失性能获得只属于我自己的便捷,所以诞生了以下需求:

    返回给前端的VO需要消减部分隐私数据

    正常情况下,写一个VO,写句sql语句,这不就来了吗,但我是谁?非著名懒狗,一个VO我就要写1+条sql,那以后VO越来越多了怎么办?我直接返回PO不好吗,大不了自己写逻辑消减数据,反正不用写sql语句了

    注解实现

    所以我就想,我自定义一个注解,名字叫@AccessPO,在返回PO的时候,消减有注解的数据不就行了吗,于是诞生了以下代码

    1. @Target(ElementType.FIELD) //作用在属性上
    2. @Retention(RetentionPolicy.RUNTIME)
    3. public @interface AccessPO {
    4. /**
    5. * 0全不可见 1用户可见 2商户可见
    6. */
    7. int type() default 2;
    8. public static final int ALL_NOT = 0;
    9. public static final int USER = 1;
    10. public static final int MERCHANT = 2;
    11. }
    12. 复制代码

    type用于权限控制,不同的接口返回不同的数据,每种角色需要消减的数据也不同

    注解有了,怎么在返回的时候消减呢?那就在修改一下我的Result

    基于Springboot返回的类都会被转为json,本质是一个Map,不停的put数据,返回给前端即可

    1. public class R extends HashMap {
    2. private static final long serialVersionUID = 1L;
    3. public R() {
    4. put("code", 0);
    5. put("msg", "success");
    6. }
    7. public static R error(String msg) {
    8. return error(400, msg);
    9. }
    10. public static R error(int code, String msg) {
    11. R r = new R();
    12. r.put("code", code);
    13. r.put("msg", msg);
    14. return r;
    15. }
    16. public static R ok(String msg) {
    17. R r = new R();
    18. r.put("code", 200);
    19. r.put("msg", msg);
    20. return r;
    21. }
    22. ```
    23. public R put(String key, Object value) {
    24. super.put(key, value);
    25. return this;
    26. }
    27. ...
    28. 复制代码

    消减字段对应方法

    怎么消减对应字段呢?用反射获取到对象,遍历一下字段,消减有注解的字段吧

    1. private Map<String, Object> getAccessFields(Object value) throws IllegalAccessException {
    2. final Class clazz = value.getClass();
    3. final Field[] fields = clazz.getDeclaredFields();
    4. Map<String, Object> map = new HashMap<>();
    5. for (int i = 1; i < fields.length; i++) {
    6. fields[i].setAccessible(true);
    7. if (!fields[i].isAnnotationPresent(AccessPO.class)) {
    8. map.put(fields[i].getName(), fields[i].get(value));
    9. }
    10. }
    11. return map;
    12. }
    13. 复制代码

    获取到类的Class对象,获取所有字段(不包括父类),循环遍历是否存在注解,若不存在则加入待返回的Map中

    好像可以了,那我type里面权限控制不是白写了吗?再修改一下
    添加一些判断身份函数,调用的时候先调用他们,就知道要删减哪些字段咯

    1. private int access = 2;
    2. public int isAccess() {
    3. return access;
    4. }
    5. public void setAccess(int access) {
    6. this.access = access;
    7. }
    8. public R user() {
    9. this.setAccess(1);
    10. return this;
    11. }
    12. public R merchant() {
    13. this.setAccess(2);
    14. return this;
    15. }
    16. 复制代码

    身份控制,在Result对象中新建一个access的字段,用来判断当前的权限是如何如何,消减对应的字段

    再修改getAccessFields()

    1. private Map<String, Object> getAccessFields(Object value) throws IllegalAccessException {
    2. final Class clazz = value.getClass();
    3. final Field[] fields = clazz.getDeclaredFields();
    4. Map<String, Object> map = new HashMap<>();
    5. for (int i = 1; i < fields.length; i++) {
    6. fields[i].setAccessible(true);
    7. if (fields[i].isAnnotationPresent(AccessPO.class)) {
    8. final AccessPO accessVO = fields[i].getDeclaredAnnotation(AccessPO.class);
    9. final int type = accessVO.type();
    10. if (this.access == type) {
    11. map.put(fields[i].getName(), fields[i].get(value));
    12. }
    13. } else {
    14. map.put(fields[i].getName(), fields[i].get(value));
    15. }
    16. }
    17. return map;
    18. }
    19. 复制代码

    如果获取到的注解的type值等于当前Result的权限,则说明身份一致,若为0,1/2都无法匹配,所以全部不见

    这样就能完成PO的字段消减了,但还要还有一点问题,如果我要返回一个List,这该怎么办,那就再加两个函数区分一下单个对象和集合

    1. public R data(String key, Object value) throws IllegalAccessException {
    2. final Map<String, Object> map = getAccessFields(value);
    3. super.put(key, map);
    4. return this;
    5. }
    6. public R list(String key, List value) throws IllegalAccessException {
    7. List<Map<String, Object>> list = new ArrayList<>();
    8. for (Object v : value) {
    9. list.add(getAccessFields(v));
    10. }
    11. super.put(key, list);
    12. return this;
    13. }
    14. 复制代码

    遍历List,对单个元素进行消减然后获取Map即可,相信SpringBoot转Json的强大

    这样就大功告成了,现在返回给前端数据,只需要查询出PO,然后直接R.ok().user().data(data).list(list).put("xxx","xxx")就行了

    例外

    当然在系统中,也不可能一个VO没有嘛,毕竟VO也是很方便的,那就有了一个新需求,我现在有一个VO是由几个PO组成的,可能需要返回VO或者VOLIST,那就继续加函数

    1. public R voList(String key, List list) throws IllegalAccessException {
    2. List<Map<String, Map<String, Object>>> r = new ArrayList<>();
    3. for (Object vo : list) {
    4. Map<String, Map<String, Object>> map = new HashMap<>();
    5. final Class clazz = vo.getClass();
    6. final Field[] fields = clazz.getDeclaredFields();
    7. for (Field field : fields) {
    8. field.setAccessible(true);
    9. Object o = field.get(vo);
    10. map.put(o.getClass().getSimpleName().toLowerCase(Locale.ROOT), getAccessFields(o));
    11. }
    12. r.add(map);
    13. }
    14. super.put(key, r);
    15. return this;
    16. }
    17. public R vo(String key, Object vo) throws IllegalAccessException {
    18. Map<String, Map<String, Object>> r = new HashMap<>();
    19. final Class clazz = vo.getClass();
    20. final Field[] fields = clazz.getDeclaredFields();
    21. for (Field field : fields) {
    22. field.setAccessible(true);
    23. Object o = field.get(vo);
    24. r.put(o.getClass().getSimpleName().toLowerCase(Locale.ROOT), getAccessFields(o));
    25. }
    26. super.put(key, r);
    27. return this;
    28. }
    29. 复制代码

    VOList的实现思路和list一样,区别是通过反射获取所有的field,再执行解析vo的方法

    VO,一个PO是一个Map,那一个VO就是Map>,使用getSimpleName().toLowerCase(Locale.ROOT)来获取小写类名当key 然后反射解析出所有的PO,循环求得Map插入即可

     

    总结

    在需求不多的情况下,还是挺方便的,只需要加注解即可完成字段隐藏,没有完美的使用数据库,直接获取整个PO的话会比手写sql多一点点压力,还是更建议手写sql
    所以这篇文章就当作学习注解和反射的使用了,其实也能拓展一下,通用化一下作为mybatisplus的插件,这样性能就嘎嘎好了,使用也很方便,什么时候写呢?下次一定

  • 相关阅读:
    【爬虫逆向】Python逆向采集猫眼电影票房数据
    权威推荐!腾讯安全DDoS边缘安全产品获国际研究机构Omdia认可
    最「难搞」的英伟达也开源了,下一个会是谁?
    思码逸 X 贝壳:用代码分析,升级既有效能度量体系
    MySQL select count(*)计数很慢,有没有优化方案?
    NLP项目:维基百科文章爬虫和分类【02】 - 语料库转换管道
    Windows系统SVN SERVER迁移。从服务器A迁移到服务器B
    Codeforces-895 (Div3)
    Redis分布式锁实现
    三台linux服务器部署ceph集群
  • 原文地址:https://blog.csdn.net/m0_71777195/article/details/126318950