STRUTS2笔记

  • 实现原理
    • struts2项目搭建
    • action概述
    • 在action中访问web资源
    • Servlet解耦的方式是怎样的呢?
    • 通过xxxAware接口获取web资源

实现原理

先放一张很俗的框架图:

是不是看着很复杂?其实简单的讲这个古老的框架将我们平时使用的springMVC中的controller使用过滤器filter来实现了。这样讲可能比较空洞,那我们不用框架如何实现对httpRequest的接收处理和控制呢?
下面我们在传统的web项目中,使用filter和JAVA反射机制来模拟一下struts2对用户请求的处理流程:


这是简单实例的项目结构:

这是我们要做到的效果:
可见这样的实现方式,难怪我们在项目中找不到controller,控制层逻辑写在过滤器中了。
过滤器代码:

 /***  拦截并处理action请求* @param req httpRequest* @param resp httpResponse* @param chain  FilterChain* @throws ServletException ServletException* @throws IOException IOException*/@Overridepublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {//获取http请求地址HttpServletRequest request= (HttpServletRequest) req;String servletPath=request.getServletPath();if(servletPath.equals("/save.action")){//获取参数Map<String, String[]> paramMap=request.getParameterMap();//由反射实例化PO并返回结果VOClass peopleClass = null;Object obj=null;try {peopleClass = Class.forName("po.People");obj= peopleClass.newInstance();//获得POJO所有属性Field[] fields=peopleClass.getDeclaredFields();for (Field field:fields){String filedName=field.getName();String[] targetVal=paramMap.get(filedName);//请求参数长度为1则 是key为sting value为string形式if(targetVal.length==1){//直接为VO属性赋值field.setAccessible(true);String filedType= field.getType().getSimpleName();//匹配属性类型if(filedType.equals("String")){field.set(obj,targetVal[0]);}//其他类型。。也是这么判断赋值的}}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();}//将VO加入request域request.setAttribute("people",obj);//最后将返回结果到前端request.getRequestDispatcher("/WEB-INF/view/input.jsp").forward(req,resp);}else if(servletPath.equals("/input.action")) {System.out.println("拦截 input.action");request.getRequestDispatcher("/WEB-INF/view/input.jsp").forward(req,resp);}chain.doFilter(req, resp);}

POJO代码:在structs中每一个POJO都可以作为一个action类,这个下篇文章再接着说

package po;public class People {private String name;private String  sex;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}@Overridepublic String toString() {return "people{" +"name='" + name + '\'' +", sex='" + sex + '\'' +'}';}
}

VIEW显示层 就是前端页面啦

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>input</title>
</head>
<body>
<form action="save.action">name:<input name="name" type="text" value="${people.name}">sex :<input name="sex" type="text" value="${people.sex}"><input type="submit">
</form>
<%=request%>:::<%=request.getAttribute("people")%>
</body>
</html>

那我们这么实现的方式对不对呢?下面搭建一个基于的struts2框架的项目看一看:

struts2项目搭建

:struts项目与传统的web项目相比,项目的结构并没有很多改变,就像加入了依赖一样。
如图:

和我们自己实现的相比:多了一堆jar包,和一个structs配置文件,少了我们自己用过滤器实现的控制器。
效果是相同的:


是不是很神奇?难道struts能无中生有么?都知道建国后不准成精,所以答案肯定是否定的!!!
那么,问题来了:How did it do that?
谜底当然从多出来的struts.xml文件中揭开啦!
下面贴上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><constant name="struts.enable.DynamicMethodInvocation" value="false" /><constant name="struts.devMode" value="true" />
<!--package:包,struts-default 这个包在struts-default.xml文件中定义的,里面定义了一堆通用的过滤器,和返回结果。name:必填属性,用于其他包引用当前包
extends:当前包继承哪个包,可以继承其中所有的配置,通常都继承struts-default
namespace 属性是可选的 类似于我们在springmvc中在controller类的类名上加了@requestMapping注解,该属性默认值为‘/’,所以我们在jsp的a标签中虽然写的是action="save.action" 但我们浏览器访问时依然要写为:/save.action --><package name="default" namespace="/at" extends="struts-default"><!--action:配置一个action: 一个struts2请求就是一个actionname:对应一个struts2请求的名字(或者对应一个servletPath,但去除‘/’和拓展名),不包含拓展名:就例如在jsp的a标签中虽然写的是action="save.action" 那么我们在<action>标签中的name属性就写为saveclass:应写为我们自己定义的action类 默认值为:com.opensymphony.xwork2.ActionSupportmethod:我们定义的action类中在在本次请求中要执行的方法 默认值为execute--><!--result:结果,表示action方法执行后可能返回的一个结果,所以一个action可能对应多个result节点即可以根据返回值返回到不同的页面。返回到不同页面的依据是result标签的name属性name:标识result节点type:表示结果的类型,默认值为dispatcher(将请求转发到当前result对应的页面)--><action name="input" class="com.opensymphony.xwork2.ActionSupport"><result>/WEB-INF/view/input.jsp</result></action><action name="save" class="po.People" method="save"><result name="people">/WEB-INF/view/input.jsp</result></action></package>
</struts>

