Struts框架图解

image.image (24)

Struts核心

  • 操作(Actions)
  • 拦截器(Interceptors)
  • 值栈(Value Stack)/OGNL
  • 结果(Result)/结果类型
  • 视图技术

image.image (1)

导入依赖

<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.5.26</version>
</dependency>

<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>2.5.20</version>
</dependency>

Maven resources下新建配置struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.devMode" value="true"/>
<package name="helloworld" extends="struts-default" namespace="/">
</package>
</struts>

image.image (2)

配置web.xml

<?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">
<display-name>Archetype Created Web Application</display-name>
<filter>
<filter-name>action2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<!-- END SNIPPET: filter -->
<filter-mapping>
<filter-name>action2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

主要添加内容:

<filter>
<filter-name>action2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<!-- END SNIPPET: filter -->
<filter-mapping>
<filter-name>action2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

image

创建Action类 默认方法名execute()(类似Servlet)

package com.xianxian;
/**
* @author 羡羡
*
* Action 类似于servlet
*/
public class Action {
/**
* 这个方法不能乱写
* @return
*/
public String execute(){
System.out.println("第一个action:"+this);
return "success";
}
}

新建视图层jsp

<%--
Created by IntelliJ IDEA.
User: 羡羡
Date: 2021/7/19
Time: 9:34
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>视图层</h1>
</body>
</html>

配置struts.xml 跳转jsp(result)

<!--clss:类的地方 name:访问路径 method:默认为execute 右键copy  copy References  容器内转发-->
<!--配置类 name映射路径 通过类返回的结果进行跳转-->
<action name="hello" class="com.xianxian.Action" method="execute">
<!--jsp 执行完后的视图-->
<result name="success">/HelloStruts.jsp</result>
</action>

修改Action方法名

image

修改Action方法名配置

修改配置的method class=action类的路径(不是绝对路径 copy References)

image

Action得到表单数据

定义表单name对应变量 设置变量对应的set方法

image

Action类返回值

默认success 通过返回的结果进行对应的网页跳转

image

按返回值执行跳转

(result里面写对应的跳转地址) name=返回的结果

image

namespace

命名空间 ,相等于servlet的链接注解 进行模块跳转(如下:跳login 变成 /admin/login)

<package name="helloworld" extends="struts-default" namespace="/admin">
<!--配置类 name映射路径 通过类返回的结果进行跳转-->
<action name="login" class="com.action.Action">
<result name="success">/logsug.jsp</result>
<result name="error">/logeror.jsp</result>
</action>
</package>

Action继承ActionSupport

(返回值则是内置 大写)

image

重新execute()方法

@Override
public String execute() throws Exception {
System.out.println("其他的地址!");
return ERROR;
}

全局跳转

两个Action有跳转的地方相同 如下图

<!--全局跳转(多个Action跳转的同一位置)-->
<global-results>
<result name="error">/NoLogin.jsp</result>
</global-results>

image

如上相同地址则可使用global 示例如下

image

action不做控制器 做跳转

<!--没有Class 不做控制器 做网页跳转  通过name进行跳转-->
<action name="tjiao" >
<result>/WEB-INF/hide.jsp</result>
</action>

Action通配符

一个Action执行多件事情

添加运行通配符

<!--运行通配符-->
<global-allowed-methods>regex:.*</global-allowed-methods>
package com.xianxian;
import com.opensymphony.xwork2.ActionSupport;
/**
* @author 羡羡
*
* Action做多件事
*/
public class UserAction extends ActionSupport {
public String add(){
System.out.println("用户增加");
return SUCCESS;
}
public String dele(){
System.out.println("用户删除");
return SUCCESS;
}
public String update(){
System.out.println("用户修改");
return SUCCESS;
}
public String search(){
System.out.println("用户查询");
return SUCCESS;
}
}

通配符配置

<!--一个action做多件事 通配符方法 访问:useradd...   method="{1}"第一个*当方法名 -->
<action name="user*" class="com.xianxian.UserAction" method="{1}">
<result>/WEB-INF/hide.jsp</result>
</action>

