文章目录
Struts2入门及组件介绍
配置流程
-
配置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> -
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方法 -
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 | 描述 |
|---|---|---|
| chain | com.opensymphony.xwork2.ActionChainResult | |
| dispatcher | org.apache.struts2.dispatcher.ServletDispatcherResult | 转发 |
| freemarker | org.apache.struts2.views.freemarker.FreemarkerResult | |
| httpheader | org.apache.struts2.dispatcher.HttpHeaderResult | |
| redirect | org.apache.struts2.dispatcher.ServletRedirectResult | 重定向 |
| redirectAction | org.apache.struts2.dispatcher.ServletActionRedirectResult | |
| stream | org.apache.struts2.dispatcher.StreamResult | |
| velocity | org.apache.struts2.dispatcher.VelocityResult | |
| xslt | org.apache.struts2.views.xslt.XSLTResult | |
| plainText | org.apache.struts2.dispatcher.PlainTextResult | |
| postback | org.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
它有以下责任:
- 负责接收用户请求[一个请求创建一个Action,在Spring中是单例的,而Struts则是非单例的]
- 负责调用Action中请求处理方法
- 负责根据Action返回值调用Result处理
- 主控制器会自动创建Action对象和Result对象
- 默认情况下,主控制器
StrtusPrepareAndExecuteFilter只处理*.action或者没有扩展名的请求,只有符合请求才能进入Action的调用[进过测试,就算直接修改web.xml的url-pattern为*.do也是404,因为主控制器本身就自带过滤功能]因此一个请求想要进入服务,就得经过双重过滤,一层web.xml,一层主控制器,能进入的只有.action和不带后缀的 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等都是这样设计的,在设计模式中这种模式叫做适配器模式
三种使用方法
- 通过ActionContext对象获取对应的容器Map,比如session:
Map<String, Object> session = ActionContext.getContext().getSession(); - 使用ServletActionContext对象的get方法获得对应对象,比如request:
ServletActionContext.getRequest();得到request对象 - 通过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吧
本文深入解析Struts2框架的配置流程,主控制器StrutsPrepareAndExecuteFilter的作用,Action的编写规则与请求处理方法,以及Result配置。涵盖Action与Result的生命周期,请求处理流程,页面跳转功能,登录处理,和不同返回类型接口的使用建议。
389

被折叠的 条评论
为什么被折叠?



