由于在原来的ArticleServlet中当添加新文章或者更新原来的文章时,会存在大量的从add_article.jsp或者update_article.jsp中传递过来的参数,然后将这些参数设置到Article这个Bean中,然后再把这个Bean传到DAO中根据Bean的属性往数据库的table插数据。
当table(对应Bean)的字段太多(属性太多)时,不停的String title = request.getParameter("title");……然后再:Article a = new Article(); a.setTitle(title);……是个很繁琐的过程。所以我们使用apache-commons-beanutils这个工具来简化代码,继续封装。
1、简化后的ArticleServlet的add()方法为:
2、具体的实现封装在RequestUtil类中:
在RequestUtil中就进入重点了:
ArticleServlet中是这么调用RequestUtil的:Article a = (Article)RequestUtil.copyParam(Article.class, request);将request直接传入RequestUtil中(request是从add_article中传过来的,包含了Article这个Bean的各种属性)。在RequestUtil中首先new出一个Article的实例:Object entity = entityClass.newInstance();(在接下来的代码中给Article的属性各种赋值,最后再将这个Article return回ArticleServlet中,ArticleServlet再将Article传给DAO,进行插入数据库的过程)。
Map allParams = request.getParameterMap();
Set entries = allParams.entrySet();
for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
String name = (String)entry.getKey();
String[] value = (String[])entry.getValue(); //以上这段代码是从request中取出所有的数据。name是Article的属性名,而value即是属性值了。注意value是String【】类型的
if(value != null){
if(value.length == 1){
BeanUtils.copyProperty(entity, name, value[0]);
}else{
BeanUtils.copyProperty(entity, name, value);
}
}
(接下来先分析简单的BeanUtils功能:
Article a = new Article();
BeanUtils.copyProperty(a, "title", "这是标题");
BeanUtils.copyProperty(a, "leaveNumber", "30");
BeanUtils.copyProperty(a, "recommend", "true");
这些话的意思是:将第三个参数赋值给第一个参数里的第二个属性(将"这是标题"赋值给a这个Article的title属性)
title是String类型,"这是标题"是String类型,而leaveNumber是int类型的,"30"是String类型的,recommend是boolean类型,"true"是String类型的,可以看出BeanUtils可以在一定范围内自动的将String转换为int、boolean、……类型赋值给对应的属性。)
1、当name为普通的属性譬如title、source、clickNumber…这些String、int…类型,且传递过来的value是长度为1的String【】时,调用BeanUtils.copyProperty(entity, name, value[0]);
2、当name为普通的属性譬如title、source、…这些String、int…类型,且传递过来的value是长度不为1的String【】时,调用BeanUtils.copyProperty(entity, name, value);
3、当name为channels这种Set(List……)类型的属性时,BeanUtils.copyProperty是不能自动的将长度为1的String【】(即String)或者长度不为1的String【】类型的值copy进Set(List……)中的,得自己定义类型转换器。这样在以后调用BeanUtils.copyProperty()方法时,如果发现name的属性为类型转换器注册的属性(此例为Set),则不执行默认的BeanUtils.copyProperty,而是执行类型转换器,将类型转换器的返回值即一个Set赋值给name属性。
ChannelsSetConverter(类型转换器)的代码:
首先ChannelsSetConverter实现了Converter接口,Converter接口中只有一个convert()方法,现在ChannelsSetConverter来重写这个方法,第一个参数Class targetClass表示目标类型,即BeanUtils.copyProperty(entity, name, value);中的name的类型(在这个例子中name为channels,它的类型为Set),第二个参数 Object value为BeanUtils.copyProperty(entity, name, value);中的value。在注册ConvertUtils.register(new ChannelsSetConverter(), Set.class);后,当调用BeanUtils.copyProperty(Object bean, String name, Object value);这句时,如果name的属性跟注册ChannelsSetConverter时的Set类型一致的话,不通过自动转换来给name赋值,而是调用ChannelsSetConverter类型转换器,用户自定义后在将返回值返回给BeanUtils.copyProperty(entity, name, value)中的name属性。
如果在add_article.jsp页面中添加文章时只选择了一个channel,那么value即是String,如果文章选择了多个channel,则value为String【】,我们统一都转换为String【】,注意String是不能强制类型转换,需要channelIds = new String[]{(String)value};,在add_article.jsp页面中传递过来的是channel的id组成的String【】
接下来根据id参数设置channels,返回回去赋值给Article中的channels。
接下来就要注册ChannelsSetConverter类型转换器,注册应该是全局性的,将它放到统一的入口地方InitBeanFactoryServlet中(tomcat启动时候首先调用的servlet,因为在web.xml中<load-on-startup>0</load-on-startup>)