这么直接看是不是有点难懂?那来拿这个配置文件和我们之前写的控制器做一个对比:


这样比较一下是不是很直观的明白了他们在代码中的作用了呢?哈哈没错就是配置框架写好的过滤器也就是控制器。告诉框架什么action请求对应什么action类,执行什么方法,返回结果在哪展示。
此时还有一个问题没有回答,也就是即使是我们自己写的控制器将vo返回到jsp时我们获取属性值还需要people.name这样,而struts2缺可以直接${name} 这是怎么回事呢?
这个问题我们先来玩个游戏,名字叫找不同:请看图:

请问这两张图有什么不一样的?
对对对!很聪明,获取到的request对象不一样!这就是这个问题答案的秘密所在,struts2的StrutsRequestWrapper类继承并重写了httpRequest的getAttribute(String key)方法:
我们看一下源码:

package org.apache.struts2.dispatcher;import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.ValueStack;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.lang3.BooleanUtils;public class StrutsRequestWrapper extends HttpServletRequestWrapper {private static final String REQUEST_WRAPPER_GET_ATTRIBUTE = "__requestWrapper.getAttribute";private final boolean disableRequestAttributeValueStackLookup;public StrutsRequestWrapper(HttpServletRequest req) {this(req, false);}public StrutsRequestWrapper(HttpServletRequest req, boolean disableRequestAttributeValueStackLookup) {super(req);this.disableRequestAttributeValueStackLookup = disableRequestAttributeValueStackLookup;}public Object getAttribute(String key) {if (key == null) {throw new NullPointerException("You must specify a key value");} else if (!this.disableRequestAttributeValueStackLookup && !key.startsWith("javax.servlet")) {ActionContext ctx = ActionContext.getContext();Object attribute = super.getAttribute(key);if (ctx != null && attribute == null) {boolean alreadyIn = BooleanUtils.isTrue((Boolean)ctx.get("__requestWrapper.getAttribute"));if (!alreadyIn && !key.contains("#")) {try {ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);ValueStack stack = ctx.getValueStack();if (stack != null) {attribute = stack.findValue(key);}} finally {ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);}}}return attribute;} else {return super.getAttribute(key);}}
}

重点在这里:

我们要获取的参数值是由struts2的值栈(valueStack)提供的关于栈值之后会介绍到

action概述

1.action:
action表示一个action请求

2.action类:
能够处理action请求的类:就比如我们上面写的people这个类,
》action类的属性名称必须遵守与javabean属性名相同的命名规则。
属性的类型必须可以是任意类型,从字符串到非字符串(基本数据类型)之间的数据转换可以自动发生。
》必须有一个不带参的构造器:struts是通过反射创建实例

》至少提供一个struts在执行这个action时调用的方法。

》同一个action类可以包含多个action方法。
struts2会为每一个http请求创建一个新的action实例,线程安全的。
(类比spring mvc也会为每一个http请求创建一个新的controller实例)

在action中访问web资源

1.什么是web资源?
HttpServletRequest ,HttpSession,ServletContext 等原生的Servlet API
2.为什么访问web资源?
B/S的应用的Controller中必然要访问web资源:向域中写属性,读写cookie,获取realPath…
3.如何访问?
1.Servlet API解耦的方式:只访问有限的Servlet API对象,且只能访问有限的方法(读取请求参数,读写域只对象的属性,使用session)

1.使用ActionContext
2.实现XxxAware接口

**

Servlet解耦的方式是怎样的呢?