多个通配符配置

与单个通配符类似(多个Action类)

<!--两个*匹配 两个action类(AdminAction、UserAction) 访问Admin_add(类名_类下方法)-->
<action name="*_*" class="com.xianxian.{1}Action" method="{2}">
<result>/WEB-INF/hide.jsp</result>
</action>

默认容器内转发

<action name="tjiao" >
<result >/WEB-INF/hide.jsp</result>
</action>

浏览器重定向

(type=“redirect”)(red ruai ke che)

<action name="tjiao" >
<result type="redirect">/WEB-INF/hide.jsp</result>
</action>

一个Action跳转到其他类Action

name:返回的值 hello跳转得Action name

UserAction跳 Action 图例

<!--一个action跳到其他action  hello是其他action-->
<result name="gohello" type="chain">hello</result>

image

image

全局异常处理

<!--全局异常处理-->
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="err" />
</global-exception-mappings>

<global-results>
<result name="error">/NoLogin.jsp</result>
<result name="err">/error.jsp</result>
</global-results>

image


Struts四大作用域

表单获取

可以用对象接收 需要有get set方法

定义表单实体类

package com.entity;
/**
* @author 羡羡
* 与表单name相同
*/
public class User {
public String name;
public String pass;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", pass='" + pass + '\'' +
'}';
}
}

表单 name 实体类.属性 (User us=new User() 在Action类new)

<html>
<body>
<h2>Hello World!</h2>
<form action="login" method="post">
<input type="text" name="us.name"/>
<input type="text" name="us.pass"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>

Action类(需要new实体类 get set方法)

User us=new User();
public User getUs() {
return us;
}
public void setUs(User us) {
this.us = us;
}

获取原生四大作用域

//获取原生的request
HttpServletRequest request=ServletActionContext.getRequest();
//获取session
HttpSession session=request.getSession();
//application
ServletContext se=ServletActionContext.getServletContext();
//获取response
HttpServletResponse response=ServletActionContext.getResponse();

Struts框架获取四大作用域

方法1、

ActionContext ac=ActionContext.getContext();
ac.put("na","struts -- request");//获取request
ac.getSession().put("sa","struts -- session");//获取session
ac.getApplication().put("se","struts -- application");//获取application

方法2、
实现三个接口

implements RequestAware, SessionAware, ApplicationAware

新建三个map

Map<String,Object> request;//request
Map<String,Object> session;//response
Map<String,Object> app;//application

重写方法 (this.新建map的变量名)

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

使用:

public String execute(){
request.put("na","struts -- request");
session.put("sa","struts -- session");
app.put("se","struts -- application");
return "success";
}

image

Action返回JSON

导入依赖

<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>2.5.20</version>
</dependency>

配置 json struts.xml (package 继承 json-default )

<!--配置返回json    result:Action定义什么param里面就写Action定义返回的参数-->
<action name="ajaxjson" class="com.xianxian.ReturnJsonAction">
<result type="json">
<!--过滤掉空 如果等会的数据为空值则不会返回那个参数-->
<param name="excludeNullProperties">true</param>
<param name="root">result</param>
</result>
</action>

image

定义通用返回类

package com.entity;
/**
* @author 羡羡
*
* json通用返回值
*
*/
public class JsonResult {
int code;//状态码
Object data;//返回的内容
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public JsonResult() {
}
public JsonResult(int code, Object data) {
this.code = code;
this.data = data;
}
}

返回jsonAction

package com.xianxian;
import com.entity.JsonResult;
import com.opensymphony.xwork2.ActionSupport;
import java.util.ArrayList;
import java.util.List;
/**
* @author 羡羡
*
* Action返回JSON
*/
public class ReturnJsonAction extends ActionSupport {
/**
* 只要返回JSON 格式要统一
*/
JsonResult result;
public JsonResult getResult() {
return result;
}
public void setResult(JsonResult result) {
this.result = result;
}
@Override
public String execute() throws Exception {
List lis=new ArrayList();
lis.add("C++");
lis.add("Java");
lis.add("C#");
lis.add("Python");
result =new JsonResult(200,lis);
//返回错误消息
/*result =new JsonResult(500,"返回失败!");*/
return SUCCESS;
}
}

