关于java:Java基础之BeanInfo

50次阅读

共计 4573 个字符,预计需要花费 12 分钟才能阅读完成。

Java 的 BeanInfo 在工作中并不怎么用到,我也是在学习 spring 源码的时候,发现 SpringBoot 启动时候会设置一个属叫 ”spring.beaninfo.ignore”,网上只能搜寻到这个配置的意思是是否跳过 java BeanInfo 的搜寻,没找到其余信息,然而 BeanInfo 又是什么呢?

JavaBean 介绍

维基百科 JavaBean 的定义:JavaBeans 是 Java 中一种非凡的类,能够将多个对象封装到一个对象(bean)中。特点是可序列化,提供无参结构器,提供 getter 办法和 setter 办法拜访对象的属性。名称中的“Bean”是用于 Java 的可重用软件组件的习用叫法。要成为 JavaBean 类,则必须遵循对于命名、结构器、办法的特定标准。有了这些标准,能力有能够应用、复用、代替和连贯 JavaBeans 的工具。标准如下:

  • 有一个 public 的无参数结构器。
  • 属性能够通过 get、set、is(能够代替 get,用在布尔型属性上)办法或遵循特定命名标准的其余办法拜访。
  • 可序列化。

以下为一个非法的 JavaBean 的定义:

public class PersonBean implements java.io.Serializable {

    /**
     * name 属性 (留神大小寫)
     */
    private String name = null;

    private boolean deceased = false;

    /** 无参结构器 (没有参数) */
    public PersonBean() {}

    /**
     * name 属性的 Getter 办法
     */
    public String getName() {return name;}

    /**
     * name 属性的 Setter 办法
     * @param value
     */
    public void setName(final String value) {name = value;}

    /**
     * deceased 属性的 Getter 办法
     * 布尔型属性的 Getter 办法的不同模式 (这里应用了 is 而非 get)
     */
    public boolean isDeceased() {return deceased;}

    /**
     * deceased 属性的 Setter 办法
     * @param value
     */
    public void setDeceased(final boolean value) {deceased = value;}
}

JavaBean 的自省

用一个简略的 SpringMVC 用户登录的场景来形容一下 JavaBean 的自省,用户登录时候,前端表单传递的参数通常是一个如下 Json 字符串:

{
    "username":"xxx",
    "password":"xxxx"
}

后端承受表单的中央,通常能够应用一个 JavaBean 用 RequestBody 的模式接管参数:

public void login(@RequestBody LoginRequest request){// Do login}

其中,LoginRequest 相似于如下的格局:

public class LoginRequest {public String getUsername() {return username;}

    public void setUsername(String username) {this.username = username;}

    public String getPassword() {return password;}

    public void setPassword(String password) {this.password = password;}

    private String username;
    private String password;
}

那么前端的 Json 如何映射到后端 LoginRequest 中的对应属性之上呢?能够看到 LoginRequest 中的字段都是 private 类型,无奈间接设置字段值(反射尽管能够设置,然而并不适合),只能通过 Setter 办法进行设置,然而程序怎么晓得 JavaBean 有哪些 Setter 办法呢?此处就用到了 JavaBean 的内省机制。

JavaBean 内省工具 Introspector

Java bean 的工具包中提供了 java 内省工具 Introspector,该工具能够通过以下办法获取 Java bean 的内省后果 BeanInfo(后文具体介绍),获取 BeanInfo 的流程如下图所示

// 在 Object 类时候进行检索,能够抉择在任意一个父类进行
BeanInfo beanInfo = Introspector.getBeanInfo(JavaBeanDemo.class,Object.class);

JavaBean 内省后果 BeanInfo

通过 java 的内省工具 Introspector 的 getBeanInfo 办法,咱们能够获取一个 JavaBean 的内省 BeanInfo,获取到的 BeanInfo 蕴含以下属性:

  • Bean 的类相干信息
  • Bean 的事件信息
  • Bean 的属性信息
  • Bean 的办法信息
  • 额定属性信息
  • Component 的图标

内省后果 BeanInfo 的类型

BeanInfo 只是一个内省后果的接口,Java 中对该接口的实现有以下三种:

  1. ApplicationBeanInfo:Apple desktop 相干的 JavaBean 内省后果
  2. ComponentBeanInfo:Java Awt 组件的内省后果,如按钮等
  3. GenericBeanInfo:通用的内省后果,JEE 开发中的内省后果都为该类型

此外,Spring 自定义了一个内省后果类型,叫 ExtendedBeanInfo,次要用于辨认返回值不为空的 Setter 办法。

Spring 的 BeanUtils.copyProperties

BeanUtils.copyProperties 用户在两个对象之间进行属性的复制,底层基于 JavaBean 的内省机制,通过内省失去拷贝源对象和目标对象属性的读办法和写办法,而后调用对应的办法进行属性的复制。以下为 BeanUtils.copyProperties 的流程

BeanUtils 对 JavaBean 内省的一些机制进行优化,到这里,大家有没有发现 Java 内省的一些毛病呢?

BeanUtils 并发问题优化

Java 内省的后果会缓存在 ThreadGroupContext 中,并且通过 synchonrized 关键字对缓存加锁 (下图中的红框局部),导致同一个线程组中的线程无奈并行内省。

Spring 的 BeanUtils 在 Java 内省之上又增加了一层缓存,这层缓存应用 ConcurrentHashMap 实现,从而进步了内省的效率。

BeanUtils Setter 属性辨认优化

在 Java 默认的内省过程中,setter 办法的返回值必须是 null,如果不是 null 的话,无奈辨认为无效的 JavaBean 属性(下图中的红色局部),Spring 自定义了一个 BeanInfo ExtendedBeanInfo 解决了这个问题。

spring.beaninfo.ignore

回到最后提到的 spring.beaninfo.ignore,这个配置用来疏忽所有自定义的 BeanInfo 类的搜寻.

BeanUtils 性能测试

复制办法 1 万次复制耗时 1 百万次复制耗时 1 亿次复制耗时
ModelMapper 复制 262mills 3875mills 283177mills
BeanUtils 复制 3mills 369mills 20347mills
间接复制 约等于 0mills 5mills 438mills

能够看出:BeanUtils 破费的工夫约为间接复制的 50 倍以上。

public class BeanUtilsPerformanceTest {public static void main(String[] args){
        // 预热虚拟机
        loopBeanUtils(100000);
        loopCopyByHand(100000);

        // 复制 1 万次的状况
        System.out.println("\nloop 10000 times:");
        loopBeanUtils(10000);
        loopCopyByHand(10000);

        // 复制 1 百万次的状况
        System.out.println("\nloop 1000000 times:");
        loopBeanUtils(1000000);
        loopCopyByHand(1000000);

        // 复制 1 亿次的状况
        System.out.println("\nloop 100000000 times:");
        loopBeanUtils(100000000);
        loopCopyByHand(100000000);
    }

    private static void loopBeanUtils(int loopTimes){TestBeanDemo source = new TestBeanDemo();
        TestBeanDemo target = new TestBeanDemo();
        long start = System.currentTimeMillis();
        for (int i=0;i<loopTimes;i++){BeanUtils.copyProperties(source,target);
        }
        System.out.println("BeanUtils cost times:"+String.valueOf(System.currentTimeMillis()-start));
    }

    private static void loopCopyByHand(int loopTimes){TestBeanDemo source = new TestBeanDemo();
        TestBeanDemo target = new TestBeanDemo();
        long start = System.currentTimeMillis();
        for (int i=0;i<loopTimes;i++){target.setField1(source.getField1());
            target.setField2(source.getField2());
            target.setField3(source.getField3());
            target.setField4(source.getField4());
            target.setField5(source.getField5());
        }
        System.out.println("Copy field one by one times:"+String.valueOf(System.currentTimeMillis()-start));
    }

    @Data
    private static class TestBeanDemo{private String field1 = UUID.randomUUID().toString();
        private String field2 = UUID.randomUUID().toString();
        private String field3 = UUID.randomUUID().toString();
        private String field4 = UUID.randomUUID().toString();
        private String field5 = UUID.randomUUID().toString();

    }
}

我是御狐神,欢送大家关注我的微信公众号
<br/>

版权所有,禁止转载!

正文完
 0