Struts2入门及组件介绍

本文深入解析Struts2框架的配置流程,主控制器StrutsPrepareAndExecuteFilter的作用,Action的编写规则与请求处理方法,以及Result配置。涵盖Action与Result的生命周期,请求处理流程,页面跳转功能,登录处理,和不同返回类型接口的使用建议。

Struts2入门及组件介绍

配置流程

  1. 配置Filter控制器StrutsPrepareAndExecuteFilter(在web.xml中添加filter)

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <filter>
            <filter-name>StrutsMvc</filter-name>
            <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
            <!--默认加载src/struts.xml,如果想自己配置则配置init-param属性-->
            <init-param>
                <param-name>config</param-name>
                <param-value>struts.xml</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>StrutsMvc</filter-name>
            <!--匹配所有action请求-->
            <url-pattern>*.action</url-pattern>
        </filter-mapping>
    </web-app>
    
  2. Action配置(struts.xml配置)

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
            "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
            "http://struts.apache.org/dtds/struts-2.1.7.dtd">
    
    <struts>
        <!--包定义,一个包可以定义多个action,extends是可以继承的属性,可以继承父包的东西,必须继承struts-default才能使用Result组件,避免冲突名称-->
        <package name="demo1" extends="struts-default">
            <!--
            默认method参数为execute
            可以不配置method方法
            如果你自定义了方法名
            那么method就改为对应的
            -->
            <!--
            name为请求名,需要去掉扩展名和前缀名
            例如请求
            /hello.do->hello
            /hello.action->hello
            /demo/hello.action->hello
            这种情况.do肯定不会被匹配
            那下面两种呢?
            我们有一个namespace的属性
            默认就是/hello.action
            配置就是namespace="/demo"
            这个加在package上面
            -->
            <action name="hello" class="com.xdl.action.HelloAction"></action>
        </package>
    </struts>
    

    这样配置那么访问/hello.action就会找到HelloAction类exeute方法

  3. Result配置(struts.xml)

    <action name="hello" class="com.xdl.action.HelloAction">
        <!--
            name对应Action返回值;
            type指定Result返回值类型
            例如type="dispatcher"就是转发
            转发的详细路径配置在result标签区域
        -->
        <result name="success" type="dispatcher">/WEB-INF/hello.jsp</result>
    </action>
    

其中struts.xml中的result标签属性type可供选择的值如下表

class描述
chaincom.opensymphony.xwork2.ActionChainResult
dispatcherorg.apache.struts2.dispatcher.ServletDispatcherResult转发
freemarkerorg.apache.struts2.views.freemarker.FreemarkerResult
httpheaderorg.apache.struts2.dispatcher.HttpHeaderResult
redirectorg.apache.struts2.dispatcher.ServletRedirectResult重定向
redirectActionorg.apache.struts2.dispatcher.ServletActionRedirectResult
streamorg.apache.struts2.dispatcher.StreamResult
velocityorg.apache.struts2.dispatcher.VelocityResult
xsltorg.apache.struts2.views.xslt.XSLTResult
plainTextorg.apache.struts2.dispatcher.PlainTextResult
postbackorg.apache.struts2.dispatcher.PostbackResult

下面是struts-default.xml文件的内容,该文件位于struts2-core-2.3.31.jar根目录

<result-types>
    <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
    <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
    <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
    <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
    <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
    <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
    <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
    <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
    <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
    <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
    <result-type name="postback" class="org.apache.struts2.dispatcher.PostbackResult" />
</result-types>

自行比对

主控制器StrutsPrepareAndExecuteFilter

它有以下责任:

  1. 负责接收用户请求[一个请求创建一个Action,在Spring中是单例的,而Struts则是非单例的]
  2. 负责调用Action中请求处理方法
  3. 负责根据Action返回值调用Result处理
  4. 主控制器会自动创建Action对象和Result对象
  5. 默认情况下,主控制器StrtusPrepareAndExecuteFilter只处理*.action或者没有扩展名的请求,只有符合请求才能进入Action的调用[进过测试,就算直接修改web.xml的url-pattern为*.do也是404,因为主控制器本身就自带过滤功能]因此一个请求想要进入服务,就得经过双重过滤,一层web.xml,一层主控制器,能进入的只有.action和不带后缀的
  6. namespace可以理解为请求前缀,例如<package name="demo1" extends="struts-default" namespace="/"></package>这样就是不追加请求,默认就是这样的

第五点补充

如果想要修改仅接收.do请求,找到配置文件default.properties能看到struts.action.extension=action,,,不能直接修改啊,我们可以在struts.xml标签追加标签

