Struts2-Action: 搭建MyEclipse框架,在Window/Prefrence中设置Server/Tomcat的目录,然后设置java选项卡中JDK的路径。 Struts的运行机制: URL请求通过HTTP协议发送给TOMCAT(假设我们的url为http://localhost:8080/$webapplication的名字$/hello), 根据URL中请求的webapplication再交给对应的webapplication处理,然后参考其web.xml的配置,其中有: ... <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> ... 被org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter接受,它又会参考struts.xml的namespace,再根据其下对应的action和result, 将请求转到对应的JSP文件。 struts的作用就是将请求和视图分开。 struts.xml解释: <struts> <constant name="struts.devMode" value="true" /> <package name="front" extends="struts-default" namespace="/front"> //package区分重名的情况;namespace不为空时必须用'/'开头,当想访问这个namespace中的action就需要在前面带上这个路径 <action name="index"> <result>/Namespace.jsp</result> </action> </package> <package name="main" extends="struts-default" namespace=""> <action name="index"> <result>/Namespace.jsp</result> </action> </package> </struts> 例如要访问name="index"的action,则url为http://localhost:8080/$webapplication的名字$/front/index namespace决定了action的访问路径,不写时默认为"",可以接收所有路径的action namespace可以写为/,或者/xxx,或者/xxx/yyy,对应的action访问路径为/index.action,/xxx/index.action,或者/xxx/yyy/index.action. namespace最好也用模块来进行命名。 <constant name="struts.devMode" value="true" /> <package name="front" extends="struts-default" namespace="/"> <action name="index" class="com.bjsxt.struts2.front.action.IndexAction1"> //当请求这个action的时候,找到对应的class,执行它里面的execute()方法,返回值时String,就把他当成一个Action。 <result name="success">/ActionIntroduction.jsp</result> </action> </package> 具体视图的返回可以由用户自己定义的Action来决定。 具体的手段是根据返回的字符串找到对应的配置项,来决定视图的内容。 具体Action的实现可以是一个普通的java类,里面有public String execute方法即可。 或者实现Action接口。 不过最常用的是从ActionSupport继承,好处在于可以直接使用Struts2封装好的方法。 Struts1和Struts2的一个区别:Struts2的每一个Action都是新NEW的,而Struts1不是,故2解决了线程同步问题。 struts2中的路径问题是根据action的路径而不是jsp路径来确定,所以尽量不要使用相对路径。 虽然可以用redirect方式解决,但redirect方式并非必要。 解决办法非常简单,统一使用绝对路径。(在jsp中用request.getContextRoot方式来拿到webapp的路径) 或者使用myeclipse经常用的,指定basePath: <% //获取根目录路径 String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <base href="<%=basePath%>" /> //设置当前页面下的链接的根路径 Action执行的时候并不一定要执行execute方法。 可以在配置文件中配置Action的时候用method=来指定执行哪个方法 也可以在url地址中动态指定(动态方法调用DMI)(推荐):!+方法名 前者会产生太多的action,所以不推荐使用。 使用通配符可以将配置降到最低,不过一定要遵守“约定优于配置”。视频14。 在action中传参数有三种方法。 值堆栈中每一个元素都有一个名字和一个值。 //用javascript指定这个form提交的action。 <input type="button" value="submit1" onclick="javascript:document.f.action='login/login1';document.f.submit();" /> ----LoginAction1.java---- public class LoginAction1 extends ActionSupport { private Map request; private Map session; private Map application; public LoginAction1() { //利用这种方法发到request(类型为Map),session,application request = (Map)ActionContext.getContext().get("request"); session = ActionContext.getContext().getSession(); application = ActionContext.getContext().getApplication(); } public String execute() { //在处理请求的时候往其中放值,在前台可以访问 request.put("r1", "r1"); session.put("s1", "s1"); application.put("a1", "a1"); return SUCCESS; } 前台页面: ... //利用<s:property value="#request.r1"/>访问,也可以用传统的方式<%=request.getAttribute("r1") %>来取值 <s:property value="#request.r1"/> | <%=request.getAttribute("r1") %> <br /> <s:property value="#session.s1"/> | <%=session.getAttribute("s1") %> <br /> <s:property value="#application.a1"/> | <%=application.getAttribute("a1") %> <br /> ... ----LoginAction2.java---- ****最常用的方法**** package com.bjsxt.struts2.user.action; import java.util.Map; import org.apache.struts2.interceptor.ApplicationAware; import org.apache.struts2.interceptor.RequestAware; import org.apache.struts2.interceptor.SessionAware; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class LoginAction2 extends ActionSupport implements RequestAware,SessionAware, ApplicationAware { private Map<String, Object> request; private Map<String, Object> session; private Map<String, Object> application; public String execute() { //直接可以用(设计思想IoC或者DI),需要实现RequestAware,SessionAware, ApplicationAware借口 request.put("r1", "r1"); session.put("s1", "s1"); application.put("a1", "a1"); return SUCCESS; } //利用setRequest(Map<String, Object> request)设置request public void setRequest(Map<String, Object> request) { this.request = request; } public void setSession(Map<String, Object> session) { this.session = session; } public void setApplication(Map<String, Object> application) { this.application = application; } } 在struts.xml中增加以下语句可以解决中文编码问题: <constant name="struts.i18n.encoding" value="GBK" /> <!-- internationalization --> <include file="login.xml" /> //用于包含另外一个.xml文件到struts.xml文件中 <default-action-ref name="index"><*default-action-ref>定义默认Action,当找不到指定Action(或者不指定Action)时,转到那个package下的Action Action总结: 1.实现一个Action的最重用的方式:从ActionSupport继承 2.DMI动态方法调用+! 3.通配符:*asd*--{1}{2} *_*--{1}{2} 4.接受参数的方法(一般用属性或者DomainModel来接受) 5.简单参数验证addFieldError 一般不适用Struts2的UI标签 6.访问Web元素 Map类型 loc(最常用) 依赖Struts2 原始类型 loc 依赖Struts2 7.包含文件配置 8.默认Action处理 result可以指定类型,默认为dispatcher(用服务器跳转到结果页面,不可以是Action),还有: redirect(不可以跳转到Action),chain(跳转到一个Action),redirectAction(跳转到另外一个Action)。 以上四种最常用,前两中最最常用。还有freemarker,httpheader,stream,velocity,xsit,plaintext,tiles等类型。 客户端跳转和服务器端跳转--视频27 dispatcher和chain为服务器端跳转,而redirect和redirectAction为客户端跳转。 chain类型跳转到另一个Action要带参数: <result type="chain"> <param name="actionName">aasdafa</param> //指定Action的名字 <param name="namespace">/asdasd</param> //指定Action的NameSpace </result> global-results用于处理包里的共有结果集,extend用于继承其他package中的global-results。 动态指定result:在Action实现类中可以指定字符串参数来指向JSP页面地址,如: ... private String r; public String execute() throws Exception { if(type == 1) r="/user_success.jsp"; else if (type == 2) r="/user_error.jsp"; return "success"; } ... 在struts.xml中, ... <action name="user" class="com.bjsxt.struts2.user.action.UserAction"> <result>${r}</result> </action> ... 来确定result的展示页面,因为Action中的参数都会存放在值堆栈中。 在index.jsp中直接用: <li><a href="user/user?type=1">返回success</a></li> <li><a href="user/user?type=2">返回error</a></li> 来给action实现类中的参数传递参数。 一次request中若有服务端跳转,则跳转前后的所有Action公用值堆栈,故各个Action之间不需要传递参数。 但若跳转到jsp页面所传递的参数不存放在值堆栈中,只能利用actioncantext中取值。 ... <action name="user" class="com.bjsxt.struts2.user.action.UserAction"> <result type="redirect">/user_success.jsp?t=${type}</result> </action> ... ... from valuestack: <s:property value="t"/><br/> //从值堆栈中取值 from actioncontext: <s:property value="#parameters.t"/> //从actioncantext中取值 ... OGNL(object grapg navigation language)表达式: 必须为Action类传递参数,它才会构造(new)这个对象,此时必须有空的构造方法。 也可以不传递参数而自己直接在类中构造。 访问静态方法需要在struts.xml中设置如下语句: <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant> 访问静态属性和静态方法: <s:property value="@com.bjsxt.struts2.ognl.S@s()"/> <s:property value="@com.bjsxt.struts2.ognl.S@STR"/> "@类名@属性(方法)" "@@方法(属性)"--只能访问Math的方法和属性 ----ognl.jsp---- <?xml version="1.0" encoding="GB18030" ?> <%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=GB18030" /> <title>OGNL表达式语言学习</title> </head> <body> <ol> <li>访问值栈中的action的普通属性: username = <s:property value="username"/> </li> <li>访问值栈中对象的普通属性(get set方法):<s:property value="user.age"/> | <s:property value="user['age']"/> | <s:property value="user[\"age\"]"/> | wrong: <%--<s:property value="user[age]"/>--%></li> <li>访问值栈中对象的普通属性(get set方法): <s:property value="cat.friend.name"/></li> <li>访问值栈中对象的普通方法:<s:property value="password.length()"/></li> <li>访问值栈中对象的普通方法:<s:property value="cat.miaomiao()" /></li> <li>访问值栈中action的普通方法:<s:property value="m()" /></li> <hr /> <li>访问静态方法:<s:property value="@com.bjsxt.struts2.ognl.S@s()"/></li> <li>访问静态属性:<s:property value="@com.bjsxt.struts2.ognl.S@STR"/></li> <li>访问Math类的静态方法:<s:property value="@@max(2,3)" /></li> <hr /> <li>访问普通类的构造方法:<s:property value="new com.bjsxt.struts2.ognl.User(8)"/></li> <hr /> <li>访问List:<s:property value="users"/></li> <li>访问List中某个元素:<s:property value="users[1]"/></li> <li>访问List中元素某个属性的集合:<s:property value="users.{age}"/></li> <li>访问List中元素某个属性的集合中的特定值:<s:property value="users.{age}[0]"/> | <s:property value="users[0].age"/></li> <li>访问Set:<s:property value="dogs"/></li> <li>访问Set中某个元素:<s:property value="dogs[1]"/></li> <li>访问Map:<s:property value="dogMap"/></li> <li>访问Map中某个元素:<s:property value="dogMap.dog101"/> | <s:property value="dogMap['dog101']"/> | <s:property value="dogMap[\"dog101\"]"/></li> <li>访问Map中所有的key:<s:property value="dogMap.keys"/></li> <li>访问Map中所有的value:<s:property value="dogMap.values"/></li> <li>访问容器的大小:<s:property value="dogMap.size()"/> | <s:property value="users.size"/> </li> <hr /> <li>投影(过滤):<s:property value="users.{?#this.age==1}[0]"/></li> <li>投影:<s:property value="users.{^#this.age>1}.{age}"/></li> <li>投影:<s:property value="users.{$#this.age>1}.{age}"/></li> <li>投影:<s:property value="users.{$#this.age>1}.{age} == null"/></li> <hr /> <li>[]:<s:property value="[0].username"/></li> </ol> <s:debug></s:debug> </body> </html> 投影(过滤):{?#this.age==1}、{^#this.age>1}、{$#this.age>1}为过滤条件。 ^代表第一个,$代表末尾那个。 <s:property value="[0].username"/>代表从第0个一直到末尾的集合。在服务器端跳转时就会有多个Action。 Struts2的标签可以分为通用标签,控制标签,UI标签,AJAX标签,$#%的区别。 1.通用标签: a)property b)set i.默认为action scope,会将值放入request和ActionContext中 ii.page、request、session、application c)bean d)include(对中文文件支持有问题,不建议使用,如需包含,改用jsp包含) e)param f)debug 2.控制标签 a)if elseif else b)iterator i.collections map enumeration iterator array c)subset 3.UI标签 a)theme i.simple xhtml(默认) css_xhtml ajax 4.AJAX标签 a)补充 5.$ # %的区别 a)$用于i18n和struts配置文件 b)#取得ActionContext的值 c)%将原本的文本属性解析为ognl,对于本来就是ognl的属性不起作用 i. 参考<s:property 和 <s:include
命名规范: 1.数据库命名: 表命名(简单就是美): _表名,如: _Topic (使用驼峰标示,尽管数据库不区分大小写) 字段命名: 保持与属性名一致(尽量不要和数据库的命名冲突) 如:ID属性 id 外键 topicId 库名: 使用项目的名称 2.类和文件的命名: 有两种方式:1.先分模块再分层次2.先分层次再分模块 采用层次划分(包的命名都用小写): com.bjsxt.bbs.action.model.service Action类采用Action结尾,如:xxxAction.java JSP命名:*_* namespace:前台直接使用"/" 后台使用"/admin" package: 前台:aciton 后台:adminAction 项目开发顺序: 1.建立界面原型 2.建立Struts.xml a.确定namespace b.确定package c.确定Action的名称,空的方法 d.确定Rssult e.将界面原形页面进行修改,匹配现有设置 f.测试 g.做好规划 3.建立数据库(或者实体类) 4.建立Model层 5.建立Service层(hibernate) 使用Junit进行单元测试 6.着手开发 请求到来后被Filter过滤,调用特定的Action的特定方法,拦截器就是在调用的时候加东西。 (其实先调用拦截器,拦截器再调用Action,再返回给拦截器,又返回给Filter) I18N 在普通Editer直接写中文会乱码。 国际化文件可以用propertiesEditer插件,在这里面写的中文会转为UTF-8保存。 Struts2的国际化有三种方式: Action-Package-App级 主要用App,对整个项目有效。 struts2的实际执行过程: 1.Struts2Flter接受页面请求 2.调用Dispatcher的serviceAction()方法 3.Dispatcher创建一个ActionProxy对象,并调用它的execute()方法 4.execute()方法又会调用ActionInvocation的invoke()方法 5.invoke()方法又会调用第一个inerceptor对象的interceptor()方法 6.interceptor()方法又会调用ActionInvocation对象的invoke()方法 7.nvoke()方法又会调用下一个inerceptor对象的interceptor()方法 8.反复直到所有inerceptor调用完毕,ActionInvocation对象调用最中的Action类的execute()方法 传递多个相同参数的时候,可以使用集合容器,但是要使用泛型。 比如在action类中有属性: ...... List<interst>; //不用new ...... 使用token拦截器控制重复提交(很少用) 写自己的转换器: public class MyPointConverter extends DefaultTypeConverter{ @Override public Object convertValue(Object value, Class toType) { if(toType == Point.class) { Point p = new Point(); String[] strs = (String[])value; String[] xy = strs[0].split(","); p.x = Integer.parseInt(xy[0]); p.y = Integer.parseInt(xy[1]); return p; } if(toType == String.class) { return value.toString(); } return super.convertValue(value, toType); } } public class MyPointConverter extends StrutsTypeConverter{ @Override public Object convertFromString(Map context, String[] values, Class toClass) { Point p = new Point(); String[] strs = (String[])values; String[] xy = strs[0].split(","); p.x = Integer.parseInt(xy[0]); p.y = Integer.parseInt(xy[1]); return p; } @Override public String convertToString(Map context, Object o) { // TODO Auto-generated method stub return o.toString(); } } 三种注册方式: i.局部:XXXAction-conversion.properties 1.p(属性名称)= converter ii.全局:xwork-conversion.properties 1.com.xxx.XXX(类名)= converter iii.Annotation 如果遇到非常麻烦的映射转换 i.request.setAttribute(); ii.session