• 就改了get,却不让我set?——Java内省机制的神奇行为举止一例


    【相关类库】org.apache.commons.beanutils.BeanUtils,提供对Java反射和自省API的包装,其中底层使用到了Java的内省方法。
    【内省的一般应用形式】通过类Introspector 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter方法,然后我们就可以通过反射机制来调用这些方法。
    【代码使用】Introspector.getBeanInfo(Class<?> beanClass);拿BeanInfo对象。
    【例子】例如一个price属性,

    1、定义 private Long price; // 价格
    2、set方法

    public void setPrice(Long price) {

    this.price = price;

    }
    3、get方法

    public String getPrice() {

    return null == price ? null : StringUtil.priceToStr(price);

    }
    则通过内省只会拿到get方法。

    【原理和注意事项】getBeanInfo()方法(返回GenericBeanInfo),其中有getTargetPropertyInfo(),会通过getPublicDeclaredMethods(beanClass);找出所有的get、set方法以PropertyDescriptor存储在list(通常长度为2),然后通过hashmap(属性名,list)暂存;
    然后通过processPropertyDescriptors()进行筛选和合并,
    (这个方法官方注释 Populates the property descriptor table by merging the lists of Property descriptors.)
    其中会判断get方法的返回值和set方法的参数类型是否assignable,若不一致则以get方法为准,认为只有get方法符合要求。
    然后存储在Introspector的properties当中,供GenericBeanInfo的构造方法调用。
    之后BeanInfo对外暴露getPropertyDescriptors(),取其中的properties。


    注:java.beans.Introspector当中,Introspector是public的,还有一个

    GenericBeanInfo是包级可见的类(带蓝三角)。

    官方注释

    * Package private implementation support class for Introspector's
    * internal use.
    * <p>
    * Mostly this is used as a placeholder for the descriptors.

    大概不使用内部类的原因,是因为GenericBeanInfo基本上为Introspector所使用但又保留了给java.beans其他类使用的可能?这块的组织形式个人感觉比较别扭。

    【最佳实践】
    模型向视图对象vo转换时,vo属性设置为传给前端的类型,保持set、get方法类型一致,调用BeanUtils.copyProperties(dest, orig);通过org.apache.commons.beanutils.Converter转换器进行转换,而不要设置不同类型的get、set方法。

    附:自定义类型转换器,注意这里Converter()的类型起名依据是转换后的类型,里面处理的是不同类型转换为转换后的类型的情况。

    ConvertUtils.register(new XXXConverter(), java.lang.XXX.class);
    BeanUtils.copyProperties(dest, orig);

    Converter例子:

    class DateConverter implements Converter {
    @Override
    public Object convert(Class type, Object value) {
    if (value == null) {
    return null;
    }
    if (value instanceof Date) {
    return value;
    }
    if (value instanceof Long) {
    Long longValue = (Long) value;
    return new Date(longValue.longValue());
    }
    try {
    return DateUtils.parseDate(value.toString(), new String[] { "yyyy-MM-dd HH:mm:ss.SSS",
    "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd" });
    } catch (Exception e) {
    throw new ConversionException(e);
    }
    }
    }

  • 相关阅读:
    springMVC-接收数据-参数绑定
    我的asp.net core目录
    我的IdentityServer目录
    win10安装mysql
    asp.net core webapi 生成导出excel
    Dapper, 批量插入,批量更新, 以及in, like
    asp.net core 依赖注入几种常见情况
    asp.net core 2.1 配置管理
    各个模式的accesstoken续期详解
    ResourceOwnerPassword模式使用数据库.
  • 原文地址:https://www.cnblogs.com/feixuefubing/p/10042636.html
Copyright © 2020-2023  润新知