返回成功 result=new JsonResult(200,lis);

image

返回失败 result=new JsonResult(200,“返回失败!”);

image

配置struts.xml 添加过滤空配置

image

image

Action文件上传

界面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="myFile"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>

struts.xml配置

<!--文件上传配置-->
<action name="upload" class="com.xianxian.FileAction">
<result name="success">/filsu.jsp</result>
</action>

文件上传Action

package com.xianxian;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;

/**
* @author 羡羡
*
* 文件上传Action
*/
public class FileAction extends ActionSupport {
private File myFile;//表单接收值 需要去表单文件上传框name属性相同
private String myFileContentType;//文件类型
private String myFileFileName;//文件名
private String destPath;//存放路径



public File getMyFile() {
return myFile;
}

public void setMyFile(File myFile) {
this.myFile = myFile;
}

public String getMyFileContentType() {
return myFileContentType;
}

public void setMyFileContentType(String myFileContentType) {
this.myFileContentType = myFileContentType;
}

public String getMyFileFileName() {
return myFileFileName;
}

public void setMyFileFileName(String myFileFileName) {
this.myFileFileName = myFileFileName;
}

public String getDestPath() {
return destPath;
}

public void setDestPath(String destPath) {
this.destPath = destPath;
}

@Override
public String execute() throws Exception {
destPath= ServletActionContext.getServletContext().getRealPath("/images");
System.out.println("Src File name: " + myFile);
System.out.println("Dst File name: " + myFileFileName);
File destFile = new File(destPath, myFileFileName);
FileUtils.copyFile(myFile, destFile);
/* HttpServletRequest request=ServletActionContext.getRequest();
HttpSession session=request.getSession();
session.setAttribute("img",FileName);*/
return SUCCESS;
}
}

限制文件的大小

<constant name="struts.multipart.maxSize" value="1000000"></constant>

image

Error:(83, 61) java: 无法访问javax.servlet.ServletContext 找不到javax.servle

image

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>7.0</version>
</dependency>

Action拦截器

拦截器(AOP编程)

  • 在调用action之前提供预处理逻辑。
  • 在调用action后提供后处理逻辑。
  • 捕获异常,以便可以执行备用处理。

Struts2框架拦截器

Struts 2框架提供了一个良好的开箱即用的拦截器列表,这些拦截器预先配置好并可以使用。 下面列出了几个重要的拦截器:

序号拦截器和说明
1alias允许参数在请求之间使用不同的别名。
2checkbox通过为未检查的复选框添加参数值false,以辅助管理复选框。
3conversionError将字符串转换为参数类型的错误信息放置到action的错误字段中。
4createSession自动创建HTTP会话(如果尚不存在)。
5debugging为开发人员提供一些不同的调试屏幕。
6execAndWait当action在后台执行时,将用户发送到中间的等待页面。
7exception映射从action到结果抛出的异常,允许通过重定向自动处理异常。
8fileUpload便于文件上传。
9i18n
在用户会话期间跟踪选定的区域。
10logger通过输出正在执行的action的名称提供简单的日志记录。
11params设置action上的请求参数。
12prepare这通常用于执行预处理工作,例如设置数据库连接。
13profile允许记录action的简单分析信息。
14scope在会话或应用程序范围内存储和检索action的状态。
15ServletConfig提供可访问各种基于servlet信息的action。
16timer以action执行时间的形式提供简单的分析信息。
17token检查action的有效性,以防止重复提交表单。
18validation提供action的验证支持。

默认拦截器

Params(没有设置Action则得不到表单的数据)

