有关Java中的BeanInfo介绍

网友投稿 770 2022-12-09

有关java中的BeanInfo介绍

有关Java中的BeanInfo介绍

目录1、javaBean介绍2、JavaBean的自省3、JavaBean内省工具Introspector4、JavaBean内省结果BeanInfo5、内省结果BeanInfo的类型6、Spring的BeanUtils.copyProperties7、BeanUtils并发问题优化8、BeanUtils Setter属性识别优化9、BeanUtils 性能测试

1、JavaBean介绍

维基百科JavaBean的定义:JavaBeans是Java中一种特殊的类,可以将多个对象封装到一个对象(bean)中。特点是可序列化,提供无参构造器,提供getter方法和setter方法访问对象的属性。名称中的“Bean”是用于Java的可重用软件组件的惯用叫法。要成为JavaBean类,则必需遵循关于命名、构造器、方法的特定规范。有了这些规范,才能有可以使用、复用、替代和连接JavaBeans的工具。规范如下:

有一个public的无参数构造器。

属性可以通过get、set、is(可以替代get,用在布尔型属性上)方法或遵循特定命名规范的其他方法访问。

可序列化。

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

public class PersonBean implements java.io.Serializable {

/**

http://* 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;

}

}

2、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的内省机制。

3、JavaBean内省工具Introspector

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

// 在Object类时候停止检索,可以选择在任意一个父类停止

BeanInfo beanInfo = Introspector.getBeanInfo(JavaBeanDemo.class,Object.class);

4、JavaBean内省结果BeanInfo

通过java的内省工具Introspector的getBeanInfo方法,我们可以获取一个JavaBean的内省BeanInfo,获取到的BeanInfo包含以下属性:

Bean的类相关信息

Bean的事件信息

Bean的属性信息

Bean的方法信息

额外属性信息

Component的图标

5、内省结果BeanInfo的类型

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

ApplicationBeanInfo:Apple desktop相关的JavaBean内省结果

ComponentBeanInfo:Java Awt组件的内省结果,如按钮等

GenericBeanInfo:通用的内省结果,JEE开发中的内省结果都为该类型

此外,Spring自定义了一个内省结果类型,叫ExtendedBeanInfo,主要用于识别返回值不为空的Setter方法。

6、Spring的BeanUtils.copyProperties

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

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

7、BeanUtils并发问题优化

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

Spring的BeanUtils在Java内省之上又添加了一层缓存,这层缓存使用ConcurrentHashMap实现,从而提高了内省的效率。

8、BeanUtils Setter属性识别优化

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

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

9、BeanUtils 性能测试

可以看出: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

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

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();

}

}

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

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();

}

}

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();

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java基础第四篇 封装与接口
下一篇:Java Runnable线程传参,实现让run访问参数
相关文章

 发表评论

暂时没有评论,来抢沙发吧~