**原来为了避免与ServletAPI耦合在一起,方便Action做单元测试,Struts2对HttpServletRequest , HttpSession和ServletContext进行了封装,构造了3个Map对象来代替这三个对象,以便在Action中可以直接使用HttpServletRequest,HttpServletSession,ServletContex对应的Map对象来保存和读取数据。而不是直接使用原生的Servlet对象。
我们用一个实例来说明:这三个map的使用:
1.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><constant name="struts.enable.DynamicMethodInvocation" value="false" /><constant name="struts.devMode" value="true" />
<!--package:包,struts-default 这个包在struts-default.xml文件中定义的,里面定义了一堆通用的过滤器,和返回结果。name:必填属性,用于其他包引用当前包extends:当前包继承哪个包,可以继承其中所有的配置,通常都继承struts-defaultnamespace 属性是可选的 类似于我们在springmvc中在controller类的类名上加了@requestMapping注解,该属性默认值为‘/’,所以我们在jsp的a标签中虽然写的是action="save.action" 但我们浏览器访问时依然要写为:/save.action--><package name="default"  extends="struts-default"><!--action:配置一个action: 一个struts2请求就是一个actionname:对应一个struts2请求的名字(或者对应一个servletPath,但去除‘/’和拓展名),不包含拓展名:就例如在jsp的a标签中虽然写的是action="save.action" 那么我们在<action>标签中的name属性就写为saveclass:应写为我们自己定义的action类 默认值为:com.opensymphony.xwork2.ActionSupportmethod:我们定义的action类中在在本次请求中要执行的方法 默认值为execute--><!--result:结果,表示action方法执行后可能返回的一个结果,所以一个action可能对应多个result节点即可以根据返回值返回到不同的页面。返回到不同页面的依据是result标签的name属性name:标识result节点type:表示结果的类型,默认值为dispatcher(将请求转发到当前result对应的页面)--><action name="input" class="com.opensymphony.xwork2.ActionSupport"><result>/WEB-INF/view/action-context.jsp</result></action><action name="actionContext" class="test.action.context.TestActionContext" method="execute"><result name="success">/WEB-INF/view/action-context.jsp</result></action></package></struts>

jsp内容:
index.jsp

<%@ page import="java.util.Date" %><%--Created by IntelliJ IDEA.User: zhangyx-vDate: 2020-9-30Time: 16:42To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><a href="actionContext.action?name=123">actionContext</a><%if (application.getAttribute("date")==null)application.setAttribute("date",new Date());%></body>
</html>

action.jsp

  Created by IntelliJ IDEA.User: zhangyx-vDate: 2020-9-30Time: 16:50To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>action-context</title></head>
<body>
applicationKey:${applicationScope.applicationKey}
<br>
<span>session:${sessionScope.sessionKey}</span>
<br>
<span>request:${requestScope.requestKey}</span></body></html>

action类:

package test.action.context;import com.opensymphony.xwork2.ActionContext;import java.util.Map;/*** @author zhangyx-v*/
public class TestActionContext {public String execute(){//0.获取actionContext对象ActionContext actionContext=ActionContext.getContext();//1.ActionContext是Action的上下文,类似于springMVC中的ApplicationContext,可以从中获取Action需要的一切信息//获取application(域对象) 对应的mapMap<String,Object>applicationMap=actionContext.getApplication();//向map中存入一属性 ?这个过程就相当于request.setAttribute(key,val)applicationMap.put("applicationKey","applicationValqqq");//获取属性request.getAttribute(key,val)Object date=applicationMap.get("date");System.out.println("date:"+date);//2.sessionMap<String,Object> sessionMap=actionContext.getSession();sessionMap.put("sessionKey","sessionValue");//3.request//ActionContext中没有提供getRequest方法来获取request对应的Map//需要手工调用get()方法,传入request字符串来获取Map<String,Object> requestMap= (Map<String, Object>) actionContext.get("request");requestMap.put("requestKey","requestVal");//4.获取请求参数对应的Map,并获取指定的参数值Map<String,Object> parameters=actionContext.getParameters();//参数map的键为参数名称,值都为字符串数组//注意:1.getParameters的返回值为Map<String,Object>,而不是Map<String,String[]>,所以下面需要强转//2.parameter这个map只能读不能写,如果写入不会报错,只是无法获取到值System.out.println("request:"+ ((String[])parameters.get("name"))[0]);return "success";}
}

数据前后端传递流程:

总结:
其实这部分只是要求记住structs对ServletContext,HttpRequest,HttpSession以及请求参数封装的(Map<String, Object>)集合的获取和使用方式:
这四个map都是从上下文中获得的,所以我们必须先使用*ActionContext.getContext() *;获取上下文,然后由上下文获取相应的map集合。
1.ServletContext对应的map获取:
actionContext.getApplication();
2.HttpSession对应的map获取
actionContext.getSession();
3.HttpRequest对应的map获取:
(Map<String, Object>)actionContext.get(“request”);
这三个也是我们常用来从后端向前端传递数据的方式。在struts2里面我们就像上面例子一样先把map获取到,然后使用map的put方法将数据存入map,前端便可获取。
前端获取值的方式:
1.ServletContext对应的map:
${applicationScope.存入map中的key}
2.HttpSession对应的map:
${sessionScope.存入map中的key}
3.HttpRequest对应的map:
${requestScope.存入map中的key}

请求参数对应的map使用:
这个map是用来封装前端请求的参数的所以是不能在后端存入值的:
1.获取请求参数对应的(Map<String, Object>)集合:
actionContext.getParameters();
2.因为map是<String,Object>类型的,但请求参数都是一个字符串数组类型的,所以通过参数名字获取指定参数后需要强转为string[]类型然后再参数的值:

通过xxxAware接口获取web资源

*通过xxxAware接口就是通过实现ApplicationAware, SessionAware, RequestAware, ParameterAware接口然后通过实现接口的setApplication,setParameters,setRequest,setSession方法来获取ServletContext,HttpSession和HttpRequest以及请求参数对应的map集合,然后传参:
具体过程如下action类:

package test.action.context;import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.ParameterAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;import java.util.Date;
import java.util.Map;/*** 以实现applicationAware皆苦为例子来说明在struts2框架中* 通过实现实现XxxAware接口的方式来获取web资源的过程* @author MOON*/
public class TestAwareAction implements ApplicationAware, SessionAware, RequestAware, ParameterAware {/*** @see #applicationMap 通过实现ApplicationAware接口的setApplication方法注入*是ServletContext对应的map集合*/private Map<String,Object> applicationMap;/*** @see #sessionMap 通过实现SessionAware接口的setSession方法注入*是HttpSession对应的map集合*/private Map<String,Object> sessionMap;/*** @see #requestMap 通过实现RequestAware接口的setRequest方法注入*是HttpRequst对应的map集合*/private Map<String,Object> requestMap;/*** @see #parameterMap 通过实现ParameterAware接口的setParameters方法注入*是请求参数对应的map集合*/private Map<String,String[]> parameterMap;/****是testAware action中药执行的方法**/public String execute(){//向application中存入一个属性key为applicationKey2的属性applicationMap.put("applicationKey2","applicationKey2Val");//从application中读取一个key为date的属性Date applicationDate= (Date) applicationMap.get("date");System.out.println("application中属性date" + applicationDate);//向sessionMap中存入一个key为sessionKey2的属性sessionMap.put("sessionKey2","sessionKey2Val");//向requestMap中存入一个key为requestKey2的属性requestMap.put("requestKey2","requestKey2Val");//从parameterMap中获取一个key为getName的参数String[] name=parameterMap.get("getName");for (int i = 0; i < name.length; i++) {System.out.println("name参数"+i+"\t"+name[i]);}return "success";}/*** applicationMap的注入方法* @param map applicationMap*/@Overridepublic void setApplication(Map<String, Object> map) {this.applicationMap=map;}/*** parameter map的注入方法* @param map parameter map*/@Overridepublic void setParameters(Map<String, String[]> map) {this.parameterMap=map;}/*** requestMap的注入方法* @param map requestMap*/@Overridepublic void setRequest(Map<String, Object> map) {this.requestMap=map;}/*** sessionMap的注入法昂发* @param map sessionMap*/@Overridepublic void setSession(Map<String, Object> map) {this.sessionMap=map;}
}

具体执行过程如图:

是不是很简单,通过实现xxxAware就可以自动的注入获取相应的map集合到我们定义的Map类型的成员变量上然后供方法调用,能实现和通过ActionContext获取一样的功能。
建议:若定义的action类中有多个action方法并且都需要使用ServletContext或HttpSession或HttpRequest以及请求参数对应的map集合,建议使用这种方式获取相应的map.
另外提一点就是,HttpSession对应的map集合其实是SessionMap类型的,我们获取到sessionMap之后强转为SessionMap类型可以调用其中的invalidate()方法使session失效。

待续。。。。。

struts2框架学习相关推荐

  1. Struts2框架学习(二) Action

    Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器 ...

