struts2学习
struts2
struts安装、配置
安装
struts-2.3.37-all.zip 资源文件
- apps :包含基于struts2的实例应用
- docs :struts2的官方文档,教程,API等文档
- lib:struts2框架的核心类库,以及第三方插件类库
- src::struts2框架的全部源代码
其中lib中包含的struts2运行的必需基础包
- struts2-core-2.3.37.jar:struts2的核心类库
- freemarker-2.3.28.jar:struts2的UI标签模板
- commons-logging-1.1.3.jar:日志包
- ognl-3.0.21.jar:ognl表达式包
- xwork-core-2.3.37.jar:xwork类库,struts2在其基础上构建
- commons-fileupload-1.4.jar:文件上传组件
配置
- 在web.xml中配置struts过滤器
<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>
struts2过滤器的配置是固定的
- 在src目录下创建struts2核心配置文件struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="helloDemo" extends="struts-default" namespace="/">
<action name="Hello" class="cn.edu.stu.action.Hello">
<result name="login">/login.jsp</result>
</action>
</package>
</struts>
struts.xml的名称和位置都是固定的,其中的标签含义如下
-
package:对应包配置
包含的属性:
name:标识该package
extends :继承其他包,这样可以继承其他包下的action定义。通常设置为struts-default
namespace:命名空间,默认为“/” -
action:对应一个action的URL
对应属性:
name:指定action访问时的名称:与package中的namespace组成访问连接
class:对应action的路径
method:指定请求action时调用的方法 -
result:根据action方法返回结果,配置到不同的路径
属性:
name:对应action调用方法的返回值
type:配置到路径的方式
type路径有两种方式:
到页面
dispatch:转发(默认)
redirect:重定向
到action
redirectAction:重定向
chain:转发(因为缓存问题,一般不用)
注意:result标签分为2中:全局和局部
global-results 标签内的result为全局
优先级:局部>全局
- 加载子类配置文件
在struts.xml文件中根元素:struts中添加
<include file="配置文件路径名"></include>
- Struts2中常量配置
在struts中大部分常量在配置文件中已经配好了
-
默认常量定义文件位置
-
修改
可以在struts.xml中的
<struts>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
</struts>
name:指定常量名
value:指定常量值
Struts2工作流程
工作流程图
源码流程
- 首先struts过滤器继承Filter
- 在web.xml中,配置了拦截所有请求,在拦截请求后会加载配置文件进行过滤
struts2中action实现的三种方式
- 普通类
不需要继承或实现其他类。但是需要添加一个execute方法
public类型
String 返回类型
public class Hello{
public String execute(){
return "login";
}
}
- 实现Action接口
public class Hello implements Action {
@Override
public String execute() throws Exception {
return null;
}
}
Action接口提供了5个常量
SUCCESS:success 代表成功
NONE:none该表页面不跳转
ERROR:error跳转到错误页面
INPUT:input 数据校验的时候跳转的路径
LOGIN:login跳转到登陆页面
-
继承ActionSupport类(最常用)
ActionSupport是实现了Action接口的子类,功能更强大 -
action和servlet比较
servlet是单例的,第一次访问会创建,后面不会创建
action是多例的,每次访问都会创建
action中方法的访问
通过action标签中的method方法
通过通配符方法
在action标签中的name中使用* ,同时在method中使用占位符来设置来任意匹配字符串
<action name="hello_*" class="cn.edu.stu.action.Hello" method="{1}">
<result name="success">/login.jsp</result>
</action>
login对应了action的方法名
动态访问实现
- 在struts.xml文件中配置:开启动态访问
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
- action配置
<action name="Hello" class="cn.edu.stu.action.Hello">
<result name="success">/login.jsp</result>
</action>
- 前端页面
<a href="${pageContext.request.contextPath}/Hello!login">登陆</a>
在路径中在action的name后面需要添加一个!在加上方法名
方法中方法返回值
有返回值,返回值类型必须是String
没有返回值
可以写成void(不推荐)
写成String类型,返回为none
action获取请求数据
首先是前端
<form action="${pageContext.request.contextPath}/login_login" method="post">
用户名:<input type="text" name="username"/><br/>
密 码:<input type="password" name="password"/>
<input type="submit" value="提交"/>
</form>
ActionContext
public String login(){
//得到参数
ActionContext context = ActionContext.getContext();
Map<String, Object> parameters = context.getParameters();
User user=new User();
String username=(String) ((Object[])parameters.get("username"))[0];
String password=(String) ((Object[])parameters.get("password"))[0];
user.setUsername(username);
user.setPassword(password);
System.out.println(user);
return LOGIN;
}
注意:ActionContext.getContext().gatParameters()得到的是一个map数组。
其中map值是一个object对象(更准确的说是一个object数组)
ServletActionContext(使用最多)
public String login(){
//得到参数
HttpServletRequest request = ServletActionContext.getRequest();
String username = request.getParameter("username");
String password = request.getParameter("password");
User user=new User();
user.setUsername(username);
user.setPassword(password);
System.out.println(user);
return LOGIN;
}
实现ServletRequestAware
通过实现ServletRequestAware接口,从写setServletRequest()方法,得到request对象,来得到参数
public class LoginAction extends ActionSupport implements ServletRequestAware {
private HttpServletRequest request;
@Override
public void setServletRequest(HttpServletRequest request) {
this.request=request;
}
public String login(){
//得到参数
String username = request.getParameter("username");
String password = request.getParameter("password");
User user=new User();
user.setUsername(username);
user.setPassword(password);
System.out.println(user);
return LOGIN;
}
}
封装表单数据到实体对象中
原始方式
先得到参数值,再赋值给一个实体对象
属性封装(表达式封装)
- 在对应的action中,声明一个实体类(User user;)
- 添加该实体类的set,get方法
- 表单中的name中使用设置表达式:user.属性名
public class LoginAction extends ActionSupport {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String login(){
System.out.println(user);
return LOGIN;
}
}
前端jsp页面
<form action="${pageContext.request.contextPath}/login_login" method="post">
用户名:<input type="text" name="user.username"/><br/>
密 码:<input type="password" name="user.password"/>
<input type="submit" value="提交"/>
</form>
在action中定义的user,在前端表单中的name属性中使用
模型驱动封装(推荐使用)
- 首先在对应的action中实现ModelDriven接口
- 在action中创建一个是实体类对象
- 在ModelDriven的一个方法 getModel中返回该实体类对象即可
public class LoginAction extends ActionSupport implements ModelDriven<User> {
private User user=new User();
@Override
public User getModel() {
return user;
}
public String login(){
System.out.println(user);
return LOGIN;
}
}
注意:属性封装和模型驱动封装不能在一个action中对一个表单进行使用,如果同时使用,只会执行驱动封装
表达式与模型驱动的比较
模型驱动封装只能把数据封装到一个实体类对象里面
表达式封装可以把数据封装到不同的实体类中
封装表单数据到List、Map
List:
- 在对应的action中声明一个list,并添加对应的set和get方法
private Listlist - 在前端form中的input中的name中设置表达式:list[0].username
public class LoginAction extends ActionSupport {
private List<User> userList=new ArrayList<>();
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public String login(){
System.out.println(userList);
return LOGIN;
}
}
<form action="${pageContext.request.contextPath}/login_login" method="post">
用户1<br/>
用户名:<input type="text" name="userList[0].username"/><br/>
密 码:<input type="password" name="userList[0].password"/><br/>
用户2<br/>
用户名:<input type="text" name="userList[1].username"/><br/>
密 码:<input type="password" name="userList[1].password"/><br/>
用户3<br/>
用户名:<input type="text" name="userList[2].username"/><br/>
密 码:<input type="password" name="userList[2].password"/><br/>
<input type="submit" value="提交"/>
</form>
map
- 在对应的action中声明一个map,并添加对应的set和get方法
private Map<String,User>map - 在前端form中的input中的name中设置表达式:map[“one”].username
public class LoginAction extends ActionSupport {
private Map<String,User> userMap=new HashMap<>();
public Map<String, User> getUserMap() {
return userMap;
}
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public String login(){
System.out.println(userMap);
return LOGIN;
}
}
<form action="${pageContext.request.contextPath}/login_login" method="post">
用户1<br/>
用户名:<input type="text" name="userMap['one'].username"/><br/>
密 码:<input type="password" name="userMap['one'].password"/><br/>
用户2<br/>
用户名:<input type="text" name="userMap['two'].username"/><br/>
密 码:<input type="password" name="userMap['two'].password"/><br/>
用户3<br/>
用户名:<input type="text" name="userMap['three'].username"/><br/>
密 码:<input type="password" name="userMap['three'].password"/><br/>
<input type="submit" value="提交"/>
</form>
在struts2中使用ognl
什么是ognl
OGNL是Object-Graph Navigation Language的缩写,对象图导航语言,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。
ognl的作用
Struts2默认的表达式语言就是OGNL,它具有以下特点:
- 支持对象方法调用。例如: objName methodName()。
- 支持类静态方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名|值名]。
例如: @java lang aaa@qq.com(foo %s’, ‘bar’)。 - 支持赋值 操作和表达式串联。
例如: price=100, discount=0.8, calculatePrice(), 在方法中进行乘法计算会返回80。 - 访问OGNL. 上下文(OGNL context)和ActionContext。
- 操作集合对象。
ognl三要素
OGNL的操作实际上就是围绕着OGNL结构的三个要素而进行的,分别是表达式(Expression)、根对象(Root Object)、上下文环境(Context) ,下面分别讲解这三个要素,具体如下。
- 表达式
表达式是整个OGNL的核心,OGNL会根据表达式去对象中取值。所有OGNL操作都是针对表达式解析后进行的。它表明了此次OGNL操作要“做什么”。表达式就是一一个带有语法含义的字符串,这个字符串规定了操作的类型和操作的内容。OGNL支持大量的表达式语法,不仅支持这种“链式”对象访问路径,还支持在表达式中进行简单的计算。 - 根对象(Root)
Root对象可以理解为OGNL的操作对象,表达式规定了“做什么”,而Root对象则规定了“对谁操作”。OGNL称为对象图导航语言,所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象。 - Context 对象
实际上OGNL的取值还需要一个上下文环境。设置了Root对象,OGNL可以对Root对象进行取值或写值等操作,Root 对象所在环境就是OGNL的上下文环境(Context) 。上下文环境规定了OGNL的操作“在哪里进行”。上下文环境Context是一一个Map类型的对象,在表达式中访问Context中的对象,需要使用“#”号加上对象名称,即“#对象名称”的形式。
实例1:字符串长度
@Test
public void func() throws OgnlException {
OgnlContext context=new OgnlContext();
Object value = Ognl.getValue("'12345'.length()", context, context.getRoot());
System.out.println(value);
}
调用字符串对象的length()方法
实例2:
ognl还可以访问对象的静态方法,语法格式
@类的全路径名@静态方法
@类的全路径名@属性名
@Test
public void func() throws OgnlException {
OgnlContext context=new OgnlContext();
Object value = Ognl.getValue("@aaa@qq.com()", context, context.getRoot());
System.out.println(value);
}
实例3:获取值
@Test
public void func() throws OgnlException {
OgnlContext context=new OgnlContext();
User user=new User();
user.setUsername("张三");
user.setPassword("123");
context.setRoot(user);
String username = (String) Ognl.getValue("username", context, context.getRoot());
System.out.println(username);
}
在struts2标签中使用ognl
-
首先需要在前端jsp页面引入标签
<%@ taglib prefix=“s” uri="/struts-tags" %> -
使用ognl
<s:property value=""/>
ognl需要配合struts2标签一起使用,其中value中存放ognl标签
OGNL中#和%的作用
#:用于从context中取数据
%:在struts2中的表单标签中,无法直接使用ognl表达式,需要将其阔在%{ }中
值栈
什么是值栈
ValueStack是Struts 的一一个接口,字面意义为值栈,OgnlValueStack 是ValueStack 的实现类,客户端发起一个请求struts2 架构会创建一个action 实例同时创建一- 个OgnlValueStack 值栈实例,OgnlValueStack贯穿整个Action 的生命周期,struts2 中使用OGNL将请求Action的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值。
值栈的内部结构
root继承了ArryList,实现压栈和出栈功能。存储action实例和请求参数。
context即ognl上下文对象,其中存储了一些引用:request、session、application等,其中最重要的是其Root为CompoundRoot.
context示例图:
ActionContext与ValueStack的关系
- 在創建ActionContext的吋候創建Valuestack的対象,將Valuestack対象給ActionContext.
- ActionContext 中有一-个Valuestack的引用. Valuestack 中也有- -个ActionContext的引用.
使用struts2标签<s:debug>标签可以查看值栈结构
其中栈顶元素是对应的action对象的引用
获取值栈对象
- 通过ActionContext
ValueStack stack= ActionContext.getContext().getValueStack();
- 通过request
ValueStack stack= (ValueStack) ServletActionContext.getRequest()
.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
向值栈中存放数据
- 使用值栈对象的set(String,value)方法
- 使用值栈对象的push()方法
- 在对应的action内创建一个变量,并添加get方法
public class LoginAction extends ActionSupport {
//方法三:通过在action中添加一个变量
private String username="wangwu";
public String getUsername() {
return username;
}
public String login(){
ValueStack valueStack = ActionContext.getContext().getValueStack();
//方法一:通过set()
valueStack.set("username","zhangsan");
//方法二:通过push()
valueStack.push("lisi");
return LOGIN;
}
}
前端测试代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--添加struts标签头--%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>测试</title>
</head>
<body>
<%--s:debug 标签可以测试值栈--%>
<s:debug/>
</body>
</html>
测试图:
从值栈中获取值
- jsp中struts2标签+ognl表达式
<s:property value=""/> - 取值如果是字符串
<s:property value=“字符串变量名”/> - 实体类
<s:property value=“实体类变量.属性”/>
list
-
通过数组下标获取对象
<s:property value=“list[0].属性”/>
…… -
通过struts2的标签遍历
<s:iterator value=”list”>
<s:property value=“属性”/>
</s:iterator >
在循环体内,已经确定是对应数组值了,所以在取得属性值时,可以省略list[下标] -
使用struts2标签和#
值栈中通过set和push存放值,取出方式
- set(key,value)
<s:property value=”key”> - push(“”);
push()存放数据,没有名称,会存放在top数组
取出方式
<s:property value=”[0].top”>
使用EL表达式也可以取出值栈的值
使用EL取出域对象中的方式:
getAttribute();
在struts2中对getAttribute方法进行了增强:先从域对象中取出,如果域对象不存在,再从值栈中取。
拦截器
拦截器是什么
拦截器,在action对象创建之后,action的方法执行之前。struts2里面封装了很多的功能,有很多拦截器,不是每次这些拦截器都执行,每次执行默认的拦截器
默认拦截器文件位置
拦截器原理
-
AOP思想(面向切面编程)
在已有的基础功能基础之上,扩展功能,不通过修改源代码的方式来扩展功能(通过接口编程:修改配置文件等) -
责任链模式
要执行多个操作,例如:增加、修改、删除三个操作
首先执行增加操作,完成后,需要一个类似放行的操作;然后依次进行。和Java web中过滤器中的过滤链放行类似的思想
AOP和责任链在拦截器中应用
-
拦截器在action对象创建之后,action的方法执行之前执行
-
在action方法执行之前执行默认拦截器,执行过程使用aop思想,在action没有直接调用拦截器的方法,使用配置文件方式进行操作
-
在执行拦截器时候,执行很多的拦截器,这个过程使用责任链模式
假如执行三个拦截器,执行拦截器1,执行拦截器1之后做放行操作,执行拦截器2,执行拦截器2之后做放行,执行拦截器3,执行拦截器3之后放行,执行action的方法
查看源码
- 执行action
- 创建action对象,使用动态代理方式
- 执行action的方法
- 执行很多的拦截器,遍历执行
类似于放行的操作的方法
过滤器与拦截器的区别
过滤器理论上可以拦截任意资源
拦截器只能拦截action
自定义拦截器
拦截器接口类:Interceptor
该接口有三个方法
- void destroy();//销毁操作
- void init();//初始化操作
- String intercept(ActionInvocation var1) throws Exception;//拦截逻辑操作
在开发中一般继承MethodFilterInterceptor,只需要考虑拦截操作,并且该类可以在配置文件中设置不拦截某些方法
实际步骤
- 写一个类继承MethodFilterInterceptor
- 实现其中的doInterceptor()方法,完成拦截逻辑
- 在struts.xml文件中进行配置
a) 在package中声明该拦截器
b) 在action中使用该拦截器
c) 需要再使用模式拦截器:否则不适用默认拦截器
继承MethodFilterInterceptor的拦截器可以指定不拦截一些方法
在使用拦截器时
<param name=”excludeMethods”>方法名</>
-
代码实例
action: LoginAction.java
interceptor: CustomInterceptor.jvar
jsp:login.jsp、welcome.jsp -
逻辑:
首先从login.jsp登陆-》LoginAction调用login方法判断成功跳转到welcome页面,否则跳转到login页面
这里需要演示拦截器:拦截login方法,和配置不拦截login方法
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--添加struts标签头--%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>登陆界面</title>
</head>
<body>
<h1>登陆</h1>
<form action="${pageContext.request.contextPath}/LoginAction?login" method="post">
用户名:<input type="text" name="username"/><br/>
密 码:<input type="password" name="password"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
welcome.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>欢迎界面</title>
</head>
<body>
<h1>欢迎登陆系统</h1>
</form>
</body>
</html>
LoginAction.java
public class LoginAction extends ActionSupport {
public String login(){
//得到request对象
HttpServletRequest request = ServletActionContext.getRequest();
String username = (String) request.getParameter("username");
if (username.equals("user")) {
//如果用户名为user,保存到session中
request.getSession().setAttribute("username",username);
//到列表页面
return "welcome";
}
//返回登陆页面
return LOGIN;
}
}
CustomInterceptor.jvar
public class CustomInterceptor extends MethodFilterInterceptor {
//拦截操作
protected String doIntercept(ActionInvocation actionInvocation)
throws Exception {
System.out.println("CustomInterceptor……");
HttpServletRequest request = ServletActionContext.getRequest();
if (request.getSession().getAttribute("username")!=null) {
//放行
actionInvocation.invoke();
}
//用户名错误,到登陆界面去
return "login";
}
}
struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="login" extends="struts-default" namespace="/">
<!--声明自定义拦截器-->
<interceptors>
<interceptor name="CustomInterceptor" class="cn.edu.stu.interceptor.CustomInterceptor"></interceptor>
</interceptors>
<action name="LoginAction" class="cn.edu.stu.action.LoginAction" method="login">
<!--使用自定义的拦截器-->
<interceptor-ref name="CustomInterceptor">
<!--
excludeMethods:设置不拦截的方法
includeMethods:设置拦截的方法
-->
<!--<param name="excludeMethods">login</param>-->
</interceptor-ref>
<!--使用默认拦截器-->
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="login">/login.jsp</result>
<result name="welcome">/welcome.jsp</result>
</action>
</package>
</struts>
上一篇: JavaSE第十二章 继承