struts2学习

struts安装、配置

安装

下载地址
https://struts.apache.org

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的名称和位置都是固定的,其中的标签含义如下

  1. package:对应包配置
    包含的属性:
    name:标识该package
    extends :继承其他包,这样可以继承其他包下的action定义。通常设置为struts-default
    namespace:命名空间,默认为“/”

  2. action:对应一个action的URL
    对应属性:
    name:指定action访问时的名称:与package中的namespace组成访问连接
    class:对应action的路径
    method:指定请求action时调用的方法

  3. result:根据action方法返回结果,配置到不同的路径
    属性:
    name:对应action调用方法的返回值
    type:配置到路径的方式
    type路径有两种方式:
    到页面
    dispatch:转发(默认)
    redirect:重定向
    到action
    redirectAction:重定向
    chain:转发(因为缓存问题,一般不用)
    注意:result标签分为2中:全局和局部
    在这里插入图片描述
    global-results 标签内的result为全局
    优先级:局部>全局

  • 加载子类配置文件
    在struts.xml文件中根元素:struts中添加
<include file="配置文件路径名"></include>
  • Struts2中常量配置
    在struts中大部分常量在配置文件中已经配好了
  1. 默认常量定义文件位置
    在这里插入图片描述

  2. 修改
    可以在struts.xml中的

<struts>
	<constant name="struts.i18n.encoding" value="UTF-8"></constant>
</struts>

name:指定常量名
value:指定常量值

Struts2工作流程

工作流程图

在这里插入图片描述

源码流程

  1. 首先struts过滤器继承Filter
    在这里插入图片描述
  2. 在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的方法名

动态访问实现

  1. 在struts.xml文件中配置:开启动态访问
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
  1. action配置
<action name="Hello" class="cn.edu.stu.action.Hello">
            <result name="success">/login.jsp</result>
 </action>
  1. 前端页面
<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;
    }
}

封装表单数据到实体对象中

原始方式

先得到参数值,再赋值给一个实体对象

属性封装(表达式封装)

  1. 在对应的action中,声明一个实体类(User user;)
  2. 添加该实体类的set,get方法
  3. 表单中的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属性中使用

模型驱动封装(推荐使用)

  1. 首先在对应的action中实现ModelDriven接口
  2. 在action中创建一个是实体类对象
  3. 在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:

  1. 在对应的action中声明一个list,并添加对应的set和get方法
    private Listlist
  2. 在前端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

  1. 在对应的action中声明一个map,并添加对应的set和get方法
    private Map<String,User>map
  2. 在前端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 String@format(foo %s’, ‘bar’)。
  • 支持赋值 操作和表达式串联。
    例如: price=100, discount=0.8, calculatePrice(), 在方法中进行乘法计算会返回80。
  • 访问OGNL. 上下文(OGNL context)和ActionContext。
  • 操作集合对象。

ognl三要素

OGNL的操作实际上就是围绕着OGNL结构的三个要素而进行的,分别是表达式(Expression)、根对象(Root Object)、上下文环境(Context) ,下面分别讲解这三个要素,具体如下。

  1. 表达式
    表达式是整个OGNL的核心,OGNL会根据表达式去对象中取值。所有OGNL操作都是针对表达式解析后进行的。它表明了此次OGNL操作要“做什么”。表达式就是一一个带有语法含义的字符串,这个字符串规定了操作的类型和操作的内容。OGNL支持大量的表达式语法,不仅支持这种“链式”对象访问路径,还支持在表达式中进行简单的计算。
  2. 根对象(Root)
    Root对象可以理解为OGNL的操作对象,表达式规定了“做什么”,而Root对象则规定了“对谁操作”。OGNL称为对象图导航语言,所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象。
  3. 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("@java.lang.Math@random()", 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

  1. 首先需要在前端jsp页面引入标签
    <%@ taglib prefix=“s” uri="/struts-tags" %>

  2. 使用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对象的引用

获取值栈对象

  1. 通过ActionContext
ValueStack stack= ActionContext.getContext().getValueStack();
  1. 通过request
ValueStack stack= (ValueStack) ServletActionContext.getRequest()
               .getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

向值栈中存放数据

  1. 使用值栈对象的set(String,value)方法
  2. 使用值栈对象的push()方法
  3. 在对应的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

  1. 通过数组下标获取对象
    <s:property value=“list[0].属性”/>
    ……

  2. 通过struts2的标签遍历
    <s:iterator value=”list”>
    <s:property value=“属性”/>
    </s:iterator >
    在循环体内,已经确定是对应数组值了,所以在取得属性值时,可以省略list[下标]

  3. 使用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的方法

查看源码

  1. 执行action
    在这里插入图片描述
  2. 创建action对象,使用动态代理方式
    在这里插入图片描述
  3. 执行action的方法
    在这里插入图片描述
  4. 执行很多的拦截器,遍历执行
    在这里插入图片描述
    类似于放行的操作的方法
    在这里插入图片描述

过滤器与拦截器的区别

过滤器理论上可以拦截任意资源
拦截器只能拦截action

自定义拦截器

拦截器接口类:Interceptor

该接口有三个方法

  • void destroy();//销毁操作
  • void init();//初始化操作
  • String intercept(ActionInvocation var1) throws Exception;//拦截逻辑操作

在开发中一般继承MethodFilterInterceptor,只需要考虑拦截操作,并且该类可以在配置文件中设置不拦截某些方法

实际步骤

  1. 写一个类继承MethodFilterInterceptor
  2. 实现其中的doInterceptor()方法,完成拦截逻辑
  3. 在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>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值