<!--设置action上的请求参数 让表单接收到参数-->
<interceptor-ref name="params"></interceptor-ref>

timer (显示表单提交需要多久 输出表单的提交时间 需要配置log4j.xml)

<!--timer:表单提交需要多久的信息 -->
<interceptor-ref name="timer"></interceptor-ref>

log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.opensymphony.xwork2" level="info"/>
<Logger name="org.apache.struts2" level="info"/>
<Logger name="org.demo.rest" level="debug"/>
<Root level="warn">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>

自定义拦截器

定义拦截器类(继承AbstractInterceptor 重新intercept方法)

package com.Interceptor;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

import java.util.Map;

/**
* @author 羡羡
*
* 第一个拦截器 (登入拦截器)
*/
public class LoginInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
/*检查有没有session*/
ActionContext ac=ActionContext.getContext();
Map session=ac.getSession();
Object obj=session.get("logname");

if(obj==null){
return "nologin";
}else{
String ok=actionInvocation.invoke();
return ok;
}
}
}

invoke()

代码在invoke之前则是拦截之前的操作 在invoke之后 则是拦截Action后的操作

配置拦截器

注册拦截器 (name 拦截的name class为拦截器类)

<!--注册拦截器-->
<interceptors>
<!--class为拦截器类的位置-->
<interceptor name="logininterceptor" class="com.Interceptor.LoginInterceptor"></interceptor>
<interceptor name="Testlj" class="com.Interceptor.TestInterceptor"></interceptor>
<interceptor name="authlj" class="com.Interceptor.AuthInterceptor"></interceptor>
</interceptors>

使用拦截器 (name需要与注册拦截器的name相同)

<!--配置登入拦截 可以多个地方进行使用-->
<interceptor-ref name="logininterceptor"></interceptor-ref>

自定义拦截器时需要使用默认拦截器栈(否则会出现参数无法得到)

<!--默认拦截器栈-->
<interceptor-ref name="defaultStack"></interceptor-ref>

拦截器栈

当多个拦截器使用到同一地方时可以使用拦截器栈

拦截器栈配置

interceptor-stack:拦截器栈 name提供给使用拦截器的name

interceptor-ref: name自定义

<!--注册拦截器-->
<interceptors>
<!--拦截器栈-->
<interceptor-stack name="zalj">
<interceptor-ref name="logininterceptor"></interceptor-ref>
<interceptor-ref name="authlj"></interceptor-ref>
</interceptor-stack>
</interceptors>

使用拦截器栈 (name为拦截器栈的name值)

<!--拦截栈配置-->
<interceptor-ref name="zalj"></interceptor-ref>

Token使用

token 防止表单进行重复提交

界面顶部添加标签

<%@ taglib prefix="s" uri="/struts-tags" %>

界面 (form表单内必须加上<s:token /> )

<%@ taglib prefix="s" uri="/struts-tags" %>
<%--
Created by IntelliJ IDEA.
User: 羡羡
Date: 2021/7/25
Time: 14:04
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form method="post" action="fromsub">
<input type="text" name="name"/>
<input type="password" name="pass">
<s:token />
<button type="submit">提交</button>
</form>
</body>
</html>

Action(FormSubAction 接收表单提交的信息)

package com.xianxian;

import com.opensymphony.xwork2.ActionSupport;

/**
* @author 羡羡
*/
public class FormSubAction extends ActionSupport {

String name;
String pass;

public String getName() {
return name;
}

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

public String getPass() {
return pass;
}

public void setPass(String pass) {
this.pass = pass;
}

@Override
public String execute() throws Exception {
System.out.println("name:"+name+" "+"pass:"+pass);
return SUCCESS;
}
}

struts.xml 配置

<action name="fromsub" class="com.xianxian.FormSubAction">
//token拦截器
<interceptor-ref name="token"></interceptor-ref>
//默认拦截器
<interceptor-ref name="defaultStack"></interceptor-ref>
//提交后跳转位置
<result name="success">/index.jsp</result>
//重复提交后跳转的位置
<result name="invalid.token">/resub.jsp</result>
</action>

