OA的学习--第三天的内容--Struts的整理回顾

来源:互联网 发布:银翼杀手剧情解析 知乎 编辑:程序博客网 时间:2024/05/22 04:38

    现在继续第三天的内容总结,刚刚给第二篇博客起名费了半天劲,到底叫什么好呢,因为里面的内容不知道该如何概括.最后决定,那天学到的最有用的东西是什么博客就叫什么名.

    第三天是从第19集到27集,主要内容是1.继续完成岗位管理的增和该功能,以及2.讲解struts的整体回顾,我理解就是讲原理;还有就是3.改进ModelDriven,前文说过,struts若要封装一个id,接收前台传递的值,是在Action中,写了一个id属性,并写了getter,setter来封装的。而若是页面需要封装多个对象,这么一个一个写也太麻烦了,所以用ModelDriven一次性封装一个实体.还有就是4.加上美工设计的页面效果,就是把演示的静态页面,改改变成可以用的JSP页面,然后就有很漂亮的效果了,我为了要效果,上网下了这个OA项目的源码;5.合并添加和修改页面,用同一个页面,6.继续开始另一个功能,部门管理,就是重复岗位管理的那些操作.

岗位管理的增和改功能

    之前做完了添加和列表功能,然后就需要做添加和修改了.这个和之前的代码基本一样,但是有一点不一样的就是,editUI方法,这个是跳转到修改页面的方法,为了页面显示数据,所以需要准备数据.所以需要和list方法一样,将数据放到ActionContext的getContext()中,只是list放到的是Map中,但是editUI只是修改一个实体,所以只要放到值栈中就能轻松获取.而放到Map中用put,放到值栈中用push.

//三个接收前台传递数据的属性 private Long id; private String name; private String description;  ...getter,setter省略// 添加public String add() throws Exception { //封装到对象 Role role = new Role(); role.setName(name); role.setDescription(description); roleService.save(role); return "toList";}// 添加页面 public String addUI() throws Exception {return "addUI";}//修改public String edit() throws Exception {Role role = roleService.getById(id);role.setName(name);role.setDescription(description);roleService.update(role);return "toList";} //修改页面public String editUI() throws Exception {// 准备回显的数据Role role = roleService.getById(model.getId());ActionContext.getContext().getValueStack().push(role);return "editUI";}

    这样Action中的代码就写完了,然后将Service中的代码补充完整,就可以让addUI和editUI中写代码了

    这是addUI页面,

<s:form action="role_add"><s:textfield name="name"></s:textfield> <br/><s:textarea name="description"></s:textarea> <br/><s:submit value="提交"></s:submit></s:form>

    这是editUI页面,这个由于editUI会准备数据role,所以依次赋值到id,name和description上.然后,修改完毕之后,提交的时候,这三个值也都会返回到后台.

 <s:form action="role_edit"><s:hidden name="id"></s:hidden><s:textfield name="name"></s:textfield> <br/><s:textarea name="description"></s:textarea> <br/><s:submit value="提交"></s:submit></s:form>

    再在list.jsp页面写上添加的链接<s:a action="role_addUI">添加</s:a>和修改的链接<s:a action="role_editUI?id=%{id}">修改</s:a>这个功能就完成了.

