涉及反射/内省/泛型的优化实践

简介:

  "当系统的每一部分都由最优解或相对优解组成,那么系统最终也将是最完美的。"

   这句话是在参加莫技术分享会上听到的,这句话吸引我占在人群后面听完了她的分享,确实受益良多。

   本文也旨在描述自己在项目演变中对一处公共处理逻辑优化的过程,周期略长最近有时间整理如下。

 

   业务系统数据传递过程中,会抽取一些公共的属性和方法封装为特定基类以便于后续开发进行继承。

   这些被抽象出来的拥有公共属性的基类,在业务流转过程中的赋值也应当进行统一妥善的处理。

   

   看到这里的小伙伴们可能心中有点疑惑,在业务组织 Class_A/Class_B/...将基类中基本属性顺势填充不就可以了吗?

   实际业务中 BaseClass 中的属性需要根据各种不同的场景进行演变和推算,你确定将这些公共推算方式放入业务逻辑中合适吗??

   随着系统扩展继承 BaseClass 的子类会膨胀,所有的子类都需要进行父类基本属性赋值,代码看起来是不是有点重复?

   当公共属性推算方式发生变化,修改所有子类计算方式和修改公共方法,工作量孰重孰轻?

   抽取成公共方法这点毋庸置疑的,但怎样高效和优雅的书写代码呢?

   本文试结合实例,简述在业务流转中对拥有公共属性基类赋值的方法以及后续持续优化。

   例子将省略公共属性的判断推算过程,实际项目中的公共属性和业务对象过于复杂,自己简单抽象对象如下(关注点为公共属性的赋值)。

复制代码
public class BaseStudentVO {
    private String stu_id;
    private String stu_name;
    private Integer stu_age;
    private Date stu_birthday;

    .......getter/setter
}

public class PrimaryScholar extends BaseStudentVO {
     .......logicAttribute
}
复制代码

   BaseStudentVO 为抽取公共属性的基类,业务中使用的为 PrimaryScholar,业务开始流转时需要将基类的公共属性填充进去。

   起初编写函数时采用反射方式,由子类反射获取超类并迭代超类公共方法,获取特定 set 方法进行调用,代码思路如下:

复制代码
 private Object setBaseAtrByReflect(Map<String, Object> baseParam, Class clazz) throws Exception {
        Object instance = clazz.newInstance();
        Class superclass = clazz.getSuperclass();
        Method[] declaredMethods = superclass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            if (declaredMethod.getName().contains("set")) {
                if (declaredMethod.getName().contains("Stu_id")) {
                    declaredMethod.invoke(instance, baseParam.get("stu_id"));
                    continue;
                }

                if (declaredMethod.getName().contains("Stu_name")) {
                    declaredMethod.invoke(instance, baseParam.get("stu_name"));
                    continue;
                }
                if (declaredMethod.getName().contains("Stu_age")) {
                    declaredMethod.invoke(instance, (Integer) baseParam.get("stu_age"));
                    continue;
                }
                if (declaredMethod.getName().contains("Stu_birthday")) {
                    declaredMethod.invoke(instance, (Date) baseParam.get("stu_birthday"));
                }
            }
        }
        return instance;
    }
复制代码

   起初这种方式大概是最差的,需要强转类型/随属性增多的的判断/迭代超类的所有的公共方法....简直不忍直视。

   后面空闲时间采用 java 内省重新编码了函数, java 内省通俗来说是 jdk 提供给程序员一种更优雅的方式获取 java-bean 的 getter/setter 方法。

   内省和反射是两码事请区别对待,但毋容置疑内省是由反射实现,只不过代码实现由 sun 公司的 java 团队,并经过很严格测试。

复制代码
  private Object setBaseAtrByIntrospect(Map<String, Object> baseParam, Class clazz) throws Exception {
        Object instance = clazz.newInstance();
        Class superclass = clazz.getSuperclass();
        PropertyDescriptor propDesc;
        Method methodSetUserName;
        for (Field field : superclass.getDeclaredFields()) {
            propDesc = new PropertyDescriptor(field.getName(), clazz);
            methodSetUserName = propDesc.getWriteMethod();
            methodSetUserName.invoke(instance, baseParam.get(field.getName()));
        }
        return instance;
    }
