日期: 2012-11-7
Struts2与struts1 对比: Struts2是WebWork2基础发展而来的。和strut1一样,Struts2也属于MVC框架. 不过有一点大家需要注意的是:尽管Struts2和struts1在名字上的差别不是很大,但 Struts2和struts1在代码编写风格上几乎不一样的。那么既然有了struts1,为何还要 推出strut2。主要是因为struts2有以下优点: 1> 在软件设计上Struts2没有想struts1那样跟ServletAPI(例如:Action中方法参数 HttpServletRequest和HttpServletResponse)和 strutsAPI(例如 继承Action 类和方法参数ActionMapping,ActionForm)有着紧密的耦合,Strut2的应用可以不依赖 于ServletAPI和strutsAPI。Struts2的这种设计属于无侵入式设计。而struts1却属于 侵入式设计。
2> Struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截等功能。
3> Struts2提供了类型转换器,我们可以把特殊的请求参数转换需要的类型。在struts1中, 如果我们要实现同样的功能,就必须向Struts1的底层实现BeanUtil注册类型转换器才行.
4> Struts2提供支持多种表现层技术。如:jsp,freeMarket,Velocity等
5> Struts2输入校验可以对指定方法进行校验,解决了Struts1长久之痛
6> 提供了全局范围,包范围和Action范围的国际化资源文件管理实现
开发第一个Hello word 1. 导包 1) struts2-core-2.x.x.jar :Struts 2框架的核心类库 2) xwork-2.x.x.jar :XWork类库,Struts 2在其上构建 3) ognl-2.6.x.jar :对象图导航语言(Object Graph Navigation Language),Struts 2框架使用的一种表达式语言 4) freemarker-2.3.x.jar :Struts 2的UI标签的模板使用FreeMarker编写 5) commons-logging-1.1.x.jar :ASF出品的日志包,Struts 2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。 6) Commons-fileupload 拷进去
2. 配置struts 的核心过滤器 打开web.xml文件 <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter>
<filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 注意 : 所有的请求 都会进入这个 核心过滤器
3. StrutsPrepareAndExecuteFilter的init()方法中将会读取src下默认的配置文件struts.xml完成初始化操作 注意:struts2读取到struts.xml的内容后,会将内容封装进javabean对象并存放在内存中,对于用户每次请求的处理将使用内存中的数据,而不是每次请求都读取struts.xml文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts>
<package name="helloword" extends="struts-default">
<action name="helloWordAction" class="com.struts.action.HelloWordAction">
<result name="success">/WEB-INF/jsp/message.jsp</result>
</action>
</package> </struts>
Package: 包 跟java类似 来管理一组相同功能的类 Name属性名称 一定要唯一 (因为包可以继承)
Nasepace : 表示命名空间
Extends 继承 struts2核心 包 (核心包 struts-default.xml中)
Action 表示 action名称 注意: 这个是请求Url一部分
Class 表示 当前的action类 例如: com.struts.action.HelloWordAction
Result 表示: 要跳转的结果页面 Result中 name属性 与 HelloWordAction 中 execute() 返回的值相同
如何访问 web-inf/jsp/login.jsp 页面 Struts.xml 文件中配置: <action name="login" class="com.opensymphony.xwork2.ActionSupport"> <result name="success">/WEB-INF/jsp/login.jsp</result>
</action> 如果没有为action指定class,默认是ActionSupport。而ActionSupport的execute() 方法默认处理就是返回一个success字符串。method属性用于指定action中的那个方法,如果没有指定默认执行action中的execute() 方法
1.获得请求路径的URI,例如url是:
2.首先寻找namespace为/path1/path2/path3的package,如果存在这个package,则在这个package中寻找名字为test的action,如果不存在这个package则转步骤3; 3.寻找namespace为/path1/path2的package,如果存在这个package,则在这个package中寻找名字为test的action,如果不存在这个package,则转步骤4; 4.寻找namespace为/path1的package,如果存在这个package,则在这个package中寻找名字为test的action,如果仍然不存在这个package,就去默认的namaspace的package下面去找名字为test的action,如果还是找不到,页面提示找不到action。
<Result/>标签
Name=”success” 默认
type="dispatcher" 等价 request.getRequestDispatcher() <result type="dispatcher">/WEB-INF/jsp/message.jsp</result>
type="redirect" 等价 response.sendRedirectr() <result type="redirect">/WEB-INF/jsp/message.jsp</result>
<!-- 重新执行那个 命名空间 下 那个 action --> <result type="redirectAction"> <param name="namespace">/reg</param> <param name="actionName">regAction</param> </result
<package name="reg" namespace="/reg" extends="struts-default">
<action name="regAction" class="com.struts.hello.action.RegAction"> <result>/WEB-INF/jsp/message.jsp</result> </action> </package>
属性注入 <param name="message">message属性注入</param>
类似el表达式 ${message}(获取action中 message属性值) <result>/WEB-INF/jsp/message.jsp?abc=${message}</result>
Bbs论坛:
日期: 2012-11-10
Struts2的处理流程:
指定多个配置文件:
在大部分应用里,随着应用规模的增加,系统中Action数量也大量增加,导致struts.xml配置文件变得非常臃肿。为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。下面的struts.xml通过<include>元素指定多个配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <include file="struts-user.xml"/> <include file="struts-order.xml"/> </struts>
通过这种方式,我们就可以将Struts 2的Action按模块配置在多个配置文件中。
为Action的属性注入值: public class HelloWorldAction{ private String savePath;
public String getSavePath() { return savePath; } public void setSavePath(String savePath) { this.savePath = savePath; } ...... }
<package name="test" namespace="/test" extends="struts-default"> <action name="helloworld" class="cn.action.HelloWorldAction" > <param name="savePath">/images</param> <result name="success">/WEB-INF/page/hello.jsp</result> </action> </package> 上面通过<param>节点为action的savePath属性注入“/images”
动态方法调用: 如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法 通常不建议大家使用动态方法调用,我们可以通过常量struts.enable.DynamicMethodInvocation关闭动态方法调用。 <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
使用通配符定义action: <package name="test" namespace="/test" extends="struts-default"> <action name="helloworld_*" class="cn.action.HelloWorldAction" method="{1}"> <result name="success">/WEB-INF/page/hello.jsp</result> </action> </package> public class HelloWorldAction{ private String message; .... public String execute() throws Exception{ this.message = "我的第一个struts2应用"; return "success"; }
public String other() throws Exception{ this.message = "第二个方法"; return "success"; } }
要访问other()方法,可以通过这样的URL访问:/test/helloworld_other.action 全局结果: 当多个action中都使用到了相同result,这时我们应该把result定义为全局结果。 <package ....> <global-results> <result name="message">/message.jsp</result> </global-results> </package> 定义常量: struts2按如下搜索顺序: struts-default.xml struts-plugin.xml struts.xml struts.properties web.xml
#指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出 struts.i18n.encoding=utf-8 #设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 struts.serve.static.browserCache=false #当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 struts.configuration.xml.reload=true
#开发模式下使用,这样可以打印出更详细的错误信息 struts.devMode=true
#该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false #struts.enable.DynamicMethodInvocation=false
#文件上传 设置文件最大为多少 默认2M struts.multipart.maxSize=10701096 #该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。 struts.action.extension=do,action,go #与spring集成时,指定由spring负责action对象的创建 #struts.objectFactory=spring #默认的视图主题 struts.ui.theme=simple
日期: 2012-11-12
自定义类型转换器: action中使用到了时间类型Date,当需要将请求参数注入到date属性时,我们必须定义转换器,否则struts2无法自动完成类型转换。(为了测试 类型转换器 默认情况下 date=2010-01-29 是可以转换的 但是 date=20100129 这就不行 会到页面输出原样值(action从缓存中得到的) 控制台出错)
下面定义了一个针对Date类型的类型转换器: 1. public class DateTypeConverter extends DefaultTypeConverter {
/** * value 表示 前台提交 表单 name对应的 value值 * 例如 : <input name="date" value="2012-12-12"/> * 注意 :value是数组 * toType : 表示 action 中 属性的类型 * private Date date; java.util.Date */ public Object convertValue(Map<String, Object> context, Object value, Class toType) {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); if(toType==Date.class)// 判断action 中的date属性 是否是 Date类型 { // request.getParameter("date") // request.getParameterValue("date") 要兼容 <input type="checkbox"/> Object[] params= (Object[])value; try { return sdf.parse(params[0].toString()); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; }
}
2.将上面的类型转换器注册为局部类型转换器: 在Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应为HelloWorldAction-conversion.properties 。在properties文件中的内容为: 属性名称=类型转换器的全类名 对于本例而言, HelloWorldAction-conversion.properties文件中的内容为: date= com.struts2.web.util.DateTypeConvert
3 全局类型转换器 将上面的类型转换器注册为全局类型转换器: 在src下放置xwork-conversion.properties文件 。在properties文件中的内容为: 待转换的类型=类型转换器的全类名 对于本例而言, xwork-conversion.properties文件中的内容为: java.util.Date=com.struts2.web.util.DateTypeConvert (全局 类型转换器 针对 全部的Action中的时间类型 所以不能 把Action中字段名做为key值 要以时间类型 做为key)
自定义拦截器: 例子: 用户没有登录 不允许 回复帖子
1. 自定义拦截器: public class PermissionInterceptor implements Interceptor {
public void destroy() { // TODO Auto-generated method stub
}
public void init() { // TODO Auto-generated method stub
}
public String intercept(ActionInvocation invocation) throws Exception {
User user= (User) ActionContext.getContext().getSession().get("user"); if(user==null) { return "login"; }
return invocation.invoke();// 执行目标方法 }
}
2. 为Action 注册拦截器
<interceptors> <!-- 注册拦截器 --> <interceptor name="permissionInterceptor" class="com.struts.bbs.util.PermissionInterceptor"></interceptor>
<!-- 配置拦截器栈 (注意:系统+自定义) --> <interceptor-stack name="permissionStack"> <interceptor-ref name="permissionInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors>
<action name="toAddReplyAction" class="com.struts.bbs.reply.action.ReplyAction" method="toAddReply">
<result name="login">login.jsp</result> <result>/WEB-INF/jsp/reply_post.jsp</result> <interceptor-ref name="permissionStack"></interceptor-ref> </action>
输入校验: 1. action 继承 ActionSupport
2. 重写 ActionSupport的 Validate()
/** * 这个方法 是针对 action中 所有方法进行校验 */ public void validate() {
if(user!=null) { if(user.getUname()==null||user.getUname().trim().equals("")) { this.addFieldError("userName", "用户名不能为空");
} if(user.getUpass()==null|| user.getUpass().trim().equals("")) { this.addFieldError("password", "密码不能为空");
}
} } 校验失败 返回 默认 为 input试图
如果针对单个 方法进行校验 方法名称 : validate+方法名称(注意方法名称首字母大写) 例如: public void validateLogin()// 针对login方法进行校验 { if(user!=null) { if(user.getUname()==null||user.getUname().trim().equals("")) { this.addFieldError("userName", "用户名不能为空");
} if(user.getUpass()==null|| user.getUpass().trim().equals("")) { this.addFieldError("password", "密码不能为空");
}
} }
配置全局资源与输出国际化信息: Src 下创建: login_zh_CN.properties login_en_US.properties 其中baseName是资源文件的基本名,我们可以自定义,但language和country必须是java支持的语言和国家。如: 中国大陆: baseName_zh_CN.properties 美国: baseName_en_US.properties 对于中文的属性文件,我们编写好后,应该使用jdk提供的native2ascii命令把文件转换为unicode编码的文件。命令的使用方式如下: native2ascii 源文件.properties 目标文件.properties 使用上面的资源文件,在struts.xml中使用常量加载全局资源文件如下: <constant name="struts.custom.i18n.resources" value="login" /> login为资源文件的基本名。 l 在JSP页面中使用<s:text name=“”/>标签输出国际化信息: <s:text name=“user”/>,name为属性文件中的key l 在Action类中,可以继承ActionSupport,使用getText()方法,该方法的第一个参数用于指定属性文件中的key。 l 在表单标签中,通过key属性指定属性文件中的key,如: <s:textfield name="realname" key="user"/> 国际化—包范围资源文件: 在一个大型应用中,整个应用有大量的内容需要实现国际化,我们可以针对不同模块、不同的action来组织国际化文件。 在java的包下放置package_language_country.properties资源文件,package为固定写法,处于该包及子包下的action都可以访问该资源。当查找指定key的消息时,系统会先从package资源文件查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找。 <s:i18n name="cn/struts2/natives/action/package"> <s:text name="helloWorld"></s:text> </s:i18n> 国际化—Action范围资源文件: 我们也可以为某个action单独指定资源文件,方法如下: 在Action类所在的路径,放置ActionClassName_language_country.properties资源文件,ActionClassName为action类的简单名称。 当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找,如果没有找到对应的key,然后沿着当前包往上查基本名为package 的资源文件,一直找到最顶层包。如果还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中寻找。 例如: <s:i18n name=”cn/struts2/acion/LoginAction”> </s:i18n> OGNL表达式语言: file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wpsE3AD.tmp.png 注意: 1. 值栈: Action中 属性的值 是放在值栈中 使用 struts标签 不需要加上 # 2 放入 request ,session application 使用 struts标签 需要加上 #
Struts2 标签 Iterarot迭代标签: <%
ArrayList<String> nameList=new ArrayList<String>(); nameList.add("张三"); nameList.add("李四"); nameList.add("王五"); nameList.add("赵六"); request.setAttribute("nameList",nameList);
HashMap<String,String> map=new HashMap<String,String>(); map.put("userName","张三"); map.put("age","22"); map.put("address","武汉市新路村特1号");
request.setAttribute("map",map); %>
迭代集合:<br/> <s:iterator var="name" value="#request.nameList" >
${name }
</s:iterator>
迭代Map集合: <s:iterator var="entry" value="#request.map"> ${entry.key }=${entry.value }<br/> </s:iterator>
If 标签: <% request.setAttribute("time",22); %>
<s:property value="#request.time"/> <s:if test="#request.time gt 6 and #request.time lt 12"> 上午好 </s:if> <s:elseif test="#request.time gt 12 and #request.time lt 18"> 下午好 </s:elseif> <s:else> 晚上好 </s:else>
<s:token />标签: <s:token />标签防止重复提交,用法如下: 第一步:在表单中加入<s:token /> <s:form action="helloworld_other" method="post" namespace="/test"> <s:textfield name="person.name"/><s:token/><s:submit/> </s:form> 第二步: <action name="helloworld_*" class="cn.action.HelloWorldAction" method="{1}"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="token" /> <result name="invalid.token">/WEB-INF/page/message.jsp</result> <result>/WEB-INF/page/result.jsp</result> </action> 以上配置加入了“token”拦截器和“invalid.token”结果,因为“token”拦截器在会话的token与请求的token不一致时,将会直接返回“invalid.token”结果。
在debug状态,控制台出现下面信息,是因为Action中并没有struts.token和struts.token.name属性,我们不用关心这个错误: 严重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting 'struts.token' on 'class xxx: Error setting expression 'struts.token' with value '[Ljava.lang.String;@39f16f' 严重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting 'struts.token.name'
|