Struts2的整体回顾

    上面完成增和添加功能,不算费劲.那么他们的原理是什么呢.为什么用list页面用#roleList,id用%{id}来获取,为什么修改页面用name="name"这种就能获取值?

    分析看看struts中的Action,拦截器,值栈和OGNL表达式以及ModelDriven方案等,来说明原因.首先是一幅图,是这样子的.

    首先,用户的每个请求,都会经过这么一个过程.先经过层层的拦截器,然后到达Action,然后再Action中准备数据,返回Result,最后显示到JSP页面上.

    其中第一个拦截器会create创建ValueStack值栈对象,并会进行init初始化.初始化时会初始化一些数据到Map中,包括request,session,application,attr,parameters等等,还有对象栈中也初始化了2个对象,一个是action,还有一个其他对象(我不知道具体是什么).

    第二个是一个叫ModerDriven的拦截器,用于封装Model,他会往对象栈中写入model对象,放到栈顶;

    然后还有params拦截器,用于封装参数,他会将参数值,从栈顶开始找,和属性一一对应上.举例:如地址是红色那部分那样,xx.action?id=3&name=ab&age=20&xx=00,总共传递了id,name,age,xx4个参数,而对象栈中有model和action对象,他们分别有id和name,id和age属性.然后经过params拦截器,他会从栈顶开始找,是否有id属性,若有就把值赋给id;然后就会去找下一个属性,若是找不到,则栈里面就没有这个参数.若此时要获取xx的参数值,就可以通过Map中的parameters,用get(xx)会就可以拿到参数值.因为parameters里面会存放所有的参数,不管这个参数在对象栈中是否有对应的属性.

    最后经过层层的拦截器,ValueStack中就准备了很多的数据了.然后到达Action,

    Action中如何利用ValueStack中的数据?通过ActionContext对象的getContext(),或者getContext().getValueStack()就能获取到值栈中的值,而JSP页面通过OGNL就可以获取到值.

    而对值如何操作呢?其中使用ActionContext的getContext()的put可以让Map中写入值,get可以获取值;当然用ActionContext的getContext()的getValueStack()也是可以操作Map的,同样也是get获取值,put写入值.而对对象栈的操作,就只能用getValueStack,用push写入值,用pop读取值.

    而JSP的页面的OGNL如何找到Map和对象栈中的对象.这分为2种,格式和语法.格式上,在struts.xml,OGNL的格式为${ongl},而在JSP页面中,OGNL的格式为%{ognl},一个用了键盘4,一个键盘5.而且这个%{ognl}只能是写在struts2的自定义标签的属性中,写到外面不能解析.然后语法,写user相当于findValue("user"),这个会从对象栈的栈顶开始找这个属性,找到就返回,找不到就去Map中根据key来找.#user,表示从Map中找key为user的;还有#user.name表示从Map中找到key为user的对象的name属性.

    然后看这段代码(这是list.jsp页面的表格中的数据),就是这样id等属性就是从栈顶开始找;而像遍历s:iterator这个自定义标签本身有个作用,就是会将循环出来的对象自动的放到栈顶,循环完就从栈顶拿到,然后再将下一个循环到的对象放到栈顶.所以id,name,description就可以从栈顶对象中拿到值.

    而回显问题,图中选中的部分,value="%{name}"就会去从对象栈中栈顶开始找name属性,而这里默认就是这样的行为,就是说写不写这个value效果是一样的.所以只要把需要回显的对象放到栈顶,那么他就会使用ognl从栈顶找到各自的属性,然后value中有值,就会显示出来.

    而这段代码写成这样,将s:property用EL表达式替换,也是有效果的.

    这个是因为,虽然id,name都不在request对象里面,但是他们在值栈里面.而EL表达式的查找顺序是这样的,先找page,找不到就继续往下找,然后找request,然后就是valueStack的对象栈和Map,只有还找不到才会去找session和application.所以他有效果是因为他最终会找到值栈中去找.若要写的简单用EL表达式就可以了.查找顺序就是:page-->request-->valueStack-->session-->application.