复制代码

   采用内省后比 assembleBaseAtrByReflect 反射时的代码是不是清爽了很多,而且摈弃了很多难堪的地方,比如 强制类型转换/随属性增多的判断。

   当后来偶然瞥见 apache.beanutil 中几个有意思的 API 时,我觉得是时候优化下原项目中对应函数的编码。

复制代码
 private Object setBaseAtrByBeanUtil(Map<String, Object> baseParam, Class clazz) throws Exception {
        Object instance = clazz.newInstance();
        BeanUtils.setProperty(instance, "stu_id", baseParam.get("stu_id"));
        BeanUtils.setProperty(instance, "stu_name", baseParam.get("stu_name"));
        BeanUtils.setProperty(instance, "stu_age", baseParam.get("stu_age"));
        BeanUtils.setProperty(instance, "stu_birthday", baseParam.get("stu_birthday"));
        return instance;
    }
复制代码

   代码是不是简单明快了很多?看起来一目了然,而且进行了精准打击,没有多余迭代判断。

   如果你反编译 BeanUtil 中的对应方法,你还是会找到 内省的影子。

   最近瞅见了分层中对泛型的抽象使用,难免让人浮想联翩,忍不住使用泛型调整了该函数的实现。

复制代码
 private <T extends BaseStudentVO> T setBaseAtrByGenenic(Map<String, Object> baseParam, T t) {
        t.setStu_id(baseParam.get("stu_id").toString());
        t.setStu_name(baseParam.get("stu_name").toString());
        t.setStu_age((Integer) baseParam.get("stu_age"));
        t.setStu_birthday((Date) baseParam.get("stu_birthday"));
        return t;
    }
复制代码

   泛型中的边界限定很切合了该业务场景,使入参泛型 t 能顺畅的使用基类中的 set 方法,彻底摆脱了反射。

   反射的执行效率偏低,早已成为不争的事实,使用泛型重写后出现了类型强转的问题,但这已经是最优解了。

   从开始的反射到后面的内省到最终的泛型,想法都是在翻看其他人的分享或撸一段实现时突然蹦出来。


本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/7147363.html,如需转载请自行联系原作者

相关文章
|
4月前
|
设计模式 Java
java反射基础
本文主要针对java中的反射基础知识进行讲解
31 0
|
8月前
|
XML 安全 Java
教你精通Java语法之第十三章、反射
Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到,那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。1. 反射的意义2. 反射重要的几个类: Class类 、Field类、 Method类、 Constructor类3. 学会合理利用反射,一定要在安全环境下使用。
39 0
|
8月前
|
存储 算法 Java
Java集合重点知识详解——优点以及内部继承关系
Java集合重点知识详解——优点以及内部继承关系
71 0
|
11月前
|
安全 Java 程序员
魔法反射--java反射初入门(基础篇)
反射被应用于许多方面, spring的注解, jdbc的连接都是基于反射来实现的, 可能在工作中我们很少能用到反射, 但是在面试的过程中面试官经常会问道, 可以不用反射, 但作为一个程序猿, 还是应该了解了解的
66 0
|
前端开发 编译器 容器
|
安全 Java 数据安全/隐私保护
Java反射(扩展)(五)
前几篇文章讲述Java反射核心功能与用法,基本的常用方法都已经囊括里面了,本篇针是对前几篇文章进行补充。
84 2
Java反射(扩展)(五)
|
网络协议 测试技术 Go
反射的引出和应用场景|学习笔记
快速学习反射的引出和应用场景
87 0
|
存储 Java API
java反射机制基础
java反射机制基础
105 0
java反射机制基础
|
Java API 容器
泛型机制与反射原理
泛型即可以理解为把数据类型作为参数,即参数化类型,用来提高代码的安全性,灵活性,避免类型转换。
78 0
泛型机制与反射原理
|
Java
Java反射 - 基础篇
Java反射 - 基础篇
89 0
Java反射 - 基础篇