范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

简简单单的反射和詹杜库放在一起就能好玩了吗?

  背景
  相信大家在日常的工作中,一定遇到过以下的某个场景:  前端需要选择某些字段,去展示不同字段下的信息,如果在字段和方法没有绑定的情况下,如何调用get方法呢?  如果有一张横转纵的表,存储的do都是实际的字段名称,那么如何转化成实体类对应的get、set方法去执行操作呢?  如果我想根据方法名称去调用对象的某个方法呢?
  相信有些基础的程序员会立刻想到使用反射就好了,没错,就是这么简单,但是每用到一次,咱们就去写一次也是比较麻烦的,所以我们可以将它封装成工具类,用的时候直接去调用就好了。  实现
  目前我在工具类实现了三个方法,分别是:  根据属性名调用对象get方法  根据属性名调用对象的set方法  根据方法名调用方法  根据属性名调用对象get方法
  实现逻辑:  获取对象的class  获取对象的所有属性  获取对象的声明方法  遍历属性  匹配方法  invoke执行该方法  返回方法的返回值  /**          * 根据属性名获取属性值          *          * @return java.lang.String          * @Param filedName          * @Param obj          * @Date 2022/12/15 15:06          * @Author wjbgn          **/         public static String getObjField(String filedName, Object obj) {             AtomicReference value = new AtomicReference<>();             Class<?> aClass = obj.getClass();             // 获取所有属性             Field[] declaredFields = aClass.getDeclaredFields();             // 获取所有方法             Method[] declaredMethods = aClass.getDeclaredMethods();             Arrays.stream(declaredFields).forEach(filed -> {                 if (filed.getName().equals(filedName)) {                     // 属性存在,尝试获取属性,调用get方法,此处get方法需要组装                     Arrays.stream(declaredMethods).forEach(method -> {                         if (method.getName().toLowerCase().equals("get" + filedName)) {                             try {                                 // 执行方法                                 Object invoke = method.invoke(obj);                                 value.set(invoke == null ? null : invoke.toString());                             } catch (Exception e) {                                 throw new RuntimeException(e);                             }                         }                     });                 }             });             return value.get();         }  根据属性名调用对象的set方法
  实现逻辑:  获取对象的声明方法  遍历方法  匹配方法名称  invoke执行该方法  /**          * 根据属性名设置属性值          *          * @return void          * @Param filedName 字段名          * @Param value 字段值          * @Param obj 对象          * @Date 2022/12/15 14:41          * @Author wjbgn          **/         public static void setObjField(String filedName, String value, Object obj) {             Class<?> aClass = obj.getClass();             Arrays.stream(aClass.getDeclaredMethods()).forEach(method -> {                 if (method.getName().toLowerCase().equals("set" + filedName)) {                     try {                         method.invoke(obj, value);                     } catch (IllegalAccessException e) {                         throw new RuntimeException(e);                     } catch (InvocationTargetException e) {                         throw new RuntimeException(e);                     }                 }             });         }  根据方法名调用方法
  实现逻辑:  获取对象的所有方法 -> 不同于获取get、set,此处需要获取所有的方法,包括实现和继承来的。  遍历方法  匹配方法名称  invoke执行该方法  返回方法返回值  /**         * 根据方法名调用该方法         *         * @return java.lang.Object         * @Param filedName         * @Param obj         * @Date 2022/12/15 15:05         * @Author wjbgn         **/        public static Object invokeMethod(String filedName, Object obj) {            AtomicReference result = new AtomicReference<>();            Arrays.stream(obj.getClass().getMethods()).forEach(method -> {                if (method.getName().toLowerCase().contains(filedName)) {                    // 有方法包含该属性                    try {                        Object invoke = method.invoke(obj);                        result.set(invoke);                        return;                    } catch (Exception e) {                        throw new RuntimeException(e);                    }                }            });            return result.get();        }  测试准备基础代码
  使用一段代码来测试下我们的方法,首先准备一些基础类。
  背景是有三个小学生,分别是  詹姆斯  ,库里  ,杜兰特  ,每个人共有一些属性,如下所示:/**     * 学生类,每个学生可以跑,跳,投篮     */    static class Student implements PlayerActon {         private String name;         private String age;         private String team;         @FieldDesc(type = "exclusive", value = " learn exclusive skills >> ")        private String exclusive;        @FieldDesc(value = ""s phone num? I don"t know!")        private String phone;         public Student(String name, String age, String team, String phone) {            this.name = name;            this.age = age;            this.team = team;            this.phone = phone;        }         public Student() {         }         public String getExclusive() {            return exclusive;        }         public void setExclusive(String exclusive) {            this.exclusive = exclusive;        }         public String getName() {            return name;        }         public void setName(String name) {            this.name = name;        }         public String getAge() {            return age;        }         public void setAge(String age) {            this.age = age;        }         public String getTeam() {            return team;        }         public void setTeam(String team) {            this.team = team;        }         public String getPhone() {            return phone;        }         public void setPhone(String phone) {            this.phone = phone;        }         @Override        public String running() {            return " is running!";        }         @Override        public String jumping() {            return " is jumping!";        }         @Override        public String shooting() {            return " make a shot!";        }    }
  上面的实体类实现了一个接口  PlayerActon  ,里面是三个方法,表示运动员可以跑  ,跳  ,投篮  :    /**     * 动作接口     */    private interface PlayerActon {        /**         * 跑         */        String running();         /**         * 跳         */        String jumping();         /**         * 投篮         */        String shooting();    }
  除此之外,看到下面的两个属性,分别带有一个注解:  @FieldDesc(type = "exclusive", value = " learn exclusive skills >> ")  private String exclusive;   @FieldDesc(value = ""s phone num? I don"t know!")  private String phone;
  这里没什么别的含义,就是想在反射的时候,给这个属性赋默认值,在注解上面可以直接取值,比较方便。另外注解的属性还有一个  type  ,这个type用来指定当前的属性是专属  字段,因为不同的球员有不同的个性,我们通过这个类型判断下,如果是这个字段,那么要给上面的三个小学生赋不同的专属技能了:   /**     * 自定义注解,描述字段     */    @Documented    @Target({ElementType.FIELD, ElementType.METHOD})    @Retention(RetentionPolicy.RUNTIME)    private @interface FieldDesc {        /**         * 类型         */        String type() default "";         /**         * 字段描述         */        String value() default "";    } 复制代码
  既然说到了技能了,那就把技能枚举定义一下:      /**     * 技能枚举     */    public static enum SkillsEnum {        STAND_HAND("James", "STAND HAND!!!", "摊手"),        SHAKE_HEAD("Curry", "SHAKE HEAD!!!", "摇头"),        SHAKE_SHOULDERS("Durant", "SHAKE SHOULDERS!!!", "晃肩膀");        private String studentName;        private String skillsName;        private String skillsNameDesc;         SkillsEnum(String studentName, String skillsName, String skillsNameDesc) {            this.studentName = studentName;            this.skillsName = skillsName;            this.skillsNameDesc = skillsNameDesc;        }         public String getStudentName() {            return studentName;        }         public void setStudentName(String studentName) {            this.studentName = studentName;        }         public String getSkillsName() {            return skillsName;        }         public void setSkillsName(String skillsName) {            this.skillsName = skillsName;        }         public String getSkillsNameDesc() {            return skillsNameDesc;        }         public void setSkillsNameDesc(String skillsNameDesc) {            this.skillsNameDesc = skillsNameDesc;        }         /**         * 根据学生获取技能         *         * @return java.lang.String         * @Param student         * @Date 2022/12/15 14:21         * @Author wjbgn         **/        public static String getSkillsByStudent(Student student) {            for (SkillsEnum skillsEnum : SkillsEnum.values()) {                if (skillsEnum.getStudentName().equals(student.getName())) {                    return skillsEnum.getSkillsName();                }            }            return null;        }    }  准备main方法
  下面我们准备一个main方法,模拟一个场景:      /***     * 工具类测试样例     *     * @Param args     * @return void     * @Date 2022/12/15 15:10     * @Author wjbgn     **/    public static void main(String[] args) {        // 获取动作对应的结果,循环10次        for (int i = 0; i < 10; i++) {            try {                // 随机获取一个学生                Student student = studentList.get(new Random().nextInt(3));                // 随机获取一个动作                String action = actionList.get(new Random().nextInt(7));                // 打印下随机结果                System.out.println(getStudentField(action, student));                 // Thread.sleep(500L);            } catch (Exception e) {                throw new RuntimeException(e);            }        }    }
  如上所示,循环10次,分别调用  getStudentField  方法,方法后面会讲,这个方法就是为了获取学生的属性,但是我们从上面的代码看的出来,获取哪一个学生,获取学生的哪一个属性都是随机的,所以我们首先把这些属性和学生初始化一下,其中除了有字段属性  ,还有方法名称  : /**     * 动作集合     */    private static List studentList = new ArrayList<>();     /**     * 动作集合     */    private static List actionList = new ArrayList<>();     /**     * 初始化 动作集合,学生     * 这里面都使用字段的名称,不使用get、set方法     */    static {        // 获取球员的年龄        actionList.add("age");        // 获取球队        actionList.add("team");        // 获取电话        actionList.add("phone");         // 学习/使用专属动作        actionList.add("exclusive");         // 跑        actionList.add("running");         // 跳        actionList.add("jumping");         // 投篮        actionList.add("shooting");         studentList.add(new Student("James", " 37 years old", " From the Los Angeles Lakers", ""));        studentList.add(new Student("Curry", " 34 years old", " From the Golden State Warriors", ""));        studentList.add(new Student("Durant", " 33 years old", " From the Brooklyn Nets", ""));    }
  有了上面的初始化,我们就可以随机的调用  getStudentField  方法,步骤:首先将学生名字返回拼接到字符串  通过  属性名称  和学生对象  调用前面封装好的ObjectDynamicUtil.getObjField  方法如果没获取到属性,表示  属性为空  或者不是属性,是方法  去调用  setStudentField   方法,如果返回有值,则成功,再次ObjectDynamicUtil.getObjField  获取一次如果仍然是空,那么就调用前面封装好的  ObjectDynamicUtil.invokeMethod  ,按属性调用方法。返回结果      /***     * 动态获取学生属性     *     * @Param     * @return void     * @Date 2022/12/15 11:23     * @Author wjbgn     **/    private static String getStudentField(String filedName, Student student) throws NoSuchFieldException {        String msg = student.getName();        // 获取属性值        String value = ObjectDynamicUtil.getObjField(filedName, student);        if (value == null || value == "") {            // 如果获取属性是空怎么办?设置一个值进去            setStudentField(filedName, student);            // 设置值后,再次执行get方法            value = ObjectDynamicUtil.getObjField(filedName, student);        }        // 调用学生实现的动作接口方法        if (value == null) {            value = (String) ObjectDynamicUtil.invokeMethod(filedName, student);        }        msg += value;        return msg;    }
  下面看下设置学生属性值的方法:  setStudentField  ,步骤:获取学生对象class  根据  属性名  获取class的属性根据属性获取注解  FieldDesc  ,即前面自定义的注解如果注解类型是  exclusive  ,就根据学生从枚举类获取专属技能拼装结果并调用  ObjectDynamicUtil.setObjField  设置对象属性    /***     * 动态设置学生属性     *     * @Param     * @return void     * @Date 2022/12/15 11:23     * @Author wjbgn     **/    private static void setStudentField(String filedName, Student student) throws NoSuchFieldException {        Class<? extends Student> studentClass = student.getClass();        Field declaredField = null;        try {            declaredField = studentClass.getDeclaredField(filedName);        } catch (NoSuchFieldException e) {            return;        } catch (SecurityException e) {            throw new RuntimeException(e);        }        FieldDesc annotation = declaredField.getAnnotation(FieldDesc.class);        String value = annotation.value();        String finalValue = value + (annotation.type().equals(filedName) ? SkillsEnum.getSkillsByStudent(student) : "");        ObjectDynamicUtil.setObjField(filedName, finalValue, student);    }  查看结果
  到此为止,所有的代码都准备完毕了,记得把main方法的  Thread。sleep  注释放开,看到的结果更加直观。
  此处注释是因为在码上掘金导致代码不能运行,不知道码上掘金是什么原因?
  结果如下图所示:
  如上所示,看到不同的学生可以做不同的事,展示不同的属性,都是随机动态获取的。  总结
  反射  是java中,最基础,也是最核心的内容,同样也是最有用的。然而实际的工作当中,我们接触到的机会少之又少,所以我们需要自我提升,将这些手段融会贯通。本文涉及的知识很小一部分反射知识,但是对应经常与表单,表格打交道的后端程序员来说,却非常有用,赶紧用起来吧~~
创意大理数字化加持大理华侨城携手平安付助力大理古城旅游商业复苏来源云南日报2023年元旦假期,大理古城景区共计接待游客90215人次,较2022年同期同比增加272,旅游复苏活力持续显现。为进一步推动大理古城景区服务升级旅游市场复苏回暖,近日逆势增长!丹寨万达小镇2022年客流达683万人次1月5日,国内著名4A级景区贵州丹寨万达小镇公布2022年度经营数据年度客流683万人次,同比2021年上升0。1。丹寨万达小镇成为2022年全国旅游景区中罕有的实现客流正增长的大任正非带领华为终将回归,三年就要结束,高管宣布方向盘回到手中这几年的手机市场要说最可惜的是哪个品牌,应该就是华为了吧,销量不断下滑,麒麟芯片的市场已经接近于零,华为的总销量已经跌出全球前十。可能大家已经不再记得三年前的华为了吧,在2019年上美影归来,惊艳了中国动画的新年中国奇谭是部中式奇幻短片集,共分八个篇章的小故事。图为海报。(片方供图)记者王彦2023年元旦当天,也是中国动画新百年的第一天,上海美术电影制片厂在B站上线了一部新作。此后的三天,千年手游武功系统玩法攻略一月5号千年手游正式上线,千年盛世是一款江湖武功世界的游戏。游戏中不存在职业概念,玩家们只要获得各类的武功的秘籍,任何招式都可以任意学习,武功秘籍包括武功,心法,被动武功。下面小编提升格局必看的6部纪录片,你看过几个有人说,格局决定结局。一个人的格局,在于他的眼界思维目标,以及身上所体现的从容和大度。格局开阔,便能一路披荆斩棘,所向披靡格局狭隘,则目光短浅,遇事容易自乱阵脚。如果感觉生活频频不盘点一下春晚,至今都没有上过春晚的4位大咖,你知道都有谁吗?每到年底,有关春晚的话题就会快速登上热搜,似乎只要是上了春晚,就意味着得到了大家的认可,但是凡事总是有例外,别人求之不得的春晚邀请,可在这4位大咖眼中,却是委婉的拒绝1。周星驰在这成功故事QED教育,如何让孩子爱上阅读走上自主阅读的道路?在孩子实现英文自主阅读的道路上,选择合适的书,是走通自主阅读这条道路的前提。同育儿一样,让孩子实现自主阅读既不能拔苗助长,也不能无所作为,要让孩子对阅读感兴趣,同时又不至于觉得太难香港身份全面剖析香港DSE考试的10大误区!家长必看!(上)很多家长办香港身份都是为了让孩子参加考试,因为考试题目简单,DSE是避开高考内卷最佳赛道,但是大家只知道DSE的优势,却对它的规则一知半解,那今天我们就通过十个问题把表让家长朋友们张龄心父母爱情中饰江亚菲,骨语的主演,老公是扈耀之2014年,孔笙执导的父母爱情引发收视狂潮。剧中饰演江亚菲的张龄心再次火了一把。张龄心次年,参演的琅琊榜播出。剧中,她饰演掌镜史夏冬。2017年,参演的我的前半生播出。在剧中,她饰19601980年,联合国谈判,科学和技术促进后殖民世界的发展文檐前语编辑檐前语自1960年代初以来,联合国一直承认科学和技术是发展政策的组成部分,虽然这种联系最初被认为是对全球北方进行的科学研究结果的应用,但到了1970年代,在谈判新的国际