  2. Struts2框架学习笔记_Struts2入门

    文章为学习struts的笔记,可供读者参考,有不足之处还望之指出. 参考: POJO与JavaBean的区别 Velocity Velocity官网 FreeMarker XSLT struts2 学 ...

  3. 框架学习系列 之Struts2框架学习总结

    1 介绍 学习和使用Struts2框架有了一段时间,这里将学习心得总结在博客的文章里面.期间也参考学习了其他优秀的文章,如有错误之处欢迎指正,转载请申明原文地址,希望大家支持,谢谢. 2 What:什 ...

  4. Struts2框架学习总结(从入门到精通)

    文章目录 一.Struts2入门 struts2概述 struts2入门案例 struts2底层执行过程 struts2相关配置 struts2的核心配置文件struts.xml struts2常量配 ...

  5. Struts2框架学习Action命名空间创建方式

         Struts2框架中Action类时一个单独的javabean对象,相比struts1来说,不需要去继承任何类型或实现任何借口,表单数据包含在Action中,而Struts1则必须继承org ...

  6. Struts2框架--学习笔记(下):OGNL表达式、值栈操作、拦截器、struts2标签、文件上传

    一.OGNL概述:OGNL是一种表达式 (1)在struts2中操作值栈数据. (2)一般把ognl在struts2中操作,和struts2标签一起使用操作值栈. (3)ognl不是strut2的一部 ...

  7. Struts2框架--学习笔记(上):搭建struts2工程、struts2基本概念、struts2对页面数据的操作

    概述: Struts2框架应用于javaee三层框架中的web层.是在Struts1和webwork基础上发张的一个全新的框架. 一.搭建一个最基本的struts2工程步骤: 1.导入基本的jar包依 ...

  8. Struts2框架学习之一:Hello World程序

    前言 Struts2框架Apache基金组织下的一个开源框架,基于MVC模式设计的Web应用开发框架.Struts 2是一个用于开发Java EE网络应用程序的开源Web应用框架,它利用并扩展了Jav ...

  9. Java之struts2框架学习

    About Struts2 Struts也是一款MVC框架 , Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并的全新的Struts2框架 其全新的S ...

  10. Struts2框架学习---ONE

    通过阅读本你将掌握:                    struts2框架的配置                    入门程序的实现:                    配置文件的加载顺序: ...

最新文章

  1. 沉痛悼念游戏开发大神毛星云
  2. simple go web application 二维码生成 打包部署
  3. DL之VGG16:基于VGG16迁移技术实现猫狗分类识别(图片数据量调整→保存h5模型)
  4. 吴恩达深度学习笔记11-Course4-Week2【深度卷积网络:实例探究】
  5. nonzero的用法一则例子
  6. linux时间同步ntp服务的安装与配置
  7. swift中高阶函数map、flatMap、filter、reduce
  8. 【安卓笔记】是否执行测试服务
  9. 1067 Sort with Swap(0, i) (25 分) 好,容易出错
  10. BEA Tuxedo中间件应用初探
  11. linux连接苹果鼠标,Linux 5.13添加对苹果Magic Mouse 2和微软SAM的支持
  12. Validation进行参数校验
  13. python是开源的是什么意思_开源是啥意思
  14. 如何将已购kindle电子书转换成pdf格式阅读
  15. 李德毅院士:迭代的智能——从薛定谔、图灵和维纳谈开去
  16. QtScrcpy手机投屏电脑利器连接Android设备
  17. 你还不清楚某个系统文件的作用吗?Windows_xp系统文件详解【大全】
  18. 清除Vs2010的工作区影射关系的缓存信息的文件夹路径
  19. 【pyecharts50例】xy轴翻转(reversal_axis)
  20. (PMP)第9章-----项目资源管理

热门文章

  1. 小甲鱼python学习笔记1
  2. java类与对象实验报告心得体会_java实验报告心得体会
  3. GJB 150A军用设备环境试验方法低气压(高度)试验测试报告
  4. 计算机二级java和c哪个难,计算机二级最好考哪个 考试难度怎么样
  5. C/C++教程 第十七章 —— MFC开发多人聊天室
  6. Fortran代码在终端输出彩色文字
  7. Unicode 入门详解(V14.0版本)
  8. 云计算学习一——网络基础
  9. CC2640R2学习笔记—CCS环境最简单配置、下载运行第一个程序(OLED)
  10. 求素数的三种方法(Java实现)