JavaWeb之BeanUtil及底层实现
一、BeanUtils的概述
1.1为什么使用BeanUtils?
- BeanUtils是Apache开发的一种工具。
- BeanUtils可以帮助我们把属性封装到javabean对象的对应属性中,比如可以把提交的表单数据封装到一个实体对象中.封装时要求参数名称和javabean的属性名相同
- beanUtils 可以便于对javaBean的对象进行赋值。
- beanUtils 可以便于对javaBean的属性进行赋值。
- beanUtils可以将一个Map集合的数据拷贝到一个javabean对象中。
1.2导入jar包
-
BeanUtils是第三方组织写的工具类,要使用它,得先下载对应的工具JAR包。
-
相关的jar包(将三个jar包全部导入)
commons‐beanutils‐1.9.3.jar // 工具核心包 工具核心包
commons‐logging‐1.2.jar // 日志记录包 日志记录包
commons‐collections‐3.2.2.jar // 增强的集合包 增强的集合包
1.3、model2模式
二、BeanUtils的常用方法
方法 | 描述 |
---|---|
BeanUtils.setProperty(bean, name, value); | 其中bean是指你将要设置的对象,name指的是将要设置的属性(写成”属性名”),value(从配置文件中读取到到的字符串值) |
BeanUtils.copyProperties(bean, name, value) | 和上面的方法是完全一样的。使用哪个都可以 |
ConvertUtils.register(Converter converter , …) | 当需要将String数据转换成引用数据类型(自定义数据类型时),需要使用此方法实现转换。 |
BeanUtils.populate(bean,Map) | 其中Map中的key必须与目标对象中的属性名相同,否则不能实现拷贝。 |
BeanUtils.copyProperties(newObject,oldObject) | 实现对象的拷贝 |
三、BeanUtils的方法实例
3.1、BeanUtils之初体验BeanUtils.populate(bean,Map);
根据jsp页面传入的参数。将数据封装到 javabean 中。
1、login.jsp页面
<form action="/day09/login">
<table>
<tr>
<th>用户名:</th>
<th><input type="text" name="username" /></th>
</tr>
<tr>
<th>密码:</th>
<th><input type="password" name="password" /></th>
</tr>
<tr>
<th><input type="submit" value="登录" /></th>
</tr>
</table>
</form>
2、根据得到的数据进行封装
BeanUtils_login.java
public class BeanUtils_login extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
// 不使用BeanUtils
// String username = request.getParameter("username");
// String password = request.getParameter("password");
// User user = new User();
// user.setUsername(username);
// user.setPassword(password);
// 使用BeanUtils
// 1.创建一个bean对象
User user = new User();
// 2.得到所有的数据 类型Map
Map<String, String[]> map = request.getParameterMap();
// 3.使用BeanUtils的populate方法将数据自动封装到javabean中
try {
BeanUtils.populate(user, map);
} catch (IllegalAccessException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(user.getUsername());
System.out.println(user.getPassword());
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
3.2、BeanUtils.setProperty(bean, name, value);设置javaBean的参数
public class Demo1 {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
//频繁的使用getter/setter方法代码过于繁琐、臃肿!下面我们用BeanUtils来进行赋值
// 1.得到Person Bean的字节码对象
Class clazz = Class.forName("cn.syj.Bean.Person");
// 2.实例化一个Person实例
Object obj = clazz.newInstance();
// 3.使用自带的Date时间转化格式。
ConvertUtils.register(new DateLocaleConverter(), Date.class);// 这里面有自定义构造器的概念稍后会详细说明
// 4. 使用哪个BeanUtils对javaBean属性进行赋值
BeanUtils.setProperty(obj, "name", "张三");
BeanUtils.setProperty(obj, "age", "28");
BeanUtils.setProperty(obj, "brithday", "1996-07-16");
// 5.将对象封装的属性进行输出
System.out.println(obj);
}
}
运行结果
3.3、BeanUtils.copyProperties(newObject,oldObject)实现对象的Copy
1、把一个javaBean的属性拷贝到另一个javaBean对象中
public class Demo2 {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
// 1.获得Person bean的字节码对象
Class clazz = Class.forName("cn.syj.Bean.Person");
// 2.生成该字节码的一个对象
Object obj1 = clazz.newInstance();
Object obj2 = clazz.newInstance();
//将转换器注册到工具类中,使用了一个自带的转化器(可以自己编写)
ConvertUtils.register(new DateLocaleConverter(), Date.class);
BeanUtils.setProperty(obj1, "name", "张三");
BeanUtils.setProperty(obj1, "age", "18");
BeanUtils.setProperty(obj1, "brithday", "1996-08-08");
BeanUtils.copyProperties(obj2, obj1);
System.out.println(obj1);
System.out.println(obj2);
}
}
运行结果展示:
2、把一个map集合中的数据拷贝到javaBean中
// 把一个map集合中的数据拷贝到javaBean中
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class clazz = Class.forName("cn.syj.Bean.Person");
Object obj = clazz.newInstance();
ConvertUtils.register(new DateConverter(), Date.class);
Map<String, Object> map = new HashedMap<String, Object>();
map.put("name", "syj");
map.put("age", "26");
map.put("brithday", "1999-08-08");
// 使用日起转换器工具类(并将其进行注册)告诉转化器这个类型的可以进行转化
ConvertUtils.register(new DateLocaleConverter(), Date.class);
BeanUtils.copyProperties(obj, map);
System.out.println(obj);
}
运行结果展示:
3.4、自定义类型转换器
-
现在我们不想已经实现的类型转化器(比如日期Date和字符串String之间的转化)我们可以自己定义一个。
-
在自定义转化器的时候我们应该去实现Converter接口,并重写里面的方法(public T convert(Class type, Object value))参数:type:目前需要转换的数据类型;value:目前参数的值
-
步骤:
1、创建一个接口实现Converter接口
2、重写接口中的convert方法public <T> T convert(Class<T> type, Object value) 参数解读: 参数1:数据最终要转化的类型 参数2:要转化的数据(请求参数中的信息) 返回值:转换后的数据
3、将自定义的转换器注册到BeanUtils中。目的就是为了通知在BeanUtils封装数据到javaBean时,怎样将指定类型的数据进行封装
ConvertUtils.register(Converter converter,Class clazz)
参数1:自定义转换器对象 参数2:需要被转化的类型.class
实现将传入的日期字符串以Date的类型进行返回:
// 自定义日期转换器工具(匿名内部类进行实现)
ConvertUtils.register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) { // type:目前需要转换的数据类型, value:目前参数的值
if (type != Date.class)
return null;
String str = value.toString().trim();
if (value == null || "".equals(str)) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD");
Date date = null;
try {
date = sdf.parse(str);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return (T) date;
}
}, Date.class);
在使用的时候我们应该将其注册到转换器工具类中:
ConvertUtils.register(new DateConverter(), Date.class);
举例说明:
public class Demo1 {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
// 1.得到Person Bean的字节码对象
Class clazz = Class.forName("cn.syj.Bean.Person");
// 2.实例化一个Person实例
Object obj = clazz.newInstance();
// 自定义日期转换器工具
ConvertUtils.register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) { // type:目前需要转换的数据类型,
// value:目前参数的值
if (type != Date.class)
return null;
String str = value.toString().trim();
if (value == null || "".equals(str)) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD");
Date date = null;
try {
date = sdf.parse(str);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return (T) date;
}
}, Date.class);
// 3.使用自带的Date时间转化格式。
// ConvertUtils.register(new DateLocaleConverter(), Date.class);//
// 这里面有自定义构造器的概念稍后会详细说明
ConvertUtils.register(new DateConverter(), Date.class);
// 4. 使用哪个BeanUtils对javaBean属性进行赋值
BeanUtils.setProperty(obj, "name", "张三");
BeanUtils.setProperty(obj, "age", "28");
BeanUtils.setProperty(obj, "brithday", "1996-07-16");
// 5.将对象封装的属性进行输出
System.out.println(obj);
}
}
运行结果:
四、简单的分析底层的实现原理
4.1、BeanUtils底层是使用内省完成的(内省的底层是使用反射完成的)
- sun公司提供了内省(Introspector)的API, Apache去进行使用
- 访问javabean属性的两种方式:
1、直接调用setXXX或者getXXX方法;
2、通过内省(内省是BenaUtils的底层实现原理)其实就是BeanUtils工具包进行实现- 通过Introspector类获得Bean对象的BenaInfo==
- 然后通过BeanInfo来获取属性描述器(PropertyDescriptor)==
- 通过属性描述器(PropertyDescriptor)就可以取得某个属性对应的getter和settter方法
- 最后通过反射技术来调用方法
4.2、实现步骤:
(查询Jdk API)
Introspector----->BeanInfo----->PropertyDescriptor----->得到属性的getter/setter方法----->反射完成操作
// 1.根据Introspector得到javaBean信息
BeanInfo info = Introspector.getBeanInfo(user.getClass());
// 2.通过BeanInfo获得JavaBean属性的描述器
PropertyDescriptor[] pds = info.getPropertyDescriptors();
// 3.获取javaBean的属性读方法(get)和写方法(set)
for (PropertyDescriptor pd : pds) {
String name = pd.getName();// 获得属性名
System.out.println("bean属性名称:" + name);
Method get = pd.getReadMethod();// 得到javaBean的get方法
Method set = pd.getWriteMethod();// 得到javaBean的set方法
}
//4.利用反射完成数据的封装
if ("username".equals(name)) {
set.invoke(user, map.get(name)[0]);// 对于名称是写死的(将其使用一个Map进行引入)
}
if ("password".equals(name)) {
set.invoke(user, map.get(name)[0]);
}
举例:手动实现内省完成数据的封装
从login.jsp页面进行数据的传输,使用Introspector_Servelt.java进行接收
login.jsp
<form action="/day09/intr">
<table>
<tr>
<th>用户名:</th>
<th><input type="text" name="username" /></th>
</tr>
<tr>
<th>密码:</th>
<th><input type="password" name="password" /></th>
</tr>
<tr>
<th><input type="submit" value="登录" /></th>
</tr>
</table>
</form>
Introspector_Servelt.java
public class Introspector_Servelt extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 创建一个javaBean实例
User user = new User();
// 得到所有的请求参数 类型Map
Map<String, String[]> map = request.getParameterMap();
// 手动实现BeanUtil内省
try {
// 1.通过Introspector首先获得BeanInfo信息
BeanInfo info = Introspector.getBeanInfo(user.getClass());
// 2.通过BeanInfo获得属性的描述器集合
PropertyDescriptor[] pds = info.getPropertyDescriptors();
// 3.遍历描述器集合
for (PropertyDescriptor pd : pds) {
String name = pd.getName();// 得到属性名称
String[] values = map.get(name);// 得到指定的请求的参数集合
// 有值说明存在对应的set方法
if (values != null) {
// 得到set方法
Method set = pd.getWriteMethod();
// 利用反射将请求参数的值传入set方法中
set.invoke(user, values[0]);
}
}
} catch (IntrospectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(user.getUsername());
System.out.println(user.getPassword());
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
结果展示:
五、BeanUtils的总结
-
BeanUtils.populate(Object bean, Map properties)方法进行数据封装
-
一般其实不需要自定义转换器,只有在特殊情况下才会自定义转换器
1、如果form参数是String类型,javabean属性String类型 —- 调用 BeanUtils.populate 将form数据封装 javabean 2、如果JavaBean属性类型不是String —- 将form数据封装javabean 需 要转换器 —– 在BeanUtils API 提供很多默认转换器(完成常见转换) 3、如果需要转换类型非常特殊,可以通过自定义转换器完成 定义类实 现Converter接口,实现convert方法 ,在populate调用之前通过 ConvertUtils.register注册转换器
-
JavaBean就是一个存数据的地方
(1)通过JSP页面中的标签去存储form表单中的数据(action跳转到JSP页面,之后“useBean”,“setProperty”进行数据存储),再在JSP界面中通过“getProperty”显示数据。
(2)在JSP页面中通过action跳转到Servlet程序的url,在Servlet程序中通过BeanUtils工具类的相关方法对JSP页面中的表单信息进行存储。