<!--覆盖配置文件,接收更多请求-->
<constant name="struts.action.extension" value="do"></constant>

如果你想接收xx.do,xx.action,xx可以如下配置

<constant name="struts.action.extension" value="action,do,,"></constant>

第六点补充

如果请求为http://localhost/a/b/hello.action他也能够访问,原理为:

  • 请求到来时会去精准匹配是否包含/a/b的请求
  • 如果没有会去掉/b
  • 然后查找是否包含/a
  • 如果还是没有就找到/
  • 这时候就找到了就返回
  • 如果还没找到就会再脱一层
  • 这没有脱的了就会404

StrutsPrepareAndExecuteFilter的配置

只需要配置web.xml中的filter和filter-mapping即可

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <welcome-file-list>
        <welcome-file>tologin.do</welcome-file>
    </welcome-file-list>
    <filter>
        <filter-name>struts</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>
                struts-default.xml,
                struts-plugin.xml,
                ss.xml
            </param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>struts</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Action

Action走的是非单例的

编写规则

请求处理方法

对应请求处理,固定格式:

public String execute(){
//请求处理逻辑,比如调用Model组件
}
属性

可以接受请求参数、可以向响应jsp传值

拿值是通过属性的get方法,接收请求走的set方法

我们想通过Action传值直接在Action中声明对应属性以及get/set方法,例如

HelloAction.java

package com.xdl.action;

import com.xdl.bean.City;

import java.util.ArrayList;
import java.util.List;

/**
 * Class Describe:
 * <p>
 * 默认方法execute
 *
 * @author biuaxia
 * @date 2018/12/1
 * @time 10:39
 */
public class HelloAction {
    private String subTitle;
    private List<City> cities;

    public String getSubTitle() {
        return subTitle;
    }

    public void setSubTitle(String subTitle) {
        this.subTitle = subTitle;
    }

    public List<City> getCities() {
        return cities;
    }

    public void setCities(List<City> cities) {
        this.cities = cities;
    }

    /**
     * public String xx(){}
     *
     * @return
     */
    public String execute() {
        System.out.println("This is execute Methods");
        subTitle = "我是副标题";
        cities = new ArrayList<>();
        cities.add(new City(1, "北京"));
        cities.add(new City(2, "成都"));
        cities.add(new City(3, "上海"));
        cities.add(new City(4, "深圳"));
        /**
         * Spring返回的是视图名
         * 这里返回的是result标识名
         * 对应<result name="xxx"/>
         */
        return "success";
    }
}

hello.jsp(接收传值),这里面分别用了jstl的标签库和Struts2对的标签库

<%--
  Created by IntelliJ IDEA.
  User: biuaxia
  Date: 2018/12/1
  Time: 10:19
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Hello</title>
</head>
<body>
<h1>Hello Struts2</h1>
<h2>${subTitle}</h2>
<h3>list对象:</h3><s>${cities}</s>
<h3>使用原生jstl标签库</h3>
<select>
    <c:forEach items="${cities}" var="city">
        <option value="${city.no}">${city.cityName}</option>
    </c:forEach>
</select>
<h3>使用Struts2标签库</h3>
<s:select name="city" list="cities" listValue="cityName" listKey="no"></s:select>
</body>
</html>
运行效果

配置规则

页面跳转功能

tologin.do -> StrutsPrepareAndExecuteFilter -> ActionSupport -> Result(dispatcher重定向) -> /WEB-INF/login.jsp

<package>
    <!--正常定义-->
	<action name="请求名" class="" method="方法名"></action>
    
    <!--特殊定义,这里面我们看不到method属性和class属性,默认调用ActionSupport-->
    <!--发出请求调用默认的ActionSupport类的execute处理,返回success-->
    <action name="请求名">
        <result name="success" type="dispatcher">/WEB-INF/login.jsp</result>
    </action>
</package>
<!--最简配置-->
<package name="demo2" extends="struts-default" namespace="//">
    <action name="tologin">
        <!--name默认为success,type默认为dispatcher-->
        <result>/WEB-INF/login.jsp</result>
    </action>
</package>

而ActionSupport类大概内容如下:

public class ActionSupport{
    public String execute(){
        return "success";
    }
}

他进来就是做跳转的处理,返回success,我们只需要配置对应的action标签内的内容即可

登录处理

login.do -> StrutsPrepareAndExecuteFilter(filter控制器) -> LoginAction -> Result ->成功 -> /WEB-INF/ok.jsp or 失败 -> /WEB-INF/login.jsp

