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

浙公网安备 33010602011771号