JEE_Struts2
一、Struts2运行流程:
Browse---发送表单请求*.action--> Tomcat---web.xml--> 匹配请求*.action,调用Filter--->
ActionContext...FilterDispatcher---查找struts.xml文件-->
ActionProxy代理类-ActionInvocation对象-加载Struts2拦截器---Struts2引擎-->
匹配请求action的name属性,调用处理量Action---execute()-->Struts2引擎--->
ActionInvocation查找struts.xml文件---匹配<success>和<result>--->返回JSP视图---> 客户端浏览器.
web.xml中过来所有请求,
FilterDispatcher负责拦截所有的用户请求.当用户请求到达后,该Filter会过滤用户请求,
如果匹配到用户请求以*.action结尾的,该请求就被转入Struts2框架处理.
Struts2对用户的每一次请求都会创建一个Action,类似单例模式,线程安全的.(Struts1的Action是存放在缓存中的)
---------------------------------------------------- 二、程序文件详解
一.Action类详解
1.Action类
1)Action: Struts2中只要一个public类包含了一个public返回值为String类型的execute()方法,这个类就可以是一个Action处理类.
2)ActionSupport: public类继承ActionSupport类,这个类也是一个Action处理类.
ActionSupport类实现了5个接口Action,Validateable,ValidationAware,TextProvider,LocaleProvider;
并事先定义了SUCCESS,ERROR,NONE,INPUT,LOGIN静态常量和execute(),validate()等方法供调用.
其中execute()方法默认返回SUCCESS.
2.Action传值
1)字段传值: Action类中定义username,password属性字段并生成get和set方法.
2)模型传值: 把属性字段封装成一个类,并生成其get和set方法,也就是封装成一个JavaBean类.
Action中只要实例化获取该JavaBean类,再提供类的set和get方法,就能获取到所有的字段属性.
定义了JavaBean类后,调用属性名要改成"模型名.属性名"的方法,如"user.password"
//请求参数的接收
3.在Action类中访问Servlet API
1)
通过ActionContext访问:ActionContext类提供了一个静态的getContext()方法来获取ActionContext对象,然后根据其对象来获取
一些Servlet API的对象.如:
ActionContext actionContext = ActionContext.getContext(); //获取ActionContext对象
Map session = actionContext.getSession(); //获取对象---得到的是Map类型,实现把Servlet从Action中分离出来,减少耦合
......
......
JSP页面访问:
---[用EL表达式访问]
${applicationScope.app}<br>
${sessionScope.session}<br>
${requestScope.request}<br>
---[用"#***"访问]
<s:property value="#request.r1" />
<s:property value="#session.s1" />
<s:property value="#application.a1" />
2)
常用的request和response提供了专门的类获取:
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
HttpSession session = request.getSession();
-----------------------
例:
ActionContext actionContext = ActionContext.getContext(); //获取ActionContext对象
actionContext.getApplication().put("app", "Application范围");
actionContext.getSession().put("session", "Session范围");
actionContext.put("request", "request范围");
---
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("request", "Request范围");
request.getSession().setAttribute("session", "Session范围");
ServletContext servletContext = ServletActionContext.getServletContext();
servletContext.setAttribute("app", "Application范围");
3)
通过实现*Aware接口获取:
Action实现ApplicationAware, 或CookiesAware, 或SessionAware接口.
定义:
private Map<String, Object> request;
private Map<String, Object> session;
private Map<String, Object> application;
Struts2框架自动调用方法注入值:
setRequest(Map<String, Object> request),
setSession(Map<String, Object> session),
setApplication(Map<String, Object> application).
(这样我们就可以直接使用定义的request, session, application了)
涉及到的设计思想:
DI依赖注入: (不用自己去获取值,直接由Struts2框架注入好值),
或称IOC控制反转: (本来自己控制去获取值的,现在成框架控制且注入值了,所以可以称控制反转了)。
//Action搜索顺序:
按照包的命名空间,按层次查找包,如果在自定义的命名空间的包中找不到action,
则跳转到默认命名空间对应的包中查找action,
如果还找不到action则跳转到默认配置的action:<default-action-ref>,
如果没有配置默认action,则报错!
二.Struts2配置详解:
二.Struts2配置详解:
--------------
web.xml文件
<web-app>...</web-app>标签下定义多个标签.
配置过滤器过滤/*所有的请求
自定义过滤器必须实现Filter接口,实现方法init(),doFilter(),destroy().
过滤器:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
过滤器关联url:
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
---------------
struts.properties文件
存放key-value对,每个key就是一个Struts2属性值,供覆盖更改Struts2框架中default.properties定义的默认信息
如:
struts.action.extension就定义了Struts2处理的请求后缀*.action.
----------------------------------------------
struts.xml文件
Struts2默认配置文件是struts.xml,存放于WEB-INF/classes目录下(Eclipse中存放于src目录下).
Struts2读取到struts.xml的内容后,是以JavaBean的形式存放在内存中.
这样以后Struts2对用户的每次请求处理将使用内存中的数据,而不是再次去读取struts.xml文件.
<struts>...</struts>标签中可以定义多个标签:
<include>: 导入其它xml配置文件
<constant>: 配置一些常量信息,如编码形式,开发模式等。有name和value属性。
<package>
<bean>
--------------------------------------------------
<package>
包:可以定义action和拦截器等.
<package name="default" namespace="/test" extends="struts-default">
<action name="login" class="mao.s.LoginAction" method="execute">
<result name="success">/welcome.jsp</result>
</action>
</package>
name: [必需的]供继承
extends: 至少继承struts-default(定义了Struts2的拦截器和Result类型,不可缺少)
namespace: 命名空间减少重复代码,命名空间的内容会作为action路径的一部分,缺省值为" "
abstract: 标识只能被其它包继承,本身不能定义action
<package>包中还可以定义多个标签:
<action>,
<default-action-ref>,
<default-class-ref>,
<default-interceptor-ref>,
<global-exception-mappings>.
----------------------------------------
<action>配置
1)
<action name="login" class="mao.s.LoginAction" method="execute">
<result name="success">/welcome.jsp</result>
</action>
name: [必需的]指定Action的名称,*.action后缀可加可不加
class: 指定处理类的路径, 缺省值为ActionSupport
method: 指定用Action类中哪个方法进行请求处理, 缺省值为execute()
concerter: 指定Action使用的类型转换器
<result>: Action处理后要返回的JSP页面,其name属性缺省值为success.
2).在<action>中使用通配符
为防止当定义了多个处理方法就要定义多个<action-method>,且要返回多个<result>,Struts2引入了通配符.
通配符可自动完成匹配,但这样对Action类中方法的命名就有了限制,必须和请求名相同,返回视图的名称也要相同.
如:
<action name="*" class="mao.s.LoginAction" method="{1}">
<result name="success">/{1}.jsp</result>
</action>
此处的"*"有表单提交的action名自动匹配.
此处的"{1}"则是自动匹配"*"的值,即请求名.
3).访问Action类中方法的其它实现方式
1<action>中不需提供method,但在客户端浏览器的<form>中定义
如:
<form action="login!login.action" method="post">
...
</form>
其中第一个login对应<action>中的name属性
第二个login对应<action>的method属性
2<form>中也不需定义,但由<form>表单中的按钮定义
如:
<form ...>
<s:submit value="登录" method="login">
</form>
-------------------------------------------------
<result>
1) <result name="success">/welcome.jsp</result>
<result>的name属性缺省值为success
2) <result name="Action类返回值" type="跳转结果类型">
<param name="参数名">参数值</param>
</result>
3)全局结果
定义在<action>...</action>中的<result>配置的只是局部结果,
定义全局结果:<global-result>...</global-result>
//
<result-type>属性:跳转结果的类型。
dispatcher: 请求转发,跳转到JSP页面(服务器跳转)。
redirect: 浏览器重定向,跳转到JSP页面(客户端跳转)。
redirectAction: 浏览器重定向到action。
painText: 转发到页面显示源代码
chain: 服务器跳转到action, 同时传递数据。
<result>/hello.jsp</result> //转发,type缺省值为dispatcher,name缺省值为"success"
<result name="success" type="redirect">/hello.jsp</result> //重定向
<result name="success" type="redirect">/hello.jsp?username=${username}</result> //重定向并传入参数值
<result type="redirectAction">login</result> //重定向到同个包内的action
<result type="redirectAction">
<param name="actionName">login</param>
<param name="namespace">/test</param>
</result> //重定向到不同包(含命名空间)的action
<result type="plainText">index.jsp</result>
<result type="plainText">
<param name="location">/index.jsp</param>
<param name="charSet">UTF-8</param>
</result> //plainText显示源代码
<global-results>
<result name="error">error.jsp</result>
</global-result>
---------------------
Struts2常量配置加载顺序:
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
后一个配置的信息会覆盖前一个配置的信息。
----------------------------------------------
//拦截器
1.Struts2框架本身提供了大量的拦截器,配置在struts-default.xml中,
Struts2通过这些拦截器完成框架的自动匹配操作.
拦截器类:
1)实现Interceptor接口,包含三个方法:
init().
destroy().
intercept(invocation):实现拦截操作,
用参数invocation调用invoke()方法,就可以将控制权交给下一个拦截器或者交给Action类处理.
invoke()方法执行完后返回,intercept()继续往下执行,直到返回.
2)继承AbstractInterceptor类,该类实现了Interceptor接口,实现了init()和destroy()的空方法,这样就只需复写intercept()方法.
3)注册拦截器:
如果为某个action配置了拦截器,默认的拦截器就不会起作用,如果想要起作用还要再次配置"defaultStack"
拦截器: <interceptor>
拦截器栈: <interceptors>
<package>
<interceptors>
<interceptor name="***" class="" />
<interceptor-stack name="***Stack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="拦截器i" />
</interceptor-stack>
</interceptors>
</package>
注册:
<action...>
<interceptor-ref name="***Stack" />
--------------------------------------------
//输入校验
一.代码方式校验
1.---校验的是Action中所有的方法(所有Action):
继承ActionSupport类, 重写validate()方法校验,
当某个数据校验失败时,调用ActionSupport.addFieldError()方法添加校验失败信息到框架的fieldErrors中,
框架的fieldErrors包含失败信息后,框架会根据struts.xml文件将请求转发到<result type="input">对应的JSP页面,
在JSP视图中可以同步显示错误信息,通过<s:fielderror/>.
2.---校验Action中指定的方法:
继承ActionSupport类, 重写validateXxx()方法校验方法名为Xxx()的方法,
3.处理流程
fieldError存放错误信息:
conversionError类型转换错误,validaterXxx()错误,validater()错误--->存放错误到fieldError再提示出错
二.xml配置方式对action进行校验
1.---校验的是Action中所有的方法(所有Action):
继承ActionSupport,提供校验文件放在和action类同个包下,
文件取名规则:ActionClassName-validation.xml
Struts2框架提供了很多校验器.
------------------------------------------ActionClassName-validation.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<field name="username"> //指定要检验的属性
<field-validator type="requiredstring"> //指定校验器1(内置的)
<param name="trim">true</param>
<message>用户名不能为空!</message> //校验失败后的提示信息
</field-validator>
</field>
<field name="mobile">
<field-validator type="requiredstring"> //指定校验器2(内置的)
<message>手机号不能为空!</message>
</field-validator>
<field-validator type="regex"> //指定校验器3(内置的)
<param name="expression"><![CDATA[^1[358]\d{9}$]]></param>
<message>手机号格式不正确!</message>
</field-validator>
</field>
</validators>
----------------------------------------
框架根据ActionClassName-validation.xml中的设置自动进行校验
校验出错后,框架会根据struts.xml文件将请求转发到<result type="input">对应的JSP页面,
在JSP视图中可以同步显示错误信息,通过<s:fielderror/>.
2.---校验Action中指定的方法:
配置文件取名:ActionClassName-ActionName-validation.xml,如:UserAction-user_add-validation.xml
三、客户端的Struts2方式校验 Struts2框架中的表单具有自动校验功能:
<s:form validate="true">自动校验. 用客户端进行验证时,Struts2框架会把配置的验证信息转换成JavaScript代码在客户端页面进行验证.
------------------------------------------------------
//类型转换 1.内置类型转换器 2.自定义类型转换器: 1)继承DefaultTypeConverter类,复写converValue()方法 2)局部注册:新建配置文件"action类型-conversion.properties", 关联转换器和请求数据. 3)全局注册:新建配置文件"xwork-conversion.properties", "待转换类型 = 转换器完整类名".
-----------------------------------
//上传
<input type="file" name="image">
<input type="submit" value="上传"
//文件上传
<form>中要定义enctype="multipart/form-data"属性.
字段和方法的命名要按照Struts2的规则。
<s:form action="${pageContext.request.contextPath}/upload.action" method="post" enctype="multipart/form-data">
<s:file name="image" label="上传的文件:"></s:file> //JavaBean中要提供与"image"名称相同的字段的set和get方法
<s:submit value="上传"></s:submit>
</s:form>
private File image; //得到文件File类型, 命名与file的name属性相同
private String imageFileName; //得到文件名String类型, 命名为"字段名+FileName"
private String imageContentType //得到文件类型名
。。。省略set和get方法
@Override
public String execute() throws Exception {
InputStream is = new FileInputStream(getImage());
OutputStream os = new FileOutputStream("g:\\" + imageFileName);
byte buffer[] = new byte[1024];
int count = 0;
while((count = is.read(buffer))>0){
os.write(buffer,0,count);
}
os.close();
is.close();
return SUCCESS;
}
上传大一点点的文件:设置<constant>属性.
上传较大文件:采用插件应用程序的方式.
如果上传多个文件则讲字段类型声明为List[]数组类型或者List<File>类型.
----------------------
//
中文编码
String username = URLEncoder.encode("传智播客","UTF-8");
解码:
<%= URLDecoder.decode( new String(request.getParameter("username").getBytes("ISO8859-1"),"UTF-8"),"UTF-8" )%>
//给action注入值
采用<param>标签
//
<constant name="struts.devMode" value="true"></constant> <!-- 打印更多错误信息 -->
<constant name="struts.configuration" value="true"></constant> <!-- 修改配置文件后是否自动重新加载 -->
//判断用户是否登录:保存值在Session中
登录: <% request.getSession().setAttribute("user", "isLoad"); %>
退出: <$ request.getSession().removeAttribute("user");
获取判断: Object user = ActionContext.getContext().getSession().get("user");
//设置消息传递
设置值: ActionContext.getContext().put("message", "您没有访问权限");
读取: ${message}
//其他目录下的JSP页面
<result name="message">/WEB-INF/message.jsp</result>
//地址路径:
${pageContext.request.contextPath}/命名空间/***.action
//匹配正则表达式
Boolean Pattern.compile("^1[358]\\d{9}").matcher(mobile).matcher
//国际化
//标签防止重复提交
1.在表单<s:form>中加入<s:token></s:token>标签
2.在struts.xml文件中配置拦截器,重复提交的检测由服务器框架自动完成.
---------------------
<package name="itcast" namespace="/test" extends="struts-default">
<action name="itcast" class="cn.itcsst.action.PersonAction">
<interceptor-ref name="defaultStack"/> //默认框架拦截器
<interceptor-ref name="token"/> //重复提交拦截器
<result name="invalid.token">/index.jsp</result> //重复提交后跳转到此JSP页面
<result>/WEB-INF/page/message.jsp</result>
</action>
</package>
//OGNL表达式
//
Struts2框架会匹配的Action有*.action或者***(省略.action)也可以.
//
Struts2框架---复杂麻烦-->可扩展化----(类似设计模式:简单事情复杂化)
//路径问题
Struts2中的路径问题是根据action的路径而不是根据jsp的路径来确定的,*.jsp路径统一使用绝对路径。
tomcat中"/"代表的是其根路径。
注意action的路径应该是" namespace路径 + *.action路径.
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPorth() + path + "/";
%>
分别拿到路径: http + localhost + 8080 + path
这样以后写请求路径就可以写成: <%= basePath %>index.jsp
//eclipse设置---15讲
//Action数据传递
action参数传输过来时与DomainModel实体类的属性字段匹配不上:
1. action --- DTO数据传输对象 ---DomainModel实体类.
2. 直接定义属性接收参数,不用DomainModel(JavaBean).
3. 模型驱动: Action实现ModelDriven<ClassName>接口
//
添加错误信息: addFieldError("***", "***");
显示错误信息:
<s:fielderror fieldName="name" theme="simple"/>
<br>
<s:property value="errors.name[0] />
<s:debug></s:debug>
//通配符
<action name="*_*" class="mao.s.{1}Action" method="{2}">
<result>/{1}_{2}_success.jsp</result>
</action>
请求:Teacher_add.action, Teacher_delete.action, Teacher_update.action .
//
<form name="f" action="" method="post">
Name: <input type="text" name="name" />
Password: <input type="text" name="password" />
<br>
<input type="button" value="submit1" onclick="javascript:document.f.action='login/login1'; document.f.submit();" />
<input type="button" value="submit2" onclick="javascript:document.f.action='login/login2'; document.f.submit();" />
</form>
//
http中传递的参数都是String类型。
//
ExtJS——第56讲
源代码——BBS2009项目
声明式异常处理——第66,67讲
I18N国际化——第69-73讲
源码解析——第75讲(请求-处理的过程)
拦截器栈模拟——第76讲(面向切面编程 || Interceptor的原理)
-----------------------------
《Java EE实用教程》郑阿奇 + 传智播客Struts2 + 尚学堂马士兵Struts2
浙公网安备 33010602011771号