JavaWeb之BeanUtils及底层实现

一、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模式

来源CSDN的Megustas_JJC

二、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页面中的表单信息进行存储。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值