这里一个成功一个失败怎么办呢,定义两个result即可

ok.jsp

<%--
  Created by IntelliJ IDEA.
  User: biuaxia
  Date: 2018/12/1
  Time: 15:47
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆成功</title>
</head>
<body>
<h1>登陆成功</h1>
<h2>用户名:${name}</h2>
<h3>这是从request域拿出的数据msg: ${requestScope.msg}</h3>
</body>
</html>

LoginAction.java

package com.xdl.action;

import com.opensymphony.xwork2.ActionContext;

import java.util.Map;

/**
 * Class Describe:
 *
 * @author biuaxia
 * @date 2018/12/1
 * @time 15:47
 */
public class LoginAction {

    /**
     * 为了接收name和pwd而定义
     * 别忘了get/set方法
     */
    private String name;
    private String pwd;

    /**
     * 添加输出信息
     */
    private String error;

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String execute() {
        //检查用户名密码正确性
        String CORRECT_NAME = "root";
        String CORRECT_PWD = "1234";
        System.out.println(name);
        System.out.println(pwd);
        if (CORRECT_NAME.equalsIgnoreCase(name) && CORRECT_PWD.equals(pwd)) {
            //正确
            //将信息存入session
            ActionContext ac = ActionContext.getContext();
            Map<String, Object> session = ac.getSession();
            System.out.println(session.getClass().getName());
            session.put("name", name);
            session.put("pwd", pwd);

            //获得request
            Map requests = (Map) ac.get("request");
            System.out.println(requests.getClass().getName());
            requests.put("msg", "登录成功");
            return "success";
        }
        //失败
        error = "用户名或密码错误";
        return "error";
    }
}

map的应用

其实我们上面登录处理调用的requests和session都是map结构,为啥不是原本的HttpServletRequest和HttpSession呢?是因为他们继承了AbstractMap

这个抽象map大概是这样的结构

public class SessionMap extends AbstractMap {
    /**
     * 准备session对象
     */
    private HttpSession httpSession;

    /**
     * 通过构造器刚给httpSession赋值
     *
     * @param request
     */
    public SessionMap(HttpServletRequest request) {
        httpSession = request.getSession();
    }

    /**
     * 重写put方法
     *
     * @param key
     * @param value
     */
    public void put(Object key, Object value) {
        httpSession.setAttribute(key.toString(), value);
    }

    /**
     * 重写get方法
     *
     * @param key
     * @return
     */
    public Object get(Object key) {
        return httpSession.getAttribute(key.toString());
    }
}

Map session = new SessionMap(request)

SessionMap、RequestMap、ApplicationMap等都是这样设计的,在设计模式中这种模式叫做适配器模式

三种使用方法

  1. 通过ActionContext对象获取对应的容器Map,比如session:Map<String, Object> session = ActionContext.getContext().getSession();
  2. 使用ServletActionContext对象的get方法获得对应对象,比如request:ServletActionContext.getRequest();得到request对象
  3. 通过Aware接口获取对应对象,使用set注入对应的对象

下面的代码实现了三种使用方法,前两种做了注释,最后一种放开了注释

LoginAction.java

package com.xdl.action;

import org.apache.struts2.interceptor.SessionAware;

import java.util.Map;

/**
 * Class Describe:
 *
 * @author biuaxia
 * @date 2018/12/1
 * @time 15:47
 */
public class LoginAction implements SessionAware {

    /**
     * 为了接收name和pwd而定义
     * 别忘了get/set方法
     */
    private String name;
    private String pwd;

    /**
     * 添加输出信息
     */
    private String error;

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String execute() {
        //检查用户名密码正确性
        String CORRECT_NAME = "root";
        String CORRECT_PWD = "1234";
        System.out.println(name);
        System.out.println(pwd);
        if (CORRECT_NAME.equalsIgnoreCase(name) && CORRECT_PWD.equals(pwd)) {
            //正确
            //将信息存入session
            /**
             * 方法一,使用map对象操作
             ActionContext ac = ActionContext.getContext()
             Map<String, Object> session = ac.getSession()
             System.out.println(session.getClass().getName())
             session.put("name", name)
             session.put("pwd", pwd)
             */
            /**
             * 方法二,使用Servlet对象操作
             * 通过ServletActionContext.getRequest()方法获得request对象
             */
            /**
             HttpServletRequest request = ServletActionContext.getRequest()
             HttpSession session = request.getSession()
             session.setAttribute("name", name)
             session.setAttribute("pwd", pwd)

             request.setAttribute("msg", "登录成功")
             */

            //获得request
            /**
             * 也是方法一
             Map requests = (Map) ac.get("request")
             System.out.println(requests.getClass().getName())
             requests.put("msg", "登录成功")
             */

            /**
             * 方法三,和Spring注入方式差不多
             * 通过注入来使用
             * 由框架底层把需要的对象传给Action,需要实现特定的接收,请看类名和最下面
             * 使用Aware接口方法注入session的操作
             */
            session.put("name", name);
            session.put("pwd", pwd);
            return "success";
        }
        //失败
        error = "用户名或密码错误";
        return "error";
    }

