java无忧网
标题: struts2学习笔记及使用 [打印本页]
作者: java无忧网 时间: 2016-10-15 14:28
标题: struts2学习笔记及使用
日期: 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'
欢迎光临 java无忧网 (http://www.javawyw.com/) |
Powered by Discuz! X3.2 |