这是本系列的最后一章,大象对示例进行适当的扩充并说明。
其实到第四篇,对于示例的说明就已经全部讲完了,如果按照这样的例子,很难有什么值得学习的地方。大象本着写点有用东西的原则,在这章,对示例进行一下适当的扩充并说明。
第五部分:扩展框架
paramsPrepareParamsStack拦截器栈
paramsPrepareParamsStack
这个拦截器栈是在
struts2-default.xml
中定义的,里面包含了很多个拦截器,最重要的是这三个:params、prepare、modelDriven。我们只要记住这样几点。
params:它负责将请求参数值设置到Action中与之同名的属性中。
prepare:当Action实现了Preparable接口时,这个拦截器就会调用prepare()方法。如果你有想在execute()方法之前执行的逻辑处理,它就可以帮你完成这个功能。
modelDriven:如果Action实现了ModelDriven接口,这个拦截器就会把getModel()方法中的返回结果压入值栈。这就意味着,可以在结果页面上直接使用model对象的属性。
它的执行顺序是这样的
首先,params拦截器会给action中的相关参数赋值,如id,username,password等等。
然后,prepare拦截器执行prepare()方法,prepare()会根据参数,如id,去调用相关的方法,设置model对象。当然,实现的这个接口方法由你自己来定义,不局限只设置model之类的功能。
接着,modelDriven拦截器会将model对象压入值栈,因为它是把getModel()方法中的返回结果放到值栈中,而这个方法的返回类型是个泛型参数,在实现ModelDriven接口的时候,可以给它指定一个具体的对象类型,因此返回类型也将是这个指定的对象类型,如ModelDriven<User>
最后,params拦截器会将参数再赋值给model对象。
思考修改与保存这两种动作。当点击人员修改时,请求为:user!input.action?id=1,params拦截器会将id参数值设置到Action中的id属性,请一定注意,id属性要有set()方法,然后prepare拦截器开始在prepare()方法中,根据这个id值取得User对象,接着modelDriven会调用getModel()方法,此时,方法中返回的是user对象,所以会把user加入到值栈中,最后再执行一次params拦截器,但这时没有其它的参数值需要赋值给user对象,所以程序会接着往下走,这里假定没有其它的业务逻辑,执行返回,字符串为input,根据前面讲的插件知识,结果页面为user-input.jsp,那么就跳转到修改页面了,而且页面中表单域将显示数据库中的值。如果理解了修改,那么保存也就清楚了。
prepare()方法虽然不错,但是也有弊端,那就是它会对Action中的每个方法都进行拦截,不管你是执行execute还是input,还是其它的自定义方法,它都会对其拦截,这当然不是我们所希望的。那有没有更好的方式?答案是肯定的,请接着往下看。
prepareMethedName
使用prepare拦截器的另一种形式,在prepare名称后面加上需要拦截的方法名。比如,你要拦截input方法,可以写成prepareInput,需要拦截save方法,就写上prepareSave。采取这样的方式后,将会在执行这些方法之前时,才对它们进行拦截。
例如,请求role!input.action,会执行RoleAction中的input方法,如果我们设置了prepareInput方法,则会先进入此方法执行,执行完后再回到input方法往下执行。
请注意,在使用这种方式时,Preparable接口定义的prepare()方法体内不要含有任何代码,就是说给这个方法一个空实现。这样,它就什么都不做,所有的拦截处理就全部交由相应的prepareMethedName来完成。
![](https://i-blog.csdnimg.cn/blog_migrate/6f033771e633d7ea147f5b1126fc4866.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/b20209a1d23452511cdfb0d9a5a55491.jpeg)
它们在每个对应的方法之前执行。prepareEntity就是来初始化实体对象,然后由modelDriven拦截器将getModel()方法中的返回结果放入值栈,当返回页面时,就可以直接取值了。
StrutsAction
重新定义一个基类,里面封装大部分的通用操作,主要依靠泛型来实现,将hibernateDao注解进来,通过继承这个基类进行基本的CRUD操作。本文末尾提供示例源码下载,里面有详细的注释,这里我只贴出部分重要代码进行说明,为了行文需要,有些注释去掉了,但源码里面都有,请大家放心。
![](https://i-blog.csdnimg.cn/blog_migrate/a3883bea29ec0fb2ad69e9d2e28114c1.jpeg)
通过扩展ActionSupport,使用泛型参数,构造函数根据反射得到T的具体类型。
![](https://i-blog.csdnimg.cn/blog_migrate/aff33fcb570544d3c620fedb3222c682.jpeg)
这就是默认的执行方法,基本的操作,在这个超类里面都进行了定义,每个方法里面设置的以do开头的方法,是方便让子类进行覆盖,当基本的业务逻辑无法满足我们的需求时,就可以在子类重写这些方法。
![](https://i-blog.csdnimg.cn/blog_migrate/b8a2d85d73c7757e80e02377cf41de69.jpeg)
方法有默认实现,主要是列表显示,保存和删除,新增和修改已经有getModel()方法取得实体,在页面上使用s标签就可以直接取值,除非有特殊的业务需求,否则不用覆盖doInputEntity()、doViewEntity()方法。请注意,当需要实现自己的逻辑时,只需要覆盖上面定义的这些方法,而不用重写execute、input之类。
我对HibernateDao又进行了适当的扩展与修改,提供了更多的基本封装方法,不过大家还可以继续添加。里面都有详细的注释,这里就不在赘述了。
功能扩展
我对例子做了两个功能,一个是角色表的增加、修改、删除、查看,另一个就是用户表的查询。可以从源码中看到,我在
RoleAction
中没有写一行关于增删改查的代码,因为它属于基本操作,超类中已经封装好了,所以这部分的代码都省了。对于用户表的查询,我覆盖了
doListEntity()
方法,在业务层进行条件封装,执行查询,返回结果。
![](https://i-blog.csdnimg.cn/blog_migrate/5d54510ed70fc268c4da68d36947bc76.jpeg)
这个list就是在超类中定义的,因为默认实现中也用到了list,另外list有一个get方法,用于在页面中显示。如果不想采取方式取得list集合,就重写doListEntity()方法。这里说明下,我是没有加分页功能的,大家可以按自己的方式添加分页查询。
在用户查询方法中,我使用的是QBC对象查询,因为这种方式很简洁,不过我在HibernateDao中也写了HQL和SQL方式的查询方法,并进行了封装,可以很方便的调用。
![](https://i-blog.csdnimg.cn/blog_migrate/e289826875febcf332306194b505d33c.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/2f560457c0f26edce5abf9db6d5a7e18.jpeg)
这个queryResult方法的定义,你可以改为传递用户名与角色ID的参数,大象在这里就是为了方便,直接使用Request请求。这里userDao调用的query方法是在HibernateDao里面封装的,因为继承了HibernateDao,就直接在Service层拿来用了。至于具体的,可以去看源码。
页面部分没有进行大的调整,主要是将role-list.jsp重命名为role.jsp,因为使用的是超类的默认实现。添加了role-input.jsp和role-view.jsp两个文件,并在user.jsp中,加入了查询条件。这些代码都很简单就不再贴了,而且前一篇也贴过一部分。
对于这个例子的完整讲解说明就到此结束了。大象还想补充说明一下,这个例子只适用于学习,不适合商用,想在实际项目中运用,还需要对框架做大量的改造工作。本系列只是基于SSH2入门学习之用,源码中不含jar包,下图是本例中所需的最少jar文件,大家只要下载了spring、struts、hibernate三个完整压缩包,那么这些jar基本上都包含了。
![](https://i-blog.csdnimg.cn/blog_migrate/ea8455b1f259be61d6c56b07dc786572.jpeg)
发布并启动Tomcat,然后输入访问地址:http://localhost:8080/ssh2 运行该示例,看看效果。