1、原理
可参考:http://blog.youkuaiyun.com/laner0515/article/details/27692673/ 、 http://blog.youkuaiyun.com/wuwenxiang91322/article/details/11070513
2、第一个项目——HelloWorld
(1)步骤:
①建立web项目,拷贝struts-2.5.10.1-min-lib/lib中的jar包。还需添加一个log4j-core
②在web.xml中配置Struts2的前端控制器-StrutsPrepareAndExecuteFilter
③定义一个POJO类:HelloAction
public class HelloAction {
public String sayHello(){
System.out.println("Hello World!");
//返回逻辑视图
return "success";
}
}
④拷贝struts.xml文件到项目的类路径或资源文件中,文件名不能修改
但可以针对每个action类设定一个struts的xml配置文件,该文件可改名字
但是在struts.xml配置文件中需要引入该配置文件
<include file="\action\helloworld\struts-hello.xml"></include>
⑤在struts.xml文件中,配置HelloAction.(把HelloAction交给Struts框架管理)
⑥访问Action
协议+主机地址+端口号+上下文路径+命名空间(namespace)+资源名称(action name)
3、Struts2中的6大配置文件
(1)Struts2框架按照如下顺序加载struts2配置:
①default.properties 该文件保存在 struts2-core-2.5.10.1.jar 中 org.apache.struts2包里面:包含了Struts2的默认常量配置
②struts-default.xml 该文件保存在 struts2-core-2.5.10.1.jar:包含了框架依赖的对象配置和结果类型,拦截器等配置.
③struts-plugin.xml 该文件保存在Struts2框架的插件中:struts-Xxx-2.5.10.1.jar.由插件提供
上述三个文件时框架自带的,不能修改,只能使用。
---------------------------------------------------------
④struts.xml 该文件是web应用默认的struts配置文件.重点.配置自定义的Action和其他信息.
⑤struts.properties 该文件是Struts的默认配置文件-->可以修改default.properties 的常量配置.
⑥web.xml 该文件是Web应用的配置文件
上述三个文件是可以修改操作的。
---------------------------------------------------------
如果多个文件配置了同一个struts2 常量,则后一个文件中配置的常量值会覆盖前面文件配置的常量值.
注意:一般在struts.xml中做常量配置.
4、常见常量配置
(1)指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker 、velocity的输出
<constantname="struts.i18n.encoding" value="UTF-8"/>
(2)指定需要Struts2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开
<constant name="struts.action.extension" value="action,,"/>(3)开发模式下使用,这样可以打印出更详细的错误信息
<constant name="struts.devMode" value="true" />:修改struts.xml之后,不需要重启Tomcat.
5、struts.xml配置文件元素详解
(1)package元素—— <struts>根元素的子元素
用来对多个<action>元素分类管理,和Java中的package没有关系.
①常见的属性:
name: <package>的名字
extends: 表示当前<package>继承哪一个<package>,一般都是:struts-default。而struts-default其实就是struts-default.xml中<package>元素的名字,继承struts-default之后,就拥有了该<package>定义的所有资源.(结果返回类型,拦截器..)
namespace: 表示命名空间,一般的以"/"打头.,命名一般以模块名.如: /crm, /oa. 和<action>的name决定了一个Action类的访问路径.
abstract: 抽象的,缺省值是false. 若一个<package>的abstract="true",那么该<package>中就不能再定义<action>元素,只能用来继承.
(2)action元素——<package>元素的子元素
专门用来配置Action类的,理论上一个Action类匹配一个<action>元素.
<action name="" class="" method=""/>
①常见的属性:
name: action的名称,在同一个<package>中,action的名字必须唯一. 和<package>的namespace共同决定了一个Action类的访问路径.
注意:action的name值不能以"/"打头.
class:一个Action类的全限定名. 缺省值:com.opensymphony.xwork2.ActionSupport类.
method:当前Action动作访问的方法, 缺省值:execute.
如果不填class和method,默认会找com.opensymphony.xwork2.ActionSupport类的execute方法
(3)result元素——配置结果视图
①局部结果视图:<result>定义在<action>中。优先级较高
②全局结果视图:<result>定义在<global-results>中,而<global-results>在<package>中
③常见的属性:
name:Action方法返回的逻辑视图名称. 缺省值:success
type:结果的跳转类型.该类型的值在struts-default.xml中已经预定义好了. 缺省值:dispatcher
常见的type值(结果类型):
dispatcher: 表示从Action请求转发到页面(JSP).
redirect: 表示从Action重定向到页面(JSP).
chain: 表示从Action请求转发到另一个Action.
redirectAction: 表示从Action重定向到另一个Action.
stream: 表示返回流. 文件下载时使用.
6、创建Action类的三种方式
(1)使用公共的POJO类作为Action. 提供公共的无参数的Action方法.(不推荐).
①缺点:
没有一种方式约束Action方法必须是公共的无参数的.
Action方法的返回逻辑视图名可以自定指定. 有时起名不规范. 比如:"ooxx".
(2)定义一个类,实现于com.opensymphony.xwork2.Action接口.并覆写execute方法即可.(不推荐)
①缺点: 不支持国际化,数据校验,消息机制
(3)定义一个Action类,继承于com.opensymphony.xwork2.ActionSupport类.(推荐)
7、Action多方法调用
(1)原始方式
StudentAction.java
public class StudentAction extends ActionSupport{
private static final long serialVersionUID = 1L;
public String list(){
System.out.println("学生列表");
return NONE;
}
public String save(){
System.out.println("添加学生");
return NONE;
}
public String delete(){
System.out.println("删除学生");
return NONE;
}
public String update(){
System.out.println("更新学生");
return NONE;
}
}
Action中多个Action方法会造成<action>配置的臃肿。
(2)解决方案:
方案1: DMI:动态方法调用 :官方不推荐
格式: action名!方法名
缺点:不安全,调用需要使用方法名。
在Struts2新的版本中,默认的关闭了DMI.若我们需要使用DMI,就需要在struts.xml配置常量,启用动态方法调用.
此时:<action/>元素不需要指定method属性值.
测试:
方案2: 使用通配符的方式类配置: 通配符:*
①使用单个通配符
测试:
②使用多个通配符
测试:
8、访问ServletApi
有三种方式
(1)通过让Action类去实现感知接口(ServletRequestAware、ServletResponseAware、ServletSessionAware)
缺点:和ServletAPI藕合严重
eg:
①getServletApi1Action.java
public class getServletApi1Action extends ActionSupport implements ServletRequestAware,ServletResponseAware{
private static final long serialVersionUID = 1L;
private HttpServletResponse resp;
private HttpServletRequest req;
public String execute(){
System.out.println(req.getParameter("username"));
return NONE;
}
@Override
public void setServletResponse(HttpServletResponse resp) {
this.resp = resp;
}
@Override
public void setServletRequest(HttpServletRequest req) {
this.req = req;
}
}
②struts.xml
<struts>
<package name="getServletPkg" extends="struts-default"
namespace="/getServlet">
<action name="api1" class="action.getServletApi.getServletApi1Action">
</action>
</package>
</struts>
③测试:
实现原理:
通过拦截器实现
(2)使用ServletActionContext类,该类提供很多静态方法可以返回Servelet API对象
eg:
①getServletApi2Action.java
public class getServletApi2Action extends ActionSupport {
private static final long serialVersionUID = 1L;
public String execute() {
HttpServletRequest req = ServletActionContext.getRequest();
req.setAttribute("requestName","requestVal");
HttpSession session = ServletActionContext.getRequest().getSession();
session.setAttribute("sessionName", "sessionVal");
ServletContext context = ServletActionContext.getServletContext();
context.setAttribute("contextName","contextVal");
return SUCCESS;
}
}
②struts.xml
<struts>
<package name="getServletPkg" extends="struts-default"
namespace="/getServlet">
<action name="api2" class="action.getServletApi.getServletApi2Action">
<result>/views/getServletApi/getServlet.jsp</result>
</action>
</package>
</struts>
③getServlet.jsp
<h3>使用ServletActionContext</h3>
<hr>
request:${requestName}
<br>
session:${sessionName}
<br>
servletContext:${contextName}
<br>
④测试:
(3)使用ActionContext类,本身是Struts2对Servlet API的封装.(推荐使用)
ActionContext:——Action的环境对象,每一次请求都是一个新的Action对象,一个ActionContext对象封装了这一次请求的相关数据.
ActionContext使用了ThreadLocal模式,所以说是线程安全的.
eg:
①getServletApi3Action.java
public class getServletApi3Action extends ActionSupport{
private static final long serialVersionUID = 1L;
public String execute(){
ActionContext context = ActionContext.getContext();
context.put("requestKey","requestValue");
context.getApplication().put("appKey","applicationVal");
context.getSession().put("sessionKey","sessionValue");
return SUCCESS;
}
}
②struts.xml
<struts>
<package name="getServletPkg" extends="struts-default"
namespace="/getServlet">
<action name="api3" class="action.getServletApi.getServletApi3Action">
<result>
/views/getServletApi/getServlet.jsp
</result>
</action>
</package>
</struts>
③getServlet.jsp
<h3>使用ActionContext</h3>
<hr>
request:${requestKey}
<br>
session:${sessionKey}
<br>
servletContext:${appKey}
<br> <hr>
④测试:
9、Action获取请求参数
两种方式:
(1)Action本身作为Model对象,通过setter方法封装(属性注入)
①把Action本身作为一个领域模型;
②要求页面提交的参数名称和Action中对应属性名称相同(只需要有属性的setter方法即可),但是如果把Action本身当成模型,则也需要提供相应的getter方法;
③该功能是由拦截器完成的;并且自带类型转换;
④在Servlet中,因为线程安全的问题,不能在Servlet中定义成员变量;但是在Struts2中,Action是每次请求都重新创建,所以肯定在Action中定义模型属性;
优点:简单;缺点:如果模型参数过多,Action中的属性会非常多,就会把模型和业务混淆在一起;
eg:
①GetParameterAction.java
public class GetParameterAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private String username;
private String password;
@Override
public String execute() throws Exception {
System.out.println("username:"+username);
System.out.println("password:"+password);
return NONE;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
②struts.xml
<struts>
<package name="getparamPkg" extends="struts-default" namespace="/">
<action name="login1" class="action.getParam.GetParameterAction"/>
</package>
</struts>
③getParam.jsp
<fieldset>
<legend>Action本身作为Model对象,通过setter方法封装</legend>
<form action="${pageContext.request.contextPath}/login1" method="post">
账户:<input type="text" name="username"><br> 密码:<input
type="text" name="password"><br> <input type="submit"
value="登陆">
</form>
</fieldset>
④测试:
(2)创建独立Model对象,页面通过ognl表达式封装(属性注入)
两种写法:
i.一个字段,并提供对应的getter和setter;
ii.一个字段,action自己new出来,并提供getter方法;
优点:action和model责任分离;缺点:页面必须按照和domain一致的规则来写;
①User.java
public class User {
private String age;
private String username;
private String password;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + "]";
}
}
②GetParameterAction2.java
public class GetParameterAction2 extends ActionSupport {
private static final long serialVersionUID = 1L;
private User user;
//自己new出来,并提供getter方法
//private User user = new User();
@Override
public String execute() throws Exception {
System.out.println("username: "+user.getUsername());
System.out.println("password: "+user.getPassword());
System.out.println("age: "+user.getAge());
return NONE;
}
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
}
③struts.xml
<package name="getparamPkg" extends="struts-default" namespace="/">
<action name="login2" class="action.getParam.GetParameterAction2"/>
</package>
④getParam.jsp
<fieldset>
<legend>创建独立model对象,页面通过ognl表达式封装</legend>
<form action="${pageContext.request.contextPath}/login2" method="post">
账户:<input type="text" name="user.username"><br> 密码:<input
type="text" name="user.password"><br> <input type="submit"
value="登陆">
</form>
</fieldset>
⑤测试
注意:
如果Action中没有user的getter方法会出现什么情况?
针对两种写法:
i.一个字段,并提供对应的getter和setter;
以该例子注入username为例,找到user.username,在Action类中寻找user属性,判断user是否为空,若为空则创建一个,调用setUser方法,然后调用user.setUserName方法注入数据到user模型。然后继续处理user.password,判断user是否为空,若为空则创建一个,此时如果没有提供getter方法,则无法取得user对象,继续创建,那么前面注入的数据将会丢失。
ii.一个字段,action自己new出来,并提供getter方法;
此时无需setUser方法,因为已经自行创建(user不为空)但仍然需要提供getter方法,否则无法取出user对象,设置值或取出值无法进行。