apache--BeanUtils使用与底层代码演示

一、BeanUtils作用

1.1 JavaBean的含义

JavaBean:符合某种标准规范的java类

符合规范:

  • 1、类必须被public修饰
  • 2、必须提供空参构造方法
  • 3、成员变量必须为私有
  • 4、提供公共的setter和getter方法

代码示例:

public Class User{
    private Interge id;
    private String username;
    Private String password;
    public Interge getId(){ return id; }
    public void setId(Interge id){ this.id=id; }
    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; }
    
    @Override
    public String toString() {
        return "User{" +
            	"id='"+id+'\'+
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                   '}';
    }

1.2 属性和成员变量的不同

成员变量:上例代码中,id,username,password都是成员变量

属性:属性表示setter和getter方法截取后的产物,如getUsername去掉get,首字母变为小写的username表示属性,大多数情况下属性与成员变量相同,这里的相同指的是变量名相同,并非含义的相同,两者是不同的概念。

//这里的username指的是属性,对应的是setUsername();
User user=new User();
BeanUtils.setProperty(user,"username","张三")
    

1.3 需要掌握的方法

  • setProperty(bean对象,属性名,值) 设置Javabean的属性
  • getProperty() 获取Javabean的属性
  • populate(Object obj,Map map) 将map集合的键值对信息封装到对应的Javabean对象中

代码示例:

BeanUtils.setProperty(user,"username","张三");
//以“张三”为参数执行setUsername方法;
System.out.println(BeanUtils.getProperty(user,"username"));
//输出:张三。执行了getUsername方法

1.4 重点介绍:populate方法

标准Javabean方法:使用上述User类。

前台页面代码:

<%@ page contentType="Text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        
    </head>
    <body>
        <form method="post" action="/beanServlet">
            <input type="text" name="id">
            <input type="text" name="username">
            <input type="text" name="password">
            <input type="submit" value="提交"》
        </form>
    </body>
</html>

beanServlet代码:

import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;


@WebServlet("/beanServlet")
public class TestBeanUtils_Servlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//            先解决post方式的中文乱码问题
        req.setCharacterEncoding("utf-8");

        //传统的做法是使用  req.getParameter("username"); 等方法获取值 再使用带参构造方法或者setter方法封装JavaBean 当数据过多时比较繁琐

        //现在使用BeanUtils简化操作

        User user=new User();
        Map<String, String[]> map = req.getParameterMap();  //这个方法 封装的Map对象 key 是表单项name属性值 value是 输入值的数组
        try {
            BeanUtils.populate(user,map);   //就这一个方法就ok了 用起来很方便
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        System.out.println(user);

    }
}

如果在表单项中输入id=1,username=张三 ,password=123456

那么结果就是User{id='1',username='张三',password=’123456‘}

这个方法的常用场景就是servlet需要封装大量参数时,简化了取值和赋值两个过程,两行代码即可快速封装。

Map<String String[]> map=req.getParameterMap();
BeanUtils.populate(user,map);

注意:

1、表单项为复选框时,javabean成员变量要设置为数组。

2、表单的name属性值,JavaBean中成员变量值、属性值必须一一对应。

二、源码分析

2.1 java的内省

​ 在计算机科学中,内省是指计算机程序在运行时(Run time)检查对象(Object)类型的一种能力,通常也可以称作运行时类型检查。 不应该将内省和反射混淆。

​ 反射是在运行状态把Java类中的各种成分映射成相应的Java类,可以动态的获取所有的属性以及动态调用任意一个方法,强调的是运行状态。

​ 内省(IntroSpector)是Java 语言针对 Bean 类属性、事件的一种缺省处理方法。 JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。这些信息储存在类的私有变量中,通过set()、get()获得。

​ 开发中,一般做法是通过类 Introspector 的 getBeanInfo方法 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法,这就是内省机制。

2.2 内省常用类

  • java.beans.Introspector:Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。

  • java.beans.BeanInfo接口:希望提供有关其 bean 的显式信息的 bean 实现者可以提供某个 BeanInfo 类,该类实现此 BeanInfo 接口并提供有关其 bean 的方法、属性、事件等显式信息。

  • java.beans.PropertyDescriptor:PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。

    获取属性示例:

    //获取User-BeanInfo对象:beanInfo是对一个Bean的描述,可以通过它取得Bean内部的信息
    BeanInfo info=Introspector.getBeanInfo(User.class,Object.class);//不内省从父类继承的属性
    //获得属性描述器
    PropertyDescriptor[] pds=info.getpropertyDiscriptors();
    //遍历所有属性
    for(propertyDicriptor pd:pds){
        System.out.println(pd.getName());
    }
    
    

    操纵属性示例:

    User user= new User();
    PropertyDescriptor pd = new PropertyDescriptor("username", User.class);
    // 得到属性的写方法,为属性赋值
    		Method method = pd.getWriteMethod(); // setUsername
    		method.invoke(user, "张三");
    
    		// 获取属性的值
    		method = pd.getReadMethod(); // getUsername()
    		System.out.println(method.invoke(User, null));
    	}
    	
    
    
    

    获取当前操作的属性的类型

    			@Test
    		public void test3() throws Exception {
    		User user= new User();
    
    			PropertyDescriptor pd = new PropertyDescriptor("username", User.class);
    			System.out.println(pd.getPropertyType());
    			
    		}
    

    以上的操作略显繁琐,Apache组织开发了一套用于操作JavaBean的API——beanutils,这套API考虑到了很多实际开发中的应用场景,因此在实际开发中很多程序员使用这套API操作JavaBean,以简化程序代码的编写。

2.3 BeanUtils的底层代码演示

MyBeanUtils类

package cn.hncu.MyBeanUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
//一个很强的工具---能够将map中的元素封装成相对应的值对象!
public class MyBeanUtils {
            //map是已经封装好的HashMap集合,里面的数据都是存在的,cls也是从已知值对象的方法名(例如:User.class)
    public static<T> T populate(Class<T> cls,Map<String, Object> map) throws Exception {
        Object obj = cls.newInstance();//值对象必须规范(即值对象中必须有一个空参构造方法!)
        //获取cls中的所有属性
        Field flds[] = cls.getDeclaredFields();
        //遍历属性值与map中的属性值进行比较,看是否存在该属性
        for(Field fld:flds){
            //拿到每个变量值(包括私有变量)进行与map中的key进行比较,看是否能够找到相匹配的
            Object value = map.get(fld.getName());
            if(value == null){
                //说明在map集合中找不到在值对象中与之相匹配的属性值
                System.out.println(fld.getName()+"不存在,为空值!");
                continue;
            }
            //如果存在则找到相应的setter方法进行设置值的操作(给属性赋值的操作)
            //拿到属性值对应的方法名!!!!!很强的(这里也就是设计模中提到的值对象模板规范的原因)
            String methodName = "set"+fld.getName().substring(0, 1).toUpperCase()+fld.getName().substring(1);
            //再通过方法名字加上属性(成员变量)类型找到该属性值设置属性值的方法进行属性设置
            Method m = cls.getMethod(methodName, fld.getType());
            m.invoke(obj, value);
        }

posted @ 2021-02-26 01:59  废熊  阅读(192)  评论(0)    收藏  举报