- Struts2 的核心开发包
- Struts2 配置文件
- Struts2 域对象
- Struts2 编程流程
- Action 组件
- 使用通配符配置 Action
- Action 中如何访问 Session
- 通过 ActionContext 对象访问 Session 对象(不推荐)
- 通过实现 SessionAware 接口访问 session
- Action 如何访问 request 对象
- Action 如何访问 application 对象
- Action 如何访问 response 对象
- Action 中如何访问 application
- Action 组件属性注入
- 请求参数数据自动封装到 Action 对象中
- 参数值注入到 Action 对象对应的属性中
- 参数值封装成 JavaBean 对象,再注入到 Action 对象中对应的属性中
- 复选框的参数值可以自动封装成数组对象,注入 Action 对应的成员变量中
- 数据的传送(Action 对象中的属性值输出到页面)
- Result 组件
- dispatcher
- redirect
- redirectAction
- json
- ValueStack 对象
- struts2 标签/ognl表达式/el表达式
- Struts2 的 MVC 结构图
- Struts2 的原理图
- 表单的请求资源路径写法
struts2-core.jar:Struts2 的控制器就在里面
xwork-core.jar:WebWork 的内核,Struts2 就是基于 WebWork 升级而来的
ognl.jar:使用 Struts2 的标签和 ognl 表达式来编写显示逻辑,输出模型层的数据
freemarker.jar:模板框架开发包,相当于 JSP,同样可以输出 HTML 静态页面,属于视图技术,实际中很少使用
struts-default.xml
定义了 struts2 在底层运行的时候所需要使用到的组件。
struts.xml
我们自定义的 xml 文件,名称可以随便起。这个 xml 文件用来声明、定义我们所写的 action 组件和哪个请求名对应。
使用 Eclipse 工具开发,那么 struts.xml 放在 src 目录下,如果使用 IDEA 工具开发,那么 struts.xml 文件放在 resources 目录下。编译后该文件位于 WEB-INF/classes/ 目录下。
这个配置文件里面的标记名称以及属性都是约定好,我们必须按照这样的规则去配置。
必须在 package 元素内声明 action 组件。
package 用来定义和管理 action、result、interceptor 等组件。
package 继承父包 struts-default,在父包定义好的组件,子包都继承过来了。
package 的 namespace 属性是用来限定 action 的请求名,如果有两个 action 中的请求名是一样的,那么通过 namespace 就可以区别开来。
如果有多个 package 元素,那么 package 的 name 属性值必须唯一。
在一个 package 标签内有多个 action,那么 action 的 name 属性值必须唯一。
action 元素的 name 属性定义请求名,class 属性用来指定 action 组件的全路径名,method 属性指定调用action 组件的方法名
result 元素用来定义响应视图,name 属性与 action 组件中的业务方法的返回值对应,type 属性用来指定响应类型,默认值 dispatcher,表示转发。
控制器调 action 组件的业务方法后,得到 String,然后控制器通过该 String 到 struts.xml 找到对应的 result,那么控制器就会通过转发的机制调 result 中定义的响应组件。
如果访问的 web 应用是 Struts 2 架构的,那么请求资源路径末尾的字符串会被服务器解释成 Action 的名称,控制器会根据这个 Action 名称(请求名称)到 struts.xml 文件中找到对应的 Action 组件。
例如:https://www.dpqyw.com/javaex/projectlist,会把 projectlist 解释成 Action 组件的名称。
Struts2 域对象Servlet 中常使用到的 request、session、application 对象,在 struts2 中被重新封装过,变成 Map 类型的。但是在 jsp 页面中的 el 表达式仍旧是访问原始的 request、session、application 对象。
当然在 struts2 中的 Action 组件中也还是可以获取到原始的 request、session、application 对象的。
Action 组件如何使用 request/session/application 对象?
第一种方式:利用ActionContext工具类来获取
第二种方式:利用Aware接口来获取,只有Action组件去实现Aware接口,框架底层才会注入request/session/application对象到Action
组件的属性中,其它类是不可以这样做的。
总结:在 Servlet 组件中获取到 reques 对象、session 对象、application 对象和 struts2 的 Action 组件中所获取到的是不一样的,默认情况下 struts2 中获取到的是封装成 Map 类型的对象,当然通过某些 API 或者 Action 类去实现特定的 Aware 接口,也是可以获取到 Http 类型的对象的。
Struts2 编程流程首先要导入 struts2 的开发包(5个核心包),我们不需要写控制器,以前我们会写一个 servlet 类作为控制器,一般命名为
ActionServlet,然后在service方法中写控制逻辑(if…else语句),现在 struts2 框架已经封装好一切,它提供了一个
由filter充当的控制器,所以我们就不要写了,就连控制器的控制逻辑也不要写了,只要在 web.xml 配置好控制器就可以了。
配置的步骤和以前配置 servlet、filter 一样,不过 struts2 提供的控制器的类名比较长,可以在 struts2-core-2.1.8.jar 中找到。
url-pattern 的写法是
public class BaseAction implements SessionAware {
// 为了可以被子类继承使用,所以访问控制符可以默认或者protected,不建议public。
// private的变量不会被子类继承,不过子类对象中含有父类私有变量数据,无法直接访问。
Map LoginAction 继承 BaseAction: LoginAction 实例化后,继承而来的 setSession() 会被调用,将传入 session 对象会存入到变量 session 中。 按照这样的机制,我们可以让所有的 Action(如 LoginAction)继承实现了 SessionAware 接口的 BaseAction,当需要更换 Struts2 框架为其他框架时,只需要修改 BaseAction 即可(另外的框架只要提供一个类似 SessionAware 接口的接口,由 BaseAction 继承)。 BaseAction 去实现接口 ServletRequestAware,那么继承自 BaseAction 的业务类 Action 就可以直接使用 request 对象了。 BaseAction 的代码如下: BaseAction 去实现接口 ServletContextAware,那么继承自 BaseAction 的业务类 Action 就可以直接使用 application 对象了。 BaseAction 去实现接口 ServletResponseAware,那么继承自 BaseAction 的业务类 Action 就可以直接使用 response 对象了。 application 将存放在 ValueStack 的 context 中。 所谓 Action 组件属性的注入,是指 Action 中声明的属性,可以在 struts.xml 中通过配置进行赋值。 然后在 struts.xml 中通过标签 配置属性值,如下所示: 在 Action 组件对象创建的时候,Action 对象中的属性 page 被赋值 1;rowsPerPage 就被赋值为 5,那么 Action 对象在调用 DAO 时,将页数和每页行数两个参数传入 DAO 的相关方法中,从而得到需要的数据。 在控制器调 Action 组件之前会先通过一组拦截器,其中一个拦截器会获得请求参数值,然后将参数值赋给 Action 组件中的对应属性上,其实是调用 Action 对象的 setter 方法给对应的成员变量赋值。 例如,页面上有用户名称和密码的文本输入框,提交表单数据后,用户名称和密码会自动封装到 Action 对象中,即赋值给 Action 对象中对应的成员变量。 页面的示例代码: Action 的示例代码: 如果 Action 中的成员变量是 JavaBean 类型,可以将参数值赋值给 Bean 对象中对应的属性。 如下所示,页面的示例代码: Action 的示例代码: 提交表单时传递的参数是 user.userName 和 user.password,而 Action 的成员变量就是 User 类型的,那么Struts2的相关拦截器就会先构造一个 User 对象,然后将参数值封装到 User 对象中,再将 User 对象复制给 Action 的成员变量 user。 日期数据也会自动封装到 JavaBean 对象中对应的 java.util.Date 类型的成员变量中。 例如,ProjectCreateAction 有 Project 类型的成员变量: Project 对象含有 Date 类型的成员变量 startDate 和 endDate: JSP 页面的表单项: 注意参数名称的格式:Action属性名称.JavaBean的属性名称。 JSP 的示例代码: Action 组件的示例代码: Action 组件的属性可以接收请求参数值,也可以把属性值向外传送(即输出到页面中),在转发的 JSP 中使用 EL 表达式来获取并输出。 在 Struts2 中,Result 的功能非常强大的。Reslut 也是类,职责是生成视图。视图可以是多种多样的(比如 JSP、JSON、报表、freeMarker 等),这些视图都可以由 Reslut 负责。 查看 struts-default.xml 文件,“struts-default” 共定义了 10 个 Result 常用类型: 不写 type,默认就是 dispatcher。 在 Struts2 中,转发和重定向的地址都是从应用名(虚拟目录)之后开始写,如下所示: 与 redirect 效果相同,只要写 Action 的名字即可: 如果想使用 json,必须引用 struts2-json-plugin.jar。接着 struts.xml 文件中,action 所在的 package 的 extends 填写“json-default”。这里的 json-default 是 struts2-json-plugin 项目下的 struts-plugin.xml 中的 package 的名称。json-default 包继承了 struts-default,在此基础上增加了 json 的 result-type。 LoginAction 的示例代码: JSP 的示例代码: struts.xml 中配置 result 的 type 为“json”: 最后服务端返回给客户端 json 字符串: struts2 的控制器接收到请求后,会先创建 request 和 response 对象,接着会创建 session 对象,接着会创建 HttpServletRequest 的另外一种实现类的对象 root,接着会创建 ValueStack 对象,接着创建 Action 组件的对象,将 Action 对象存储在 ValueStack 对象中,而 ValueStack 对象则会存储在 request 对象中,ValueStack 这个对象内部有个 Ognl 引擎,这个引擎有两个属性,属性名分别为 root 和 context,root 属性指向的是一个CompoundRoot 类的实例,是栈结构。context 属性指向的是一个 Map 对象。而 Action 对象则存储在 root 对象中,同时把 request 对象、session 对象、application 对象的引用地址存放在 ValueStack 对象中的 context 对象里。 摘要: 在 JSP 中使用 struts2 标签 这个 struts2 标签和 ognl 组合的含义: 在 struts.xml 配置文件中使用 el 表达式,如下所示: 这个 el 表达式可以获取到 Action 对象中 user 属性的值,这个属性指向的是一个 user 对象,从而得到 user 对象的 userId 属性的值。 el 表达式 ${user.userId} 底层的运行过程: 接着控制器将请求转发给 user.jsp 文件,在转发路径中携带参数 userId 值,在 JSP 文件中含有下面的代码: 转发路径中携带参数,那么参数值会存放在 request 对象中,jsp组件可以共享同个请求中的 request 对象中的数据,所以可以通过 request对象获取到 userId 参数值。 result 组件的 type="dispatcher",jsp 中的 el 表达式和 ognl 表达式才可以获得 Action 对象的属性值,因为 Action 对象实际上是在 request 对象中,转发的组件可以共享 request 对象中的数据;如果改成 redirect 类型,是重新发送请求,而且只是调用 jsp 组件,根本就没有调用 Action 组件,新的请求中 ValueStack 对象是重新创建的,request、session 等对象也是新建的,ValueStack 对象中的 root 栈顶根本就没有 Action 对象,所以 jsp 中的 el 表达式和 ognl 表达式根本无法获取到前一次请求中的 Action 对象属性中的数据。 非常有意思,type=“dispatcher”,我们要获得 Action 对象的属性值,也要在 JSP 中写点东西(el表达式或ognl表达式),可是 注:Action 类的访问路径在 Struts 2 的 struts.xml 文件中配置。 JSP 页面如果不通过 Struts2 控制器转发,则必须写 URI,即必须从项目的虚拟目录开始写,如下所示: JSP 页面如果通过 Struts2 控制器转发,则不需要写项目的虚拟目录和 package 的命名空间,如下所示:package priv.lwx.struts2.action;
import priv.lwx.struts2.action.dao.UserDAO;
import priv.lwx.struts2.action.entity.User;
public class LoginAction extends BaseAction {
private User user;
private UserDAO userDAO = new UserDAO();
public String login() {
System.out.println(user.getUserName());
System.out.println(user.getPassword());
User user = userDAO.login(this.user.getUserName(), this.user.getPassword());
if (user != null) {
// 登录成功
// 继承自父类的变量session,LoginAction实例化后,控制器会调用setSession(Map session)方法,
// 将session对象赋值给变量session
session.put("user", user);
return "success";
}
// 登录失败
return "fail";
}
public String form() {
return "success";
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
package priv.lwx.struts2.action;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.SessionAware;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public class BaseAction implements SessionAware, ServletRequestAware {
// 为了可以被子类继承使用,所以访问控制符可以默认或者protected,不建议public。
// private的变量不会被子类继承,不过子类对象中含有父类私有变量数据,无法直接访问。
Map
Action 如何访问 application 对象
ActionContext act = ActionContext.getContext();
Map
例如在 ProjectListAction 中声明 page 和 rowsPerPage 两个属性,代码如下:package priv.lwx.struts2;
import priv.lwx.struts2.dao.ProjectDAO;
import priv.lwx.struts2.entity.Project;
import java.util.List;
public class ProjectListAction {
// input
private int page;
private int rowsPerPage;
// output
private List
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
package priv.lwx.struts2.action;
public class LoginAction1 {
private String userName;
private String password;
public String execute() {
// 因为Struts2的拦截器会自动将参数值赋值给Action对象中对应的成员变量,所以这里可以直接访问变量,获取变量的值
System.out.println(userName);
System.out.println(password);
return "success";
}
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;
}
}
参数值封装成 JavaBean 对象,再注入到 Action 对象中对应的属性中
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
package priv.lwx.struts2.action;
import priv.lwx.struts2.action.dao.UserDAO;
import priv.lwx.struts2.action.entity.User;
public class LoginAction extends BaseAction {
private User user;
private UserDAO userDAO = new UserDAO();
public String login() {
System.out.println(user.getUserName());
System.out.println(user.getPassword());
User user = userDAO.login(this.user.getUserName(), this.user.getPassword());
if (user != null) {
// 登录成功
// 继承自父类的变量session,LoginAction实例化后,控制器会调用setSession(Map session)方法,
// 将session对象赋值给变量session
session.put("user", user);
return "success";
}
// 登录失败
return "fail";
}
public String form() {
return "success";
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
public class ProjectCreateAction {
private Project project;
...
public class Project {
private Integer id;
private String no;
private String name;
private Date startDate;
private Date endDate;
...
开始时间:
结束时间:
package priv.lwx.struts.memory.action;
import priv.lwx.struts.memory.dao.ProjectDAO;
public class ProjectDeleteAction extends BaseAction {
private Integer[] projects;
private ProjectDAO projectDAO = new ProjectDAO();
public String execute() {
projectDAO.delete(projects);
return "success";
}
public Integer[] getProjects() {
return projects;
}
public void setProjects(Integer[] projects) {
this.projects = projects;
}
}
数据的传送(Action 对象中的属性值输出到页面)
1.dispatcher
2.redirect
3.redirectAction
4.stream
5.json
redirect
redirectAction
json
package priv.lwx.struts2.action;
import priv.lwx.struts2.action.dao.UserDAO;
import priv.lwx.struts2.action.entity.User;
public class LoginAction extends BaseAction {
private User user;
public String login() {
System.out.println(user.getUserName());
System.out.println(user.getPassword());
return "success";
}
public String form() {
return "success";
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
1.ognl 表达式,就是由 Ognl 引擎解析表达式,然后到 ValueStack 对象中查询指定属性的值。
2.struts2 中的 el 表达式,底层是到 pageContext、request、root、session、application 等域对象中查询指定属性的值。root 对象其实 HttpServletRequest 的实现类的实例对象,这个对象中的某个方法会去 request 对象取数据,取不到则会调用 Ognl 引擎到 ValueStack 对象中取数据,所以 struts2 中的 el 表达式底层有使用到 ognl 表达式。
Ognl 引擎会解析 ognl 表达式"user.userId",直接到 ValueStack 对象中 root 栈顶去获取属性 user 的值,而这个值是 user 对象,接着获取 user 对象的属性 userId 的值并输出。
我们想象有一个 el 引擎,它先到 pageContext、request 找属性 user 的值,其实是找不到的,然后调用 Ognl 引擎,将 el 表达式转换成 ognl 表达式,直接到 ValueStack 对象中的 root 对象的栈顶查找,结果找到了,因为 Action 对象就在栈顶,它有 user 这个属性,而 user 属性引用的是一个 User 对象,该对象含有 userId 属性,于是可以获取到该属性的值。<%
out.print("
"+request.getParameter("userId")+"
");
%>
注:Struts2 框架中,Action 类中的成员变量在 jsp 中可以使用 el 表达式直接访问。