拦截器案例

首页 登入页 超链接 (未登入是跳到未登入界面 ,登入后才可以访问超链接)

界面

首页 (a 访问的链接是AdminAction里面的add方法)

<%--
Created by IntelliJ IDEA.
User: 羡羡
Date: 2021/7/24
Time: 10:39
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a href="adminadd">访问管理员add方法</a>
</body>
</html>

登入

<%--
Created by IntelliJ IDEA.
User: 羡羡
Date: 2021/7/24
Time: 10:39
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登入页</h1>
<form action="logac" method="post">
<input type="text" name="name"/>
<input type="text" name="pass"/>
<input type="submit" value="login"/>
</form>
</body>
</html>

未登入跳转界面

<%--
Created by IntelliJ IDEA.
User: 羡羡
Date: 2021/7/19
Time: 10:05
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登入失败 请确认账号密码!</h1>
</body>
</html>

定义表单Action (得到表单的数据)

package com.xianxian;

import com.entity.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;

/**
* @author 羡羡
* 完成登入
*/
public class LogAction extends ActionSupport{

String name;//账号
String pass;//密码

public String getName() {
return name;
}

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

public String getPass() {
return pass;
}

public void setPass(String pass) {
this.pass = pass;
}

@Override
public String execute() throws Exception {
System.out.println("正在登录!"+name+" "+pass);
//设置session
ActionContext ac=ActionContext.getContext();
Map session=ac.getSession();
session.put("logname",name);
return SUCCESS;
}
}

AdminAction 增删改查方法(首页a标签的链接是AdminAction的add方法)

package com.xianxian;

import com.opensymphony.xwork2.ActionSupport;

/**
* @author 羡羡
*/
public class AdminAction extends ActionSupport {
public String add(){
System.out.println("管理员增加");
return SUCCESS;
}

public String dele(){
System.out.println("管理员删除");
return SUCCESS;
}

public String update(){
System.out.println("管理员修改");
return SUCCESS;
}

public String search(){
System.out.println("管理员查询");
return SUCCESS;
}
}

自定义登入拦截器LoginInterceptor

package com.Interceptor;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

import java.util.Map;

/**
* @author 羡羡
*
* 第一个拦截器 (登入拦截器)
*/
public class LoginInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
/*检查有没有session*/
ActionContext ac=ActionContext.getContext();
Map session=ac.getSession();
Object obj=session.get("logname");

if(obj==null){
return "nologin";//跳到未登入的界面
}else{
String ok=actionInvocation.invoke();
return ok;//否则可以访问a标签
}
}
}

配置struts.xml(配置拦截器)
需要配运行通配符(20)

自定义拦截器配置 需要添加默认拦截器(name=“defaultStack”)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.devMode" value="true"/>
<package name="helloworld" extends="struts-default" namespace="/">
<!--LoginAction登入检查-->
<!--AdminAction 需要登录检查-->
<!--首页链接 adminad 没有登入去登录页面-->

<!--配置登入拦截器
class:为登入拦截类
-->
<interceptors>
<interceptor name="loglj" class="com.Interceptor.LoginInterceptor"></interceptor>
</interceptors>

<!--运行通配符-->
<global-allowed-methods>regex:.*</global-allowed-methods>

<!--配置LoginAction
没有被拦截则进入index.jsp
-->
<action name="logac" class="com.xianxian.LogAction">
<result>/index.jsp</result>
</action>

<!--配置通配符
method={1} 第一个*为方法
需要加入默认栈 否则出现数据丢失
-->
<action name="admin*" class="com.xianxian.AdminAction" method="{1}">
<!--加入默认栈 否则数据会丢失-->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!--配置登入拦截-->
<interceptor-ref name="loglj"></interceptor-ref>
<result>/index.jsp</result>
<result name="nologin">/NoLogin.jsp</result>
</action>

</package>
</struts>