    private Map<String, Object> session;

    /**
     * 创建Action对象后自动执行一次
     *
     * @param session
     */
    @Override
    public void setSession(Map<String, Object> session) {
        this.session = session;
    }
}

使用建议

Action对象使用

如果是Action对象使用,建议采用Aware接口注入,耦合度低,但是这样做会每次做一个set,在项目中我们会不用每个Action实现Aware,我们可以实现一个BaseAction继承SessionAware并实现setSession即可[注意该类参数和方法的修饰符]

BaseAction.java

package com.xdl.action;

import org.apache.struts2.interceptor.SessionAware;

import java.util.Map;

/**
 * Class Describe:
 *
 * @author biuaxia
 * @date 2018/12/2
 * @time 21:57
 */
public class BaseAction implements SessionAware {

    protected Map<String, Object> session;

    @Override
    public void setSession(Map<String, Object> map) {
        this.session = map;
    }

}

LoginAction.java

package com.xdl.action;

import org.apache.struts2.interceptor.SessionAware;

import java.util.Map;

/**
 * Class Describe:
 *
 * @author biuaxia
 * @date 2018/12/1
 * @time 15:47
 */
public class LoginAction extends BaseAction {

    /**
     * 为了接收name和pwd而定义
     * 别忘了get/set方法
     */
    private String name;
    private String pwd;

    /**
     * 添加输出信息
     */
    private String error;

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String execute() {
        //检查用户名密码正确性
        String CORRECT_NAME = "root";
        String CORRECT_PWD = "1234";
        System.out.println(name);
        System.out.println(pwd);
        if (CORRECT_NAME.equalsIgnoreCase(name) && CORRECT_PWD.equals(pwd)) {
            //正确
            //将信息存入session
            /**
             * 方法一,使用map对象操作
             ActionContext ac = ActionContext.getContext()
             Map<String, Object> session = ac.getSession()
             System.out.println(session.getClass().getName())
             session.put("name", name)
             session.put("pwd", pwd)
             */
            /**
             * 方法二,使用Servlet对象操作
             * 通过ServletActionContext.getRequest()方法获得request对象
             */
            /**
             HttpServletRequest request = ServletActionContext.getRequest()
             HttpSession session = request.getSession()
             session.setAttribute("name", name)
             session.setAttribute("pwd", pwd)

             request.setAttribute("msg", "登录成功")
             */

            //获得request
            /**
             * 也是方法一
             Map requests = (Map) ac.get("request")
             System.out.println(requests.getClass().getName())
             requests.put("msg", "登录成功")
             */

            /**
             * 方法三,和Spring注入方式差不多
             * 通过注入来使用
             * 由框架底层把需要的对象传给Action,需要实现特定的接收,请看类名和最下面
             * 使用Aware接口方法注入session的操作
             */
            session.put("name", name);
            session.put("pwd", pwd);
            return "success";
        }
        //失败
        error = "用户名或密码错误";
        return "error";
    }

    /*
    这里和下面注释的setSession方法都是第三种方法 - 使用Aware接口
    private Map<String, Object> session;
    */

    /**
     * 创建Action对象后自动执行一次
     *
     * @param session
     */
   /* @Override
    public void setSession(Map<String, Object> session) {
        this.session = session;
    }*/
}
返回类型不同的接口

Map类型的接口:

RequestAware,SessionAware,ApplicationAware

Servlet类型的接口:

ServletRequestAware,ServletResponseAware,ServletContextAware

使用例子

我们比如想要使用HttpServletRequest对象,那么就可以使BaseAction对象继承ServletRequestAware接口,然后放置一个request对象,LoginAction就可以使用了

非Action对象使用

只能用ActionContext[Map]或者ServletActionContext[Servlet]

比如想在拦截器拿到对象,可以使用上面两个来获取或者存储值,但是根据map或者servlet来自行判断。

例如想要获得上传路径,就得调用request获取真实路径

如果只是用来存值取值就用map,特殊用途使用Servlet吧

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值