我是靠谱客的博主 传统麦片,最近开发中收集的这篇文章主要介绍Struts2基础--Struts2(你必须要懂得基础)01_Struts2概述02_搭建Struts2运行环境03_Eclipse界面设置04_HelloWorld详解05_请求和响应06_2015年12月5日小结07_值栈09_Struts2通用标签08_2015年12月7日小结10_Struts2表单标签11_2015年12月8日小结12_404错误产生的原因13_Struts2运行流程14_模型驱动15_2015年12月11日复习16_声明式异常管理17_主题18_类型转换19_2015,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
01_Struts2概述作者: 风离紫竹--tryzq521@126.com |
1.Struts2是一款工作在表述层的大型、开源、免费框架。
2.工作在表述/表示层的框架需要具备的功能
①获取请求参数:在Controller中提供setXxx()方法就可以让框架自动将请求参数注入进来。
②文件上传、下载
③防止表单重复提交
④国际化
⑤在JSP页面上显示数据:提供getXxx()方法
⑥表单验证
……
3.框架
①使命:Write less,do more!写得少,做得多!每当我们多学一点知识,就有可能少写一行代码!
②框架:将某个特定领域中的常用操作封装到一起,简化开发的产品。
[1]junit:单元测试框架
[2]jQuery:JavaScript框架
③第三方JAR包
c3p0、DBUtils、BeanUtils、logging、GSON、FileUpload、IO、dom4j、……
4.Struts2特点
①自动收集请求参数:提供setXxx()方法
②值栈:Struts2为每一个请求提供的一个临时数据存储空间
③非侵入式:实体类支持POJO开发。Plain Old Java Object——普通的、传统的Java对象。
实体类:描述现实世界具体概念的类,例如:Book、User、Person、Cat、Dog等等。
JavaBean==Entity==Domain(领域模型)
④支持可插拔的拦截器和拦截器栈:拦截器和拦截器栈可以根据需要加入或移除。
⑤声明式操作:与编程式操作相对应,将以往需要通过编写代码实现的功能,通过XML文件配置的方式实现。
[1]声明式异常
[2]声明式输入验证
⑥使用Filter作为内部核心控制器,而Struts1和SpringMVC是使用Servlet作为内部核心控制器。
5.Struts2历史
Struts2并不是由Struts1升级得到的。其实是一个换了品牌标签的WebWork。
综合能力=技术能力×其他能力
6.Struts2学习建议
①目的:用
②为了更好的使用Struts2,需要适当的了解Struts2内部原理,工作流程。看源码不能从开头到结尾一行一行看,根据源码的功能,有针对性的看。
③重点内容
[1]基本请求响应
[2]值栈:会用
[3]模型驱动
modelDriven拦截器
preparable拦截器
[4]拦截器和拦截器栈使用
02_搭建Struts2运行环境作者: 风离紫竹--tryzq521@126.com |
1.步骤
①导入Struts2需要使用的JAR包
②在web.xml中配置Struts2的核心Filter
③引入Struts2自身的配置文件
2.war文件
①是动态Web工程导出得到的
②将war文件复制到Tomcat的webapps目录下,启动Tomcat,Tomcat就会自动解压war文件,然后这个工程就可以通过浏览器访问了
③比较便捷的方式是:直接使用压缩软件解压即可
3.导入JAR包
①参考Struts2内置的例子工程中的空工程:struts2-blank
②解压struts2-blank.war文件,找到LibrarySupportstruts-2.3.15.3appsstruts2-blankWEB-INFlib目录,复制其中所有JAR包到我们的动态Web工程
*空的例子工程中的jar包组合并不是一个最小组合,但是一个最经典的组合
4.在web.xml中配置Struts2的核心Filter
①参照空的例子工程:LibrarySupportstruts-2.3.15.3appsstruts2-blankWEB-INFweb.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
>
|
5.引入Struts2自身配置文件
①参照空的例子工程:LibrarySupportstruts-2.3.15.3appsstruts2-blankWEB-INFclasses目录,将struts.xml文件复制到我们工程的src目录下
②关联DTD文档约束
[1]如果当前系统能够接入Internet,则不必做额外操作,Eclipse会根据DTD文档的描述自动下载需要的DTD文件
[2]如果不能接入Internet,则需要告诉Eclipse,关联的dtd文件的位置
[3]步骤:复制dtd文档的URI标识→window→preferences→搜索XML→找到XML Catalog→选中User Specified Entries→点击Add→将前面复制的dtd文档的URI标识粘贴到key文本框中→在key type中选择URI→点击File system查找dtd文件的位置(LibrarySupportstruts-2.3.15.3srccoresrcmainresources)→一路确认→将struts.xml文件关闭重新打开生效
03_Eclipse界面设置作者: 风离紫竹--tryzq521@126.com |
建议使用的透视图——视窗组合
Window→Save perspective as 保存当前透视图设置,便于将来不小心打乱后恢复
恢复的方法:Window→reset perspective
定制右键new菜单
window→Customer perspective
设置字体大小
★Debug界面
切换大小写快捷键:Ctrl+Shift+X/Y
04_HelloWorld详解作者: 风离紫竹--tryzq521@126.com |
1.第一个请求:直接通过Struts2转发
简化配置方式 |
<!-- 配置第一个请求:/saveUI.action -->
<
action
name
=
"saveUI"
>
<!-- 通过URL地址的方式声明当前请求处理完成后,要前往的目标页面,这个地址也是服务器解析的 -->
<
result
>
/saveUI.jsp
</
result
>
</
action
>
|
完整配置方式 |
<!-- 配置第一个请求:/saveUI.action -->
<
action
name
=
"saveUI"
class
=
"com.opensymphony.xwork2.ActionSupport"
method
=
"execute"
>
<!-- 通过URL地址的方式声明当前请求处理完成后,要前往的目标页面,这个地址也是服务器解析的 -->
<
result
name
=
"success"
>
/saveUI.jsp
</
result
>
</
action
>
|
图解:
2.第二个请求图解
3.请求参数注入和数据显示
通过setXxx()方法注入请求参数
通过getXxx()方法显示BookAction类中的属性值
4.Struts2支持String和基本数据类型之间的自动的类型转换
05_请求和响应来源网址: file:///D:/金山快盘/Struts2/LibrarySupport/struts-2.3.15.3/docs/WW/docs/redirect-action-result.html作者: 风离紫竹--tryzq521@126.com |
1.请求
①Action类
[1]Action类的要求
-
-
- 无参的构造器:便于通过反射创建其对象
- 有通过getXxx()和setXxx()方法定义的属性
- 至少有一个处理请求的Action方法
-
- 权限:public
- 返回值:String类型——对应配置文件中的某一个result
- 无参
-
public
String saveBook() {
System.
out
.println(
"saveBook..."
);
return
"saveSuccess"
;
}
|
-
-
- Struts2不强制要求Action实现任何接口或继承任何类
-
[2]Action类在运行过程中是
多实例的。每一个请求创建一个Action类的对象。而Servlet、Filter都是单实例、多线程方式运行的,使用同一个对象处理不同的请求。
②action配置:struts.xml文件中的配置信息
③ActionSupport类[了解]
是普通的Action类的支持类。给我们带来的便利:
[1]能够比较快捷的生成execute()方法、以及使用SUCCESS等常量。
[2]替我们实现了很多有用的接口
④名称空间[了解]
[1]Struts2配置文件中package标签的namespace属性
[2]定义当前package中的所有action的共同访问路径
<
package
name
=
"default"
namespace
=
"/atguigu"
extends
=
"struts-default"
>
|
http://localhost:8989/Struts2_02_HelloWorld/atguigu/saveBook.action |
[3]作用:让不同package在不同的namespace下使用相同的action name
⑤请求扩展名
[1]概念:请求目标资源时,URL地址以“.”作为分隔符的后缀。
http://localhost:8989/Struts2_02_HelloWorld/index.jsp | jsp |
http://localhost:8989/Struts2_02_HelloWorld/saveUI.action | action |
http://localhost:8989/Struts2_02_HelloWorld/index.html | html |
[2]作用:将
普通的请求和专门由
Struts2处理的请求区分开
[3]Struts2默认的扩展名:
action和没有扩展名
struts.action.extension=
action,,
|
打开default.properties文件,查看Struts2内置的各个常量值
[4]
Struts2请求和普通请求的区别
(1)Struts2请求必须由Action类处理,需要在配置文件中进行配置
(2)普通的请求是交给Servlet容器——Tomcat来处理
[5]Struts2默认扩展名的修改
<!-- 修改Struts2的常量值 -->
<
constant
name
=
"struts.action.extension"
value
=
"action,,atguigu"
/>
|
⑥动态方法调用[了解]
[1]开启功能
<
constant
name
=
"struts.enable.DynamicMethodInvocation"
value
=
"true"
/>
|
[2]在URL地址中的action的name值后面使用如下格式:actionName!methodName.action
http://localhost:8989/DynamicMethodInv/saveBook!query.action |
[3]作用:临时调用同一个Action类中的另一个Action方法
⑦通配符映射
[1]简化配置文件:将不同的部分抽取出来,使用*代替,通过{index}引用即可
[2]基本配置方式
<!-- /bookAction_saveBook.action -->
<!-- bookAction_saveBook和bookAction_*Book比较,得到*匹配到的内容是:save,使用save代替{1} -->
<
action
name
=
"bookAction_*Book"
class
=
"com.atguigu.wildcard.action.BookAction"
method
=
"{1}Book"
>
<
result
name
=
"{1}-success"
>
/result/{1}Success.jsp
</
result
>
</
action
>
|
[3]模糊匹配的原则
-
-
- 精确匹配优先于模糊匹配采纳
- 多个模糊匹配中先声明的优先
-
2.响应:Result
①结果:Struts2在处理完Action请求之后为用户提供的响应的
结果。这个结果可以是一个HTML或JSP文件,也可以是一个下载的文件。
②Struts2中返回结果的方式:结果类型。
<
result-types
>
<
result-type
name
=
"chain"
class
=
"com.opensymphony.xwork2.ActionChainResult"
/>
<
result-type
name
=
"dispatcher"
class
=
"org.apache.struts2.dispatcher.ServletDispatcherResult"
default
=
"true"
/>
<
result-type
name
=
"freemarker"
class
=
"org.apache.struts2.views.freemarker.FreemarkerResult"
/>
<
result-type
name
=
"httpheader"
class
=
"org.apache.struts2.dispatcher.HttpHeaderResult"
/>
<
result-type
name
=
"redirect"
class
=
"org.apache.struts2.dispatcher.ServletRedirectResult"
/>
<
result-type
name
=
"redirectAction"
class
=
"org.apache.struts2.dispatcher.ServletActionRedirectResult"
/>
<
result-type
name
=
"stream"
class
=
"org.apache.struts2.dispatcher.StreamResult"
/>
<
result-type
name
=
"velocity"
class
=
"org.apache.struts2.dispatcher.VelocityResult"
/>
<
result-type
name
=
"xslt"
class
=
"org.apache.struts2.views.xslt.XSLTResult"
/>
<
result-type
name
=
"plainText"
class
=
"org.apache.struts2.dispatcher.PlainTextResult"
/>
</
result-types
>
|
[1]chain:转发到一个Action
[2]dispatcher:转发,默认
[3]redirect:重定向到目标资源
[4]redirectAction:重定向到另一个Action
文档中完整的配置方式 |
<result type="redirectAction">
<param name="actionName">dashboard</param>
<param name="namespace">/secure</param>
</result>
|
如果namespace保持默认值/,则可以使用下面的简略方式,直接在result的开始和结束标签之间写目标Action的名字 |
<result type="redirectAction">dashboard</result> |
注意:由于这里是通过另外一个Action的名字直接找到目标Action,并不是通过URL地址来查找,所以不加/
[5]stream:以流的形式返回结果
3.获取Web资源
①概念:在Web应用中request、session、application等等对象。
②在Struts2环境下的Web资源
[1]原生的Web资源:原始的request、session、application对象本身。
[2]Struts2根据原生的Web资源封装得到的Map对象
说明 | 类型 |
封装请求域数据的Map | Map<String,Object> |
封装Session域数据的Map | Map<String,Object> |
封装Application域数据的Map | Map<String,Object> |
封装请求参数数据的Map | Map<String,String[]> |
<input type="checkbox" name="insterest" value="in01"/>
<input type="checkbox" name="insterest" value="in02"/>
<input type="checkbox" name="insterest" value="in03"/>
${param}
${paramValues}
String userName = request.getParameter("userName");
String [] insterest = request.getParameterValues("insterest");
request.getParameter("xxx");//从请求参数中获取数据
request.getAttribute("vv");//从请求域中获取数据,之前必须有setAttribute("vv","tt");操作
③获取的方式
[1]原生的Web资源
(1)主动获取:
ServletActionContext.
getXxx("xx");
(2)被动注入:让Action类实现
ServletXxxAware接口,通过setXxx()方法注入
[2]Map类型的Web资源
(1)主动获取
第一步 | ActionContext.getContext()返回ActionContext对象 |
第二步 | actionContext.getXxx();方法 |
(2)被动注入:让Action类实现
XxxAware接口,通过setXxx()方法注入
④SessionMap可以将Session对象强制失效
SessionMap
sm
= ( SessionMap)
session
;
sm
.invalidate();
|
⑤数据的来源
实现XxxAware接口或ServletXxxAware接口后是由Struts2的ServletConfigInterceptor拦截器将对应的Web资源注入进来的。
拦截器是先于目标Action的目标方法执行的组件。
⑥主动获取和被动注入方式对比
主动获取
|
被动注入√
|
代码不够优雅 | 代码很优雅 |
Web资源对象的类型不是很贴切 | Web资源对象类型很贴切 |
学习成本较高 | 使用便捷 |
06_2015年12月5日小结作者: 风离紫竹--tryzq521@126.com |
1.搭建Struts2的运行时环境
①导入Struts2jar包
②在web.xml中配置核心Filter
③导入Struts2自身的配置文件
2.HelloWorld
3.知道Struts2配置文件中下列配置项的基本含义
①package:包含action配置
②action:配置Struts2请求
name属性
class属性
method属性
③result:Struts2请求执行完成后要执行的结果,通常是要前往的页面
name属性
4.会创建能够处理Struts2请求的Action类,并会编写Action方法
5.理解Struts2扩展名作用:将普通请求和Struts2请求区分开
6.能够找到Struts2中定义常量的属性文件
7.会使用通配符映射简化配置
07_值栈作者: 风离紫竹--tryzq521@126.com |
1.提出问题
①Action类中提供一个getXxx()方法
public
String getMessage() {
return
"The atguig is very good!"
;
}
|
②在结果JSP页面上使用EL表达式读取数据
${requestScope.message }
|
③EL表达式翻译成Java代码
request.getAttribute("message"); |
④问题:我们之前并没有做过setAttribute("xx","xx");操作,为什么能够通过getAttribute("message")获取到这个值?
⑤通过查看StrutsRequestWrapper类的源码,发现:Struts2对原始的request对象进行了包装,修改了getAttribute()方法的行为
[1]先从原始的请求域中读取数据
[2]如果原始的请求域中能够找到所需要的数据,则直接返回
[3]如果原始的请求域中找不到所需要的数据,则再获取ValueStack对象
[4]从ValueStack对象中获取数据
2.值栈本身:ValueStack接口
①作用:每一个值栈对象都是Struts2为每一个请求所分配的临时的数据存储空间。
②ValueStack保存数据依靠内部的两个数据容器
[1]对象栈:CompoundRoot root;
[2]Map栈:transient Map<String, Object> context;
③获取值栈对象
ActionContext
ctx
= ActionContext. getContext();
ValueStack
stack
=
ctx
.getValueStack();
|
ValueStack接口的实现类:com.opensymphony.xwork2.ognl.OgnlValueStack
④对象栈
[1]本质:一个基于ArrayList实现的一个“堆栈”数据结构
Tips:堆栈——一种
先进后出的数据结构
队列——一种先进先出的数据结构
[2]查看值栈中数据的简便方法:使用s:debug标签
⑤Map栈
[1]类型:Map<String,Object>
[2]包含的内容
(1)原生的Web资源对象
(2)Struts2封装的Web资源对象的Map对象
3.使用OGNL表达式读取值栈中的数据
①OGNL:Object Graph Navigation Language 对象图导航语言
读取对象属性的传统方式 | department.getEmpList().get(5).getAddress().getStreet(); |
OGNL方式 | department.empList[5].address.street |
OGNL是Apache软件基金会组织下的一个独立的项目,并不依赖于Struts2。
②在Struts2中如何使用OGNL
[1]调用值栈对象的findValue("OGNL表达式")或findString("OGNL表达式")方法
findValue("OGNL表达式")返回Object类型的数据
findString("OGNL表达式")返回字符串类型的数据
[2]使用OGNL表达式访问值栈中的对象栈
-
-
-
- 格式:[索引].propertyName
- 索引指的是开始查找的位置,如果在当前位置找到了属性值,则直接返回,并停止查找。
- 如果在当前位置没有找到,则继续向下查找,直到找到或遍历了全部的对象栈为止。
- 索引可以省略,如果省略,则表示从栈顶开始找。
-
-
[3]使用OGNL表达式访问值栈中的Map栈
-
-
- 格式:#key.propertyName
- 常用的key
-
- request:对应request的Map
- session:对应session的Map
- application:对应application的Map
- parameters:对应请求参数的Map
- attr:根据域从小到大的范围查找数据
-
- 实现类:org.apache.struts2.util.AttributeMap
- 注意:如果Map的键中包含特殊符号,则需要使用[]传入key
-
valueStack
.findString(
"#session['user-name']"
);
|
在EL表达式和JSON数据中都有同样的问题,且解决方式也是一样的。
09_Struts2通用标签作者: 风离紫竹--tryzq521@126.com |
1.Struts2标签概述
①Struts2标签和值栈结合的非常紧密
②分类
[1]通用标签
[2]表单标签
③导入Struts2标签库
<%@
taglib
uri
=
"/struts-tags"
prefix
=
"s"
%>
|
2.Struts2标签中访问值栈的OGNL表达式语法
①很多标签的value以及其他属性能够进行自动的OGNL解析,将传入的值当做OGNL表达式来执行
<
s:url
action
=
"bookAction"
>
<
s:param
name
=
"paramWords"
value
=
"[0].words"
/>
</
s:url
>
|
②当我们希望给自动OGNL解析的属性传入的值不要当做OGNL表达式来执行,那么给这个值加上引号即可
<
s:url
action
=
"bookAction"
>
<
s:param
name
=
"paramWords"
value
=
"'goodMorning'"
/>
</
s:url
>
|
③使用%{}可以进行强制OGNL解析
<
s:a
value
=
"/targetUrl?paramWords=%{[0].words}"
>
To Some Place
</
s:a
>
|
3.遍历Map集合
①Map集合的结构
②遍历Map集合
<
s:iterator
value
=
"#request.map"
>
<
tr
>
<%-- 当前栈顶对象是Entry类型的 --%>
<
td
><
s:property
/></
td
>
<%-- 调用栈顶对象的getKey()方法 --%>
<
td
><
s:property
value
=
"[0].key"
/></
td
>
<%-- 调用栈顶对象的getValue()方法 --%>
<
td
><
s:property
value
=
"[0].value"
/></
td
>
</
tr
>
</
s:iterator
>
|
4.sort标签排序思路
08_2015年12月7日小结作者: 风离紫竹--tryzq521@126.com |
1.结果类型
①转发到目标资源
②重定向到目标资源
③转发到目标Action:chain
④重定向到目标Action
[1]通过URL地址:redirect
[2]通过Action名字:redirectAction
2.获取Web资源
①通过实现XxxAware接口获取Map类型的Web资源对象
②通过实现ServletXxxAware接口获取原生的Web资源对象
3.值栈
①通过OGNL表达式访问对象栈
②通过OGNL表达式访问Map栈
10_Struts2表单标签来源网址: http://localhost:8989/FormTag/SingleBoolean.jsp作者: 风离紫竹--tryzq521@126.com |
1.福利
①自动排版
②自动表单回显
2.自动回显的依据:使用栈顶对象的getXxx()方法读取数据,进行回显。
3.单选、多选、下拉列表生成方式
4.对于多选框
在Action类中接收多选框提交的数据时,需要使用List类型。使用Struts2标签生成多选框后可以自动回显。
5.提交单个布尔值
①遇到的问题:使用HTML的多选框提交单个布尔值,如果目标字段默认值是true,则即使没有选择多选框目标字段的值也还是true。
②原因:没有选中多选框时,表单就不提及这个多选框了,于是Action类中保持默认值。
③Struts2的s:checkbox标签原理
[1]选中时提交的数据
[2]没选中时提交的数据
[3]目标Action类可以根据接收到是数据的个数决定如何设置
只接收到一个值:设置为false
接收到两个值:设置为true
[4]生成的HTML代码
<input type="checkbox" name="married" value="true" id="SingleBooleanAction_married"/>
<input type="
hidden" name="__checkbox_married" value="true" />
<label for="SingleBooleanAction_married" class="checkboxLabel">已婚/未婚</label>
|
11_2015年12月8日小结作者: 风离紫竹--tryzq521@126.com |
1.CRUD操作
2.课上标签全部敲一遍
12_404错误产生的原因作者: 风离紫竹--tryzq521@126.com |
1.确实是路径不对,并不存在这样的资源。
2.访问了WEB-INF目录下的资源。这个目录不允许浏览器直接访问,但可以通过转发访问。
3.web.xml或strust.xml等配置文件有错误,在Web工程启动的时候已经抛出了异常,此时访问Web工程中的任何资源都是404
4.服务器端缓存造成的,清理服务器缓存即可。
13_Struts2运行流程作者: 风离紫竹--tryzq521@126.com |
1.运行流程关注点
①Struts2核心Filter拦截到浏览器请求,检查是否是Struts2请求。判断的依据是ActionMapping对象是否为空。
②如果获取到ActionMapping对象为空,则说明当前是一个普通请求,chain.doFilter()放行,后续交给所在的Servlet容器处理。
③如果获取到ActionMapping对象不为空,则说明当前是一个Struts2请求。凡是Struts2请求都必然要执行一个目标Action,也必然要执行这个目标Action类中的目标方法。
④在调用目标Action方法之前做相关准备工作,首先是创建ActionProxy对象。
⑤ActionProxy会创建ActionInvocation对象负责总体调度。
⑥在对ActionInvocation对象进行初始化操作时会将目标Action对象压入到栈顶。
⑦按照拦截器栈中定义的顺序逐一调用每一个拦截器。调用是递归的方式,前面的拦截器调用后面的,后面的没有执行完成时,前面的拦截器处于等待状态。
⑧所有拦截器都执行完成后,ActionInvocation对象会负责调用目标Action方法。
⑨目标Action方法执行完成后,根据返回的字符串决定执行什么Result。
⑩一般来说Result是jsp页面,需要执行JSP页面对应的代码,其中往往包含需要执行的标签。
⑪如果JSP页面执行完成,那么返回给浏览器的响应数据就准备好了,拦截器按照与调用时相反的顺序递归返回。
⑫当最后一个拦截器返回之后,整个过程结束。
14_模型驱动作者: 风离紫竹--tryzq521@126.com |
1.问题一:保存图书和更新图书时,请求参数如何注入到目标Action的setXxx()方法中的?
①是params拦截器负责执行的这个操作
②params拦截器是针对对象栈中栈顶对象进行注入,并不是非要注入到目标Action中,而是目标Action恰好在栈顶的位置,所以才被注入了请求参数。
2.问题二:BookAction类中,包含大量与Book实体类中重复的代码,冗余程度很高。
①BookAction类中的冗余代码的作用:让params拦截器将表单提交的请求参数注入进来。
②由于params拦截器是针对栈顶对象进行注入,所以如果能够将Book对象放到栈顶接收请求参数,那么BookAction中就可以省略setXxx()方法了。
③modelDriven拦截器工作机制
[1]检查目标Action类的对象是否实现了ModelDriven接口
[2]如果实现了ModelDriven接口,则调用目标Action对象的getModel()方法
[3]将getModel()方法的返回值压入栈顶
3.问题三:在执行更新操作的表单回显时,对象栈中存在两个Book对象,浪费内存空间。
①产生原因:modelDriven拦截器负责压入了一个空的Book对象,在editBook()方法中又手动压入了一个用于表单回显的Book对象,从而造成了重复。
②解决办法:在手动压入之前,先手动弹出栈顶的空的Book对象。
4.问题四:能否通过不操作值栈的方式解决问题三?
逐一的给栈顶的this.book对象赋值
this
.
book
.setBookId(
book
.getBookId());
this
.
book
.setBookName(
book
.getBookName());
this
.
book
.setAuthor(
book
.getAuthor());
this
.
book
.setPrice(
book
.getPrice());
|
非常的繁琐
5.问题五:不同的Action方法对栈顶的模型对象有不同的需求,能否为getModel()方法有针对性的准备适合的模型对象?
①具体的不同需求
[1]editBook()方法:需要从Dao中取出的Book对象
[2]saveBook()/updateBook()方法:需要一个新创建的Book对象
[3]其他方法:没有特殊要求,不依赖于模型对象
②解决问题思路:为有需要的Action方法
量身定做模型对象。
③解决方案:使用prepare拦截器
④prepare拦截器工作方式
[0]关键:在modelDriven拦截器执行之前被调用
[1]检查目标Action类是否实现了Preparable接口
[2]如果实现了这个接口,则调用目标Action类的prepareXxx()前缀方法
[3]我们可以在目标Action专属的前缀方法中,为this.book赋合适的值
目标方法 | saveBook() |
前缀方法 |
prepareSaveBook(){
this.book = new Book();
}
|
[4]在getModel()方法中,就不必再给this.book赋值了。
6.问题六:在执行prepareEditBook()方法时空指针异常。
①产生的原因:通过this.book.getBookId()方式获取bookId时,this.book还没有被赋值是null。
②解决问题方式:采取其他办法在prepareEditBook()方法中获取请求参数传入的bookId的值。
③具体步骤
[1]声明bookId成员变量,并提供setXxx()方法
[2]将默认拦截器栈改为paramsPrepareParamsStack拦截器栈。目的是:让prepare拦截器前面有params拦截器负责注入请求参数
[3]顺便将删除操作中使用的bookId也修改为this.bookId
④修改默认拦截器栈
<
default-interceptor-ref
name
=
"paramsPrepareParamsStack"
/>
|
7.问题七:BookAction中的prepare()方法始终都会被调用执行,但又没有什么用处。
可以通过修改prepare拦截器的参数禁用prepare()方法
<!-- 声明自定义拦截器和拦截器栈 -->
<
interceptors
>
<!-- 创建自定义拦截器栈 -->
<
interceptor-stack
name
=
"atguiguStack"
>
<!-- 引入Struts2提供的某一个拦截器栈 -->
<
interceptor-ref
name
=
"paramsPrepareParamsStack"
>
<!-- 修改某一个拦截器的参数 -->
<
param
name
=
"prepare.alwaysInvokePrepare"
>
false
</
param
>
</
interceptor-ref
>
</
interceptor-stack
>
</
interceptors
>
<!-- 引用自定义拦截器栈 -->
<
default-interceptor-ref
name
=
"atguiguStack"
/>
|
15_2015年12月11日复习作者: 风离紫竹--tryzq521@126.com |
当Struts2应用接收到一个请求:
1.初始化阶段
①请求会被Struts2的核心Filter拦截到
②将Servlet容器(Tomcat)创建的request对象包装为Struts2专用的request对象,类型是:StrutsRequestWrapper,目的是为了修改getAttribute()方法的行为。
③尝试获取ActionMapping对象,并判断ActionMapping对象是否为null。
[1]如果为null:说明当前请求是一个普通请求,交给Servlet容器处理。chain.doFilter()
[2]如果不为null:说明当前请求是一个Struts2请求。凡是Struts2请求都必然会对应一个Action类以及Action方法。所以执行一个Struts2请求的最终目的都是要执行目标Action的目标方法。
[3]但是,在执行目标Action的目标方法之前有很多拦截器需要执行,此时需要ActionProxy对象作为目标Action的代理对象,负责宏观上执行一些准备工作。
[4]ActionProxy对象仅仅是在宏观上进行总体调用,并不负责具体工作,具体工作是由ActionInvocation对象执行的。所以ActionProxy对象会首先将ActionInvocation对象创建出来。
[5]在对ActionInvocation对象初始化操作时,将目标Action的对象压入对象栈的栈顶。这样做的原因是:很多拦截器会针对栈顶对象进行操作,此时将目标Action放到栈顶,这样的拦截器操作的就是目标Action对象。例如:params拦截器将请求参数注入到栈顶对象中,默认栈顶是目标Action,所以默认情况下请求参数会注入到目标Action对象中。
2.执行拦截器阶段
①拦截器执行的顺序:是由当前默认的拦截器栈中的顺序决定的。
②每一个拦截器都负责某一种专门的功能
……
-
- servletConfig拦截器:负责将Web资源对象注入到setXxx()方法中
- i18n拦截器:负责进行国际化操作
- prepare拦截器:负责调用为目标Action方法专门量身定做的prepareXxx()前缀方法。前缀方法最主要的作用就是为modelDriven拦截器准备模型对象
……
-
- modelDriven拦截器:负责将目标Action对象的getModel()方法的返回值压入栈顶。
- fileUpload拦截器:负责文件上传操作
……
-
- params拦截器:负责将请求参数注入到栈顶对象的setXxx()方法中,并进行自动的类型转换
……
3.执行目标Action方法
4.执行结果
①一般来说,执行结果都是JSP页面,此时就需要执行JSP文件翻译、编译后得到的class文件,响应的数据就准备好了。但此时虽然响应的数据已经准备好了,但还没有正式给浏览器返回,因为此时服务器端的程序还没有执行完。
②如果JSP页面上包含标签,那么也需要执行标签对应的Java代码。
5.拦截器按照递归的相反顺序返回阶段
- 拦截器调用下一个拦截器是通过DefaultActionInvocation类的invoke()方法,在246行调用下一个拦截器的intercept()方法。intercept()方法调用下一个拦截器,那么下一个拦截器没有执行完成的时候,当前方法就在246行等待。
- 下一个拦截器执行完成,当前invoke()方法才能够继续执行。
- 如果所有拦截器都执行完成,则在252行执行目标Action的目标方法
- 目标方法执行完成后,在275行执行结果:executeResult()
- 在281行invoke()方法最终结束,返回给上一次调用它的那个invoke()方法
16_声明式异常管理作者: 风离紫竹--tryzq521@126.com |
1.声明式异常
①概念:与编程式异常对应。编程式异常管理是通过自己编写代码的方式实现;声明式异常管理是通过在配置文件中配置的方式实现。
②编程式异常管理
try{
//1.开启事务
conn.setAutoCommit(false);
//2.执行请求——包括后续的全部操作:Servlet、Service、Dao
chain.doFilter(req,resp,chain);
//3.提交事务
conn.commit();
}catch(Exception e){
//4.回滚事务
conn.rollBack();
//5.前往用于显示异常信息的页面
request.setAttribute("message","异常信息....");
//转发...
}finally{
//6.关闭数据库连接
JDBCUtils.close(conn);
}
|
③其他声明式功能
[1]声明式事务
[2]声明式输入验证
2.声明式异常的基本思路
配置一个异常类型,指定捕获到这个类型的异常后执行对应的Result
3.Struts2捕获到指定的异常对象后,会封装一个ExceptionHolder对象压入栈顶,可以通过OGNL表达式访问ExceptionHolder对象的exception属性去获取相关异常信息
4.action级别的配置方式
<
action
name
=
"daoDanAction"
class
=
"com.atguigu.exception.action.ExceptionAction"
method
=
"daoDanAction"
>
<!-- action级别的声明式异常配置,对其他action无效 -->
<!-- 使用exception-mapping将某个异常类型和一个指定的result关联起来 -->
<!-- exception属性:指定要管理的异常类型 -->
<!-- result属性:指定捕获到指定异常后要执行的结果 -->
<
exception-mapping
result
=
"exception"
exception
=
"java.lang.ArithmeticException"
/>
<
result
>
/good.jsp
</
result
>
<
result
name
=
"exception"
>
/makeException.jsp
</
result
>
</
action
>
|
5.全局配置方式
<!-- 配置全局级别的result,当前package中所有action都可以使用 -->
<
global-results
>
<
result
name
=
"global_error"
>
/globalMsg.jsp
</
result
>
</
global-results
>
<!-- 声明全局级别的异常管理信息 -->
<
global-exception-mappings
>
<!-- 这里使用的result必须是一个全局级别的result -->
<
exception-mapping
result
=
"global_error"
exception
=
"java.lang.ArithmeticException"
/>
</
global-exception-mappings
>
|
17_主题作者: 风离紫竹--tryzq521@126.com |
1.主题
Struts2中可以根据不同主题控制UI标签显示的方式。
UI:User Interface用户界面
UI标签:最终在页面上有显示效果的标签
s:textfield、s:password等等
2.Struts2的主题
xhtml:默认。使用表格的方式进行自动排版
simple:不对标签进行任何修饰,设置后标签的很多属性会失效,例如表单标签的label属性
使用simple主题可以避免Struts2自动排版对美工操作的干扰
css_xhtml:使用CSS的方式进行自动排版
……
3.主题的级别
①全局:修改主题常量值
<
constant
name
=
"struts.ui.theme"
value
=
"simple"
/>
|
②某一个域对象中设置主题,在这个域对象的范围内有效
③表单s:form标签内部
<
s:form
action
=
"abc"
theme
=
"xhtml"
>
|
④具体的某一个标签内部
18_类型转换作者: 风离紫竹--tryzq521@126.com |
1.Struts2支持从String到基本数据类型的自动类型转换
请求参数本质上都是通过request.getParameter()方法获取的,这个方法的返回值就是String类型的,而目标Action可以使用本来的类型接收。
2.转换失败
①默认情况下:如果转换失败,则目标字段保持默认值,没有其他变化,就像什么都没发生一样。
②回到指定页面显示提示消息
[1]目标Action类实现ValidationAware接口
继承ActionSupport即可
[2]在action配置中提供一个name=input的result,在这个result中指定返回的页面
[3]默认情况(默认主题)下可以自动显示错误消息
3.定制类型转换的错误消息
①在目标Action类所在的包下创建与目标Action类同名的属性文件
目标Action类类名 | Conversion01 |
属性文件文件名 | Conversion01.properties |
②在属性文件中,创建键值对
格式 |
invalid.fieldvalue.目标字段名=错误消息内容
|
例子 |
invalid.fieldvalue.age=
u5E74u9F84u5FC5u987Bu662Fu6574u6570
|
4.simple主题下显示错误消息
①simple主题下不会自动显示错误消息
②可以通过访问值栈来获取错误消息
Tips:getErrors()方法已过期,使用fieldErrors属性来获取
<
s:property
value
=
"[0].fieldErrors.age[0]"
/>
|
③简便的做法是使用s:fielderror标签显示错误消息
<
s:fielderror
name
=
"age"
/>
|
④修改s:fielderror标签的模板文件去掉ul、li、span
[1]模板文件的位置:struts2-core-2.3.15.3.jar/template/simple/fielderror.ftl
[2]修改步骤
(1)在我们自己的src目录下创建包:template.simple
(2)在template.simple包下创建同名的:fielderror.ftl文件
(3)将原版的文件内容复制到山寨版的文件中,修改即可
注意:下面内容是一个完整的ul的开始标签
<ul<#
rt
/>
<#if parameters.id?if_exists != "">
id="${parameters.id?
html
}"<#
rt
/>
</#if>
<#if parameters.cssClass??>
class="${parameters.cssClass?
html
}"<#
rt
/>
<#else>
class="errorMessage"<#
rt
/>
</#if>
<#if parameters.cssStyle??>
style="${parameters.cssStyle?
html
}"<#
rt
/>
</#if>
>
|
5.自定义类型转换器
①为什么需要自定义类型转换器?
实际开发过程中,有时需要输入的不是基本数据类型,而是某种用户特定的类型。
②创建自定义类型转换器的步骤
[1]创建自定义类型转换器的类——满足Struts2对类型转换器的要求
[2]注册/声明自定义类型转换器类
(1)Action级别:针对目标字段进行设置
<1>确定包含目标字段的类,这个类不一定是Action类,也可以是模型驱动中用到的模型类
<2>创建“包含目标字段的类”的简单类名-conversion.properties文件
<3>文件创建的位置是:“包含目标字段的类”同一个包下
<4>内容
目标字段名=转换器类全类名 |
address=
com.atguigu.convert.converter.AddressConverter
|
(2)全局级别
<1>在src目录下创建xwork-conversion.properties文件
<2>文件内容
要转换的类型全类名=转换器类全类名 |
com.atguigu.convert.entity.Address=
com.atguigu.convert.converter.AddressConverter
|
6.复杂对象
①单一值:使用一个简单的表单元素就可以输入的值。例如:int、String、double等等。
②复杂对象:一个目标字段中包含多个单一值。
③给复杂对象赋值
[1]表单
<
s:textfield
name
=
"address.country"
label
=
"COUNTRY"
/>
|
[2]目标Action类中,除了有必要的setXxx()方法,还必须有getXxx()方法
有getXxx()方法的目的是为了让每一个属性的值都注入到同一个复杂对象中,如果没有,则每个属性值注入都会创建一个新的复杂对象,导致最后只有一个属性有值。
19_2015年12月11日小结作者: 风离紫竹--tryzq521@126.com |
1.声明式异常
①声明式和编程式区别
②会配置Action级别和全局的声明式异常
2.主题
①知道主题含义
②会设置全局使用的主题风格和表单范围内的主题风格
3.类型转换
①Struts2支持String到基本数据类型以及日期类型自动的类型转换。
②类型转换失败后,如何显示错误消息
[1]让目标Action类继承ActionSupport
[2]提供name=input的result
[3]simple主题:s:fielderror标签
[4]可以通过修改s:fielderror标签的模板文件去掉ul、li、span多余的元素
③自定义类型转换器
[1]作用:为自定义的数据类型提供转换机制
[2]实现步骤
(1)创建StrutsTypeConverter的子类
(2)注册自定义类型转换器类
-
-
-
- 基于字段的注册方式:类级别
- 基于类型的注册方式:全局
-
-
④会给复杂对象注入请求参数
address.city
⑤会给List类型的复杂对象注入请求参数
empList[0].address.city
20_国际化作者: 风离紫竹--tryzq521@126.com |
1.在Struts2中提供国际化资源文件:和JavaWeb中一样。
文件名格式:baseName_语言码_国家码.properties
例子:
默认 |
atguiguI18N.properties
|
英语美国 |
atguiguI18N2_en_US.properties
|
中文中国 |
atguiguI18N2_zh_CN.properties
|
内容:key=value
2.以常量形式指定基名
Struts2中国际化资源文件基名的常量:struts.custom.i18n.resources
设置方式
< constant name = "struts.custom.i18n.resources" value = "atguiguI18N"/> |
3.在Action类中实现国际化
①需要实现com.opensymphony.xwork2.TextProvider接口,可以通过继承com.opensymphony.xwork2.ActionSupport实现
②Locale对象最终的来源:Map栈
③读取国际化资源文件的方法
[1]getText(String key);根据key到国际化资源文件中读取对应的值
[2]getText(String key,List<?> paramList);根据key到国际化资源文件中读取对应的值的同时,传入填充字符串占位符的参数列表
[3]在填充占位符时,Struts2能够对日期类型的对象进行国际化
4.JSP页面上实现国际化
①在默认主题下使用强制执行OGNL表达式的方式实现国际化
<
s:textfield
name
=
"usreName"
label
=
"%{[0].getText('userName')}"
/>
|
②在默认主题下使用key属性实现国际化
<
s:textfield
name
=
"usreName"
key
=
"userName"
/>
|
③simple主题
[1]s:submit标签的key属性仍然有效,其他很多标签的key属性不起作用了。
[2]使用s:text标签
<
s:text
name
=
"userName"
/>
|
[3]使用s:param子标签传入字符串的占位符
没有填充的状态:
<
s:text
name
=
"today"
/><
br
/><
br
/>
填充一个普通的字符串:
<
s:text
name
=
"today"
>
<
s:param
value
=
"'mmm'"
/>
</
s:text
>
<
br
/><
br
/>
<!-- 创建一个日期对象 -->
<
s:bean
name
=
"java.util.Date"
var
=
"myDate"
/>
填充日期数据:
<
s:text
name
=
"today"
>
<
s:param
value
=
"#attr.myDate"
/>
</
s:text
>
|
5.在国际化资源文件中执行OGNL表达式
①格式:${OGNL表达式}
②例子:message= ${[0].atguiguMsg}
6.点击超链接切换语言
①超链接的目标URL地址必须是一个action,原因是这个请求一定要经过i18n拦截器才有效
②请求参数的名字
request_locale:Locale信息会被保存到Session域中
request_only_locale:Locale信息不会被保存到Session域中
③i18n拦截器原理——了解
[1]首先从请求参数中尝试获取Locale对象
[2]如果请求参数中没有,则尝试从Session域中获取Locale对象
[3]如果Session域中也没有,则从请求消息头中获取Locale对象
7.字符串占位符
①作用:动态填充固定模式的字符串
②占位符的格式:{index}
index指的不是占位符的顺序,而是给MessageFormat. format( str, "Jone" ,"Kate" , "Jim");方法传入的可变参数的顺序,从0开始。
可以理解为:index就是可变参数对应的数组的下标
21_声明式输入验证来源网址: file:///D:/%E9%87%91%E5%B1%B1%E5%BF%AB%E7%9B%98/Struts2/LibrarySupport/struts-2.3.15.3/docs/WW/docs/basic-validation.html作者: 风离紫竹--tryzq521@126.com |
1.输入验证
①概念:针对用户在表单中输入的数据进行检查
②分类
[1]客户端验证:JavaScript
[2]服务器端验证:Servlet/Action
(1)声明式验证:通过在配置文件中指定验证规则来检查用户输入数据
(2)编程式验证:通过编写代码来检查用户输入数据
2.HelloWorld
①Action类:需要实现ValidationAware接口,通过继承ActionSupport实现
②在Struts2配置文件中提供name=input的result
③引入验证规则配置文件
[1]参考:LibrarySupportstruts-2.3.15.3appsstruts2-blankWEB-INFsrcjavaexampleLogin-validation.xml文件
[2]格式:Action类简单类名-validation.xml
[3]位置:和目标Action类在同一个包下
<!-- 针对age字段进行验证 -->
<
field
name
=
"age"
>
<!-- 使用int验证规则 -->
<
field-validator
type
=
"int"
>
<!-- 设置有效范围的最小值为20 -->
<
param
name
=
"min"
>
20
</
param
>
<!-- 设置有效范围的最大值为50 -->
<
param
name
=
"max"
>
50
</
param
>
<!-- 错误消息,在错误消息中可以使用OGNL表达式动态获取验证器对象的属性值 -->
<
message
>
您输入的年龄不在${ min}到${max}之间
</
message
>
</
field-validator
>
</
field
>
|
3.声明式验证的基本原理[了解]
①验证规则背后的验证器类
[1]验证规则的声明
xwork-core-2.3.15.3.jarcom.opensymphony.xwork2.validator.validatorsdefault.xml文件
<
validator
name
=
"int"
class
=
"com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"
/>
|
[2]验证器类
②验证规则中指定的参数如何传递给验证器对象?
通过验证器对象的setXxx()方法传入
③验证消息如何解析的?
[1]关键:错误消息中可以包含OGNL表达式,例如:${min}或${max}
[2]原理:解析错误消息时,临时将验证器对象压入栈顶,解析完成后从栈顶弹出
4.验证消息国际化
①准备国际化资源文件
②在Struts2配置文件中指定国际化资源文件的基名
③在message标签的key属性中指定国际化资源文件中的键即可
5.simple主题显示错误消息
①使用s:fielderror标签显示错误消息
②可以通过修改s:fielderror标签的模板文件或使用CSS样式等方式去掉多余的圆点
6.非字段验证
①字段验证:仅仅针对一个字段进行的验证,例如:针对一个age字段进行验证。
②非字段验证:验证规则要检查的数据不局限于某一个字段,而是根据多个字段的值综合进行验证。例如:注册用户时,重复两次填写密码,要求两次密码必须一致。
③错误消息被保存到了actionErrors属性中,表示这个错误是“请求”级别的,不是某个字段级别的。使用s:actionerror标签显示
<
s:actionerror
/>
|
7.短路验证
①条件表达式:age > 50 && age < 100
②针对某一个字段同时使用两个验证规则,默认情况下两个验证规则都会生效。如果希望第一规则验证失败,就不进行第二个规则的验证,则可以将第一个规则短路。
③配置方式
<
field
name
=
"email"
>
<!-- 验证字符串长度 -->
<
field-validator
type
=
"stringlength"
short-circuit
=
"true"
>
<
param
name
=
"minLength"
>
5
</
param
>
<
param
name
=
"maxLength"
>
10
</
param
>
<
param
name
=
"trim"
>
true
</
param
>
<
message
>
Email长度要求在${minLength}和${maxLength}之间
</
message
>
</
field-validator
>
<!-- 验证Email格式 -->
<
field-validator
type
=
"email"
>
<
message
>
Email格式不正确
</
message
>
</
field-validator
>
</
field
>
|
8.与类型转换协同工作
①提出问题:针对一个字段进行验证时,如果发生了类型转换错误,那么就没必要再进行声明式验证了。
②涉及到的三个拦截器
[1]params拦截器:负责请求参数类型转换
[2]conversionError拦截器:负责处理类型转换过程中出现的错误
[3]validation拦截器:负责进行“声明式验证”
③实现方式:修改conversionError拦截器的源码
[1]明确要修改源码的类是:com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor
[2]创建包:com.opensymphony.xwork2.interceptor
[3]在com.opensymphony.xwork2.interceptor包下创建ConversionErrorInterceptor类
[4]从原版的类中,复制代码到山寨版的类中
[5]根据需要修改即可:在intercept()方法的return语句之前加入如下代码
//目标:如果发生了类型转换错误,则返回"input",执行name=input的result,不执行后续的其他拦截器
//当然也不执行目标Action类的目标方法了
//1.获取目标Action对象
Object action = invocation.getAction();
//2.检测目标Action是否实现了ValidationAware接口
if(action instanceof ValidationAware) {
//3.如果实现了ValidationAware接口,则转换类型
ValidationAware va = (ValidationAware) action;
//4.检测是否包含错误信息
if(va.hasErrors()) {
//5.如果包含了错误信息,则return "input";
return "input";
}
}
|
④启示:Struts2的任何一个拦截器在执行过程中都有两个选择
[1]继续调用后续的拦截器或目标Action方法
return
invocation
.invoke()
|
[2]直接返回一个字符串,以执行某个已经定义好的result
return "指定result的name值"; |
9.验证消息重用
①年龄和等级的错误消息
Age
must
between
${ min}
and
${max}
|
Grade
must
between
${ min}
and
${max}
|
②归结为整数类型的通用的错误消息
atguigu.int.msg=
Integer
value
must
between
${min}
and
${max}
|
问题:消息写死之后分不清是哪个文本框提交的数据了
③在通用错误消息中获取字段名称
atguigu.int.msg=
${fieldName}
must
between
${min}
and
${max}
|
问题:为什么${fieldName}能够获取到字段名称?
[1]${}里面的字符串是一个OGNL表达式
[2]OGNL表达式没有以#开始,说明访问的是对象栈,同时前面没有使用[index],说明是从栈顶开始查找属性值
[3]验证器对象在解析错误消息时,会临时把验证器对象自己压入栈顶,而验证器对象就具有getFieldName()方法
④字段名称本身也希望能够国际化
atguigu.int.msg=
${getText(fieldName)}
must
between
${min }
and
${max}
age=
AGE~
grade=
GRADE~
|
问题:getText(fieldName)是如何执行的?
[1]解析fieldName得到"age"或"grade"字符串
[2]将"age"或"grade"字符串传递给DefaultTextProvider对象的getText()方法
[3]getText()方法会到国际化资源文件中查找对应的值
10.自定义验证器
①需求:验证身份证号
②创建自定义验证器类:扩展Struts2提供的接口或实现类
FieldValidatorSupport
需要实现validate()方法
/**
* The validation implementation must guarantee that setValidatorContext will
* be called with a non
-
null ValidatorContext before validate is called.
*
*
@param
object the object to be validated.
是执行验证的目标Action类的对象
*
@throws
ValidationException is thrown if there is validation error(s).
*/
void
validate (Object
object
)
throws
ValidationException;
|
③注册
[1]注册的目的:将
自定义验证规则和
自定义验证器类关联起来
[2]在src目录下创建validators.xml文件,加入如下内容
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!
DOCTYPE
validators
PUBLIC
"-//Apache Struts//XWork Validator Definition 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd"
>
<!-- START SNIPPET: validators-default -->
<
validators
>
<!-- name属性:声明验证规则名称,用于在验证规则配置文件中引用 -->
<!-- class属性:自定义验证器类的全类名 -->
<
validator
name
=
"idCard"
class
=
"com.atguigu.validation.validator.IdCardValidator"
/>
</
validators
>
|
11.同一个字段在不同的action请求中使用不同验证规则
①例子:UserAction中,对于userName字段
登录:要求字符串长度在5~10之间
注册:要求字符串长度在15~20之间
②配置文件的格式:<ActionClassName>-<ActionAliasName>-validation.xml
③举例:
登录:UserAction-login-validation.xml
注册:UserAction-register-validation.xml
12.练习:在增删改查操作中,针对添加和编辑图书进行验证
①书名:必须填
②作者:必须填
③价格:必须填
注意:字符串要使用requiredstring规则验证,因为如果字符串没有填,设置请求参数后,接收到的是空字符串:"",和null是不一样的,required只能检测null,不能检查空字符串。
尽量使用包装类型代替基本数据类型进行有没有填写验证
requiredstring规则验证可以进行自动去前后空格
22_文件上传作者: 风离紫竹--tryzq521@126.com |
1.文件上传对表单的要求
[1]method=post
[2]enctype=multipart/form-data
[3]<input type="file" name="xxx"/>
2.服务器端:使用FileUpload组件
commons-fileupload -1.3.jar
commons-io -2.0.1.jar
这两个jar包已经包含在Struts2的jar包组合中了
3.Struts2中文件上传
①参照:org.apache.struts2.interceptor.FileUploadInterceptor类的JavaDoc
②准备
[1]表单:<input type="file" name="xxx"/>
[2]Action类中
|
private
File
logo
;
private
String
logoContentType
;
private
String
logoFileName
;
|
4.文件上传限制条件
①单个大小:设置fileUpload拦截器的参数
[1]Action类实现ValidationAware接口并提供name=input的result
[2]修改fileUpload拦截器的参数
<!-- 声明自定义拦截器和拦截器栈 -->
<
interceptors
>
<!-- 创建自定义拦截器栈 -->
<
interceptor-stack
name
=
"atguiguStack"
>
<!-- 引入Struts2提供的某一个拦截器栈 -->
<
interceptor-ref
name
=
"paramsPrepareParamsStack"
>
<!-- 设置单个文件大小限制 -->
<
param
name
=
"fileUpload.maximumSize"
>
204800
</
param
>
</
interceptor-ref
>
</
interceptor-stack
>
</
interceptors
>
<!-- 引用自定义拦截器栈 -->
<
default-interceptor-ref
name
=
"atguiguStack"
/>
|
②文件类型/扩展名:设置fileUpload拦截器的参数
<
param
name
=
"fileUpload.allowedTypes"
>
image/gif,image/png,image/jpg,image/jpeg,image/bmp
</
param
>
|
③总大小:设置Struts2内置常量
<!-- 设置上传文件的总大小,默认值是2M -->
<
constant
name
=
"struts.multipart.maxSize"
value
=
"1048576"
/>
|
如果超过总大小,那么此时的错误消息是Action级别的,使用actionerror标签显示
④定制错误消息
[1]Struts2自身定义文件上传错误消息的位置
struts2-core-2.3.15.3.jarorg.apache.struts2struts-messages.properties |
[2]定义自己的国际化资源文件即可定制上述错误消息
23_文件下载来源网址: file:///D:/%E9%87%91%E5%B1%B1%E5%BF%AB%E7%9B%98/Struts2/LibrarySupport/struts-2.3.15.3/docs/WW/docs/stream-result.html作者: 风离紫竹--tryzq521@126.com |
1.不建议将文件本身通过超链接暴露给用户
2.建议将文件保存在WEB-INF目录下保护起来
3.通过输入流将文件读取到内存中,再通过输出流返回给浏览器
4.设置相应的响应消息头
①内容类型
②Content-Dispositoin:告诉浏览器当前这个响应是附件形式,需要以文件下载的方式处理
attachment;filename=document.pdf |
5.文件名编码
①非火狐浏览器:UTF-8编码
②火狐浏览器:特殊设置
fileName = "=?utf-8?b?"+new BASE64Encoder().encode(fileName.getBytes("utf-8"))+"?="; |
6.Struts2中通过stream结果类型实现下载
①需要提供的参数
[1]contentType:内容类型
[2]contentLength:下载文件的大小
[3]contentDisposition:Content-Dispositoin响应消息头
[4]inputName:输入流的getXxx()方法定义的属性的属性名,如果是默认值inputStream则可以省略
②提供参数的方式
[1]XML文件中提供
<result name="success" type="stream">
<param name="contentType">image/jpeg</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">attachment;filename="document.pdf"</param>
<param name="bufferSize">1024</param>
</result>
|
[2]Action类中通过getXxx()方法提供
24_防止表单重复提交作者: 风离紫竹--tryzq521@126.com |
1.产生方式
①提交表单后,点浏览器的后退按钮,没有刷新页面的情况下再次提交表单
②表单处理后,如果是转发到目标页面,刷新目标页面
③网速慢的时候,多次点击提交按钮
2.在Session域中保存一个token值
①JSP页面生成一个token值
[1]保存到Session域中
[2]保存到表单隐藏域中
②Servlet中分别从Session域和请求参数中获取token值,进行比较
[1]一致
接受当前请求参数
将token值从Session域中删除
[2]不一致
不接受当前请求参数,表示当前是重复提交的表单
3.Struts2中
①JSP页面上可以使用s:token标签
[1]生成token值
[2]将token值保存到Session域中
[3]将token值保存到表单隐藏域中
②服务器端:使用token或tokenSession拦截器判断是否是表单重复提交
[1]token拦截器:要求Action类实现ValidationAware接口,提供invalid.token的result
<
action
name
=
"TokenAction"
class
=
"com.atguigu.token.action.TokenAction"
>
<!-- 引入token拦截器 -->
<
interceptor-ref
name
=
"token"
/>
<!-- 引入默认拦截器栈,如果不引入,则不会执行默认拦截器栈 -->
<
interceptor-ref
name
=
"defaultStack"
/>
<
result
>
/result.jsp
</
result
>
<!-- 在检测到表单重复提交后,执行这个result -->
<
result
name
=
"invalid.token"
>
/token.jsp
</
result
>
</
action
>
|
定制错误消息:在国际化资源文件中覆盖struts.messages.invalid.token属性即可
[2]tokenSession拦截器:即使发生了表单重复提交也不会让用户感觉到
25_自定义拦截器作者: 风离紫竹--tryzq521@126.com |
1.情景
BookAction:需要用户登录后才能够访问
saveBook()
queryBook()
updateBook()
removeBook()
OrderAction:需要用户登录后才能够访问
saveOrder()
queryOrder()
updateOrder()
removeOrder()
UserAction:负责执行用户登录,用户名密码验证成功后将用户名保存到Session域中
login()
login.jsp
result.jsp
2.自定义拦截器
①作用:就像Struts2内置的拦截器一样,可以在执行目标Action方法之前执行,负责特定的功能。例如:登录检查。
②步骤
[1]创建自定义拦截器类:com.opensymphony.xwork2.interceptor.AbstractInterceptor
[2]注册自定义拦截器类
<
interceptors
>
<!-- 注册自定义拦截器类 -->
<
interceptor
name
=
"loginInterceptor"
class
=
"com.atguigu.myinter.inter.LoginInterceptor"
/>
</
interceptors
>
|
[3]在自定义拦截器栈中引用自定义拦截器:全局范围
<
interceptors
>
<!-- 注册自定义拦截器类 -->
<
interceptor
name
=
"loginInterceptor"
class
=
"com.atguigu.myinter.inter.LoginInterceptor"
/>
<!-- 在全局范围内引用自定义拦截器 -->
<
interceptor-stack
name
=
"atguiguStack"
>
<!-- 引用自定义拦截器 -->
<
interceptor-ref
name
=
"loginInterceptor"
/>
<!-- 引用默认拦截器栈 -->
<
interceptor-ref
name
=
"defaultStack"
/>
</
interceptor-stack
>
</
interceptors
>
<!-- 使用自定义拦截器栈 -->
<
default-interceptor-ref
name
=
"atguiguStack"
/>
|
[4]在action级别引用自定义拦截器
<
action
name
=
"TokenAction"
class
=
"com.atguigu.token.action.TokenAction"
>
<!-- 引入token拦截器 -->
<
interceptor-ref
name
=
"token"
/>
<!-- 引入默认拦截器栈,如果不引入,则不会执行默认拦截器栈 -->
<
interceptor-ref
name
=
"defaultStack"
/>
<
result
>
/result.jsp
</
result
>
<!-- 在检测到表单重复提交后,执行这个result -->
<
result
name
=
"invalid.token"
>
/token.jsp
</
result
>
</
action
>
|
3.Struts2拦截器
①拦截器是在目标Action方法执行之前被调用的
②拦截器执行的顺序是由拦截器栈决定的
③每一个拦截器都能够控制流程
[1]继续执行后续的拦截器以及目标Action方法
return
invocation
.invoke();
chain.doFilter();
[2]直接返回一个字符串,从而执行某个result
return
"toLoginPage"
;
④拦截器是可插拔的:可以根据事件情况将需要的拦截器加入到当前拦截器栈中,也可以将不需要的移除。
编程思想:组件化开发模式。将一个项目的功能拆分,由不同组件来负责,每一个组件中的功能都是相对独立的。如果所有功能混杂在一个类中就难以维护了。——高内聚,低耦合
⑤常用的拦截器功能
[1]默认拦截器栈:
defaultStack
[2]模型驱动拦截器栈:
paramsPrepareParamsStack
[3]声明式异常:exception
[4]国际化:i18n
[5]注入请求参数:
params
[6]注入Web资源:servletConfig
[7]调用prepareXxx()方法:
prepare
[8]将getModel()方法的返回值压入栈顶:
modelDriven
[9]文件上传:
fileUpload
[10]处理类型转换失败错误消息:conversionError
[11]输入验证:
validation
[12]检测错误消息并执行name=input的result:
workflow
26_Struts2总结作者: 风离紫竹--tryzq521@126.com |
1.理论方面
- [理解]Struts2的Action类是多实例模式运行的,每一个请求创建一个Action对象
- [理解]Struts2工作流程
- [理解]值栈的作用和结构
- [理解]拦截器的作用和工作机制,拦截器执行可以根据不同条件决定是继续执行后续拦截器还是执行某个result
- [知道]常用拦截器的功能
- [理解]模型驱动工作机制,知道模型驱动功能中所涉及到的各个拦截器的作用和大致原理
- [理解]区分普通请求和Struts2请求的区别
2.操作方面
- [熟练掌握]发送Struts2请求,在Action类的Action方法中处理
- [熟练掌握]接收/读取Struts2请求中携带的请求参数
-
- 通过Action类的setXxx()方法接收
- 通过Model类的setXxx()方法接收
- 通过parameters Map获取
- [熟练掌握]针对action配置使用通配符映射
- [熟练掌握]在Action类中通过XxxAware接口获取封装过的Web资源,通过ServletXxxAware接口获取原生的Web资源
- [知道]会修改Struts2常量。常用常量值有:
-
- 开发者模式:struts.devMode
- 国际化资源文件基名:struts.custom.i18n.resources
- 默认主题:struts.ui.theme
- 请求最大容量:struts.multipart.maxSize
- 请求扩展名:struts.action.extension
- 动态方法调用:struts.enable.DynamicMethodInvocation
- [熟练掌握]使用模型驱动的方式进行增删改查操作
- [知道]会根据拦截器提供的setXxx()方法修改拦截器参数
- [知道]自定义拦截器的创建和注册
- [知道]在全局或某个action中引用拦截器或拦截器栈
- [熟练掌握]会使用基本的Struts2标签
- [知道]操作单元
-
- 国际化
- 类型转换
- 文件上传下载
- 输入验证
- 防止表单重复提交
|
最后
以上就是传统麦片为你收集整理的Struts2基础--Struts2(你必须要懂得基础)01_Struts2概述02_搭建Struts2运行环境03_Eclipse界面设置04_HelloWorld详解05_请求和响应06_2015年12月5日小结07_值栈09_Struts2通用标签08_2015年12月7日小结10_Struts2表单标签11_2015年12月8日小结12_404错误产生的原因13_Struts2运行流程14_模型驱动15_2015年12月11日复习16_声明式异常管理17_主题18_类型转换19_2015的全部内容,希望文章能够帮你解决Struts2基础--Struts2(你必须要懂得基础)01_Struts2概述02_搭建Struts2运行环境03_Eclipse界面设置04_HelloWorld详解05_请求和响应06_2015年12月5日小结07_值栈09_Struts2通用标签08_2015年12月7日小结10_Struts2表单标签11_2015年12月8日小结12_404错误产生的原因13_Struts2运行流程14_模型驱动15_2015年12月11日复习16_声明式异常管理17_主题18_类型转换19_2015所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复