ModelDriven

    看上面的代码,页面中封装的id,name和description,可以通过model来封装,因为ModelDriven拦截器会将model放到栈顶去,然后再Action中就可以用ActionContext来获取到放到栈顶中的值了.

    将Action实现ModelDriven,然后实现它的getModel方法.实例化一个Role类型的model对象,返回,放到对象栈栈顶.然后下面使用就可以直接用model.getId()就可以了.而之前的id,name,description都不用再声明了,getter,setter方法也不用写了.

 public class RoleAction  extends ActionSupport implements ModelDriven<Role> {            // private Long id; //getter,setter也注释了 private Role model = new Role(); public Role getModel() {      return model; }/** * 修改 *  * @return * @throws Exception */public String edit() throws Exception {Role role = roleService.getById(model.getId());role.setName(model.getName());role.setDescription(model.getDescription());roleService.update(role);return "toList";}}

    这个在后面还可以提取,因为每个Action都要实现ModelDriven,那么提取一个公共的BaseAction,由这个BaseAction来继承ActionSupport和实现ModelDriven<T>,而实现getModel方法用反射来搞定.然后RoleAction只用继承BaseAction,就可以省了各Action中的getModel()的代码了.并且注入Service也可以写到BaseAction这里,这样1个Action使用多个Service也不用写多遍代码了.而BaseAction的代码就是这样.

public abstract class BaseAction<T> extends ActionSupport implements ModelDriven<T> {//=============Service实例的声明=============@Resourceprotected RoleService roleService;  //=============ModelDrvien的支持=============protected T model;public BaseAction() {//通过反射获取model的真实类型//通过反射创建model的实例try {ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();Class<T> clazz = (Class<T>)pt.getActualTypeArguments()[0];model = clazz.newInstance();} catch (Exception e) {throw new RuntimeException(e);}}public T getModel() {return model;}}

    第4个美工设计的页面效果,我就不弄了,我是直接拷的源码的,没有改过.

合并添加和修改页面

    上面的addUI和editUI页面可以发现,这两个页面的代码基本是一样的,除了1.form提交的action有一些不同,还有就是editUI,编辑需要回显数据,编辑完之后需要将id传回去,所以页面有一个接收id的隐藏域.所以这样一改两个页面就可以合并了.

<s:form action="role_%{ id == null ? 'add' : 'edit' }"><s:hidden name="id"></s:hidden>    <div class="ItemBlock_Title1"><!-- 信息说明<DIV CLASS="ItemBlock_Title1">    <IMG BORDER="0" WIDTH="4" HEIGHT="7" SRC="${pageContext.request.contextPath}/style/blue/images/item_point.gif" /> 岗位信息 </DIV>  -->    </div>        <!-- 表单内容显示 -->    <div class="ItemBlockBorder">        <div class="ItemBlock">            <table cellpadding="0" cellspacing="0" class="mainForm">                <tr>                    <td width="100">岗位名称</td>                    <td><s:textfield name="name" cssClass="InputStyle" /> *</td>                </tr>                <tr>                    <td>岗位说明</td>                    <td><s:textarea name="description" cssClass="TextareaStyle"></s:textarea></td>                </tr>            </table>        </div>    </div>        <!-- 表单操作 -->    <div id="InputDetailBar">        <input type="image" src="${pageContext.request.contextPath}/style/images/save.png"/>        <a href="javascript:history.go(-1);"><img src="${pageContext.request.contextPath}/style/images/goBack.png"/></a>    </div></s:form>

    由于这个时候已经加上了美工效果,所以代码是这样的.主要代码是,不论是添加还是修改,都加上id的隐藏域,这么有id就存着,没id就空着.所以可以根据id是否有值,来判断是添加还是修改,就是form标签中的role_%{id==null?'add':'edit'}这句代码.其他的就是美工效果,东西没变.

    最后,addUI.jsp和editUI.jsp变成了一个saveUI.jsp页面,所以Action的addUI()和editUI()的返回值变成"saveUI"字符串,而struts.xml也要去掉addUI和editUI的result配置,加上一个saveUI的配置,对应到saveUI.jsp页面.

部门管理功能

    到此岗位类别功能基本完成.然后24集开始讲部门管理的功能。部门管理就比岗位类别稍微复杂一点,因为涉及到自关联,树形结构。具体代码,我就不一一细细介绍了,很多内容和岗位类别都是类似的,所以,后面只介绍一些不一样的地方。

    针对实体设计,总结了一下流程。分析实体,一般是看他是不是用于增删改查,实体的关系,一般用了其他实体,就有关系;分析里面的属性,先逐渐,然后关联关系,关联关系是一条线,有两端,所以要写两个属性,不管是两个属性写到一个实体中(自关联),还是分别写到两个实体中。然后,根据页面添加一般属性,以及一些因为特殊要求,解决某些问题,设计的属性.


    然后按照上面的规则,画出了类图.


    其中,对于Department,前面id,name,description都是一般属性,users,parent和children都是关联关系属性,包括和User的一对多,和自己的一对多.

映射文件

    实体设计完了,这么多一对多,多对多的关系,映射文件该如何写?

    如下图所示,映射文件对比实体来写.一般属性好处理,然后关联属性就是.先写一个注释的模板.<!-- **属性,本类与?的? -->,如举例department属性,本类与Department(类)的多对一关系,而下面就是roles属性,本类与Role的多对多关系.


    然后对于这些关联关系,总结了格式和模板。其中多对一,用many-to-one标签,写上name(one端的属性名),class(one端的类名),column(关联列,外键),至少三个.而一对多的模板是,一对多,多这边是一个集合,并且实体属性用中的是set,所以用set,也是3个属性。many端的属性名,many端的外键 ,many端的类名。然后多对多的模板是和一对多基本一样.但是由于多对多是会多一张表,并且会有2个外键.所以在name旁边加上table属性,而除了外键key之外,还有一个column外键.


    然后就可以根据模板填空了.将?1填入到name中,?2填入class.然后剩下column.

    对于多对一,在多端放入一端的主键作为外键.所以one端生成外键.然后对应多端,外键名应该一样.

    如下,User和Department是多对一的关系.在one端department属性中生成外键,departmentId,然后放到多端users中,对应上.


    而多对多,在一对多的基础上.首先,写一个表名,只要两边一致就可以.以及各自生成外键,放到对方中.

    举例:Role和User是多对多的关系。在Role映射文件中,生成key外键roleId,然后放到User的column外键中;然后再User映射文件中,生成key外键userId,放到Role的column外键中.或者这么理解 在roles属性中生成column外键roleId,放到users属性的key外键中。然后在users属性中生成外键userId,放到roles的key外键中。


    对比,可以发现对于key column,就是和class不搭的,这才是真正的外键,而column是搭配的,所以这个应该是实体的主键生成的外键.如class为Department,column为DepartmentId;而class为User,key column为departmentId,或者key column为roleId,而column为userId。

    上面都是对于映射文件,怎么更方便的写出做的处理。但是,还是有些不懂原理。若是懂了原理,应该就能更得心应手。

    分析,对于many-to-one和one-to-many该如何确定,注释中可以说明,是从本类出发,本类和其他类的关系.many-to-one,说明本类是many对应的是one.所以根据表结构,many端持有one端的外键,从表上的显示,就是many表中,有一列外键,该外键是one表的主键.而many-to-many,从表上显示,就是多了一张第三张表,而且many和many表中的主键在第三张表中都是外键.

    现在差不多懂了,不过即使不懂也没有太大的关系,因为我们现在用的基本都是注解@ManyToOne,@ManyToMany,这种写起来感觉方便好多,除了有的时候,因为要控制one和many端,只有一方来维护关系,有时候搞不清楚,mappedBy该在哪里写.mappedBy的意思是说,不管理这个关系,一般是many端来维护关系,而one端不维护关系.

    所以mappedBy写的就是one类在many类中的属性名,而且是写在one类的many属性(get方法)上.

    举例:若Order和OrderItem是一对多的关系,而根据关联关系生成属性的规则,在Order中有一个set类型的orderItem,在OrderItem类中有一个Order类型的order.然后就是在Order类的order属性上写上.

public class Order {@OneToMany(mappedBy="order" …).public Set<OrderItem> getOrderItems() {…}}public class OrderItem {      private Order order; …}

    总结就是,在one类中说明自己不想维护关系.

    以上就是第三天的OA学习内容,里面最重要的就是了解struts2的原理。最后,我对映射文件的分析,感觉让我对映射文件印象更深刻了。本来14号加把劲就可以写完了,非得剩一点拖着,又拖了5天了,今天都19号了,自制力还是不行。

    这几天学习了4集赵栋的OA,里面和汤阳光讲的很类似,但是深度不一样,汤阳光讲的特别容易接受很好懂,一步一步来,而赵栋喜欢往底层挖,分析底层是如何实现的。还有就是他们的配置文件的写法,相比赵栋的,汤阳光写的配置文件真的是非常简单。这里已经用了扫描实例化bean了,那边还在一个一个写bean,让spring管理,不过老实说,我真忘了,一个一个写的那种写法具体然怎么写了,所以也是一次很好的学习。还有两天的泛型和AOP的学习,对于泛型的泛型方法,一些泛型约束了解了下,对于静态代理和动态代理JDK和CGLIB,进行了分析.不过,只能说,两天只能开个窗,看一看,但是要摸一摸,还得再花时间,研究研究.    

0 0
原创粉丝点击