Struts 2.3.24源码解析+Struts2拦截参数,处理请求,返回到前台过程详析
Struts2官网:http://struts.apache.org/
目前最新版本:Struts 2.3.24
Struts1已经完全被淘汰了,而Struts2是借鉴了webwork的设计理念而设计的基于MVC的框架。感兴趣的可以了解一下webwork概念,这里不做涉及。我们常说的Struts2 Spring Hibernate三大面试中常被问到的框架,其实Struts2和SpringMVC是两个独立的MVC框架。而且SpringMVC是目前最好的MVC框架(个人感觉),但是由于旧的一些项目运维需要Struts2框架的知识,故而做整理。
下载了Struts2框架之后,我们要学习什么?1.学习框架运作的整个流程;2.学习如何搭建框架;3.自己动手写一个。先来看看框架的流程,如下:
Struts2(由于Struts1表现层单一,无法跟Freemarker等技术整合),它采用拦截器的机制来处理用户的请求。
先来讲讲Struts2的原理图,如上图所示:
1.当用户发起请求时(一个URL),服务器端的Web容器收到了请求。
2.这时,Struts2的核心控制器FilterDispatcher接受用户发起的请求,然后判断这个请求是交给action处理?还是交给web组件来处理?如果请求的action或web组件不存在,则报404错误。在整个处理过程中,需要一个辅助对象:Action映射器(ActionMapper),ActionMapper会确定调用哪个Action(这个过程的实现是依靠ActionMapper返回一个收集Action详细信息的ActionMaping对象)
3.然后,来交给Action来处理,它会根据struts.xml的配置信息(首先执行拦截此action的所有拦截器,然后再执行请求的action对象<在这个处理过程中需要辅助对象:Action代理(ActionProxy);配置管理器(ConfigurationManager);ActionInvocation>,),
4.Action执行完毕之后,返回一个结果(此结果用字符串来表示),这个结果经过拦截Action的所有拦截器之后,返回给主控制器。主控制器根据此结果从配置文件中找到真正的路径,然后将请求转发给相应的视图。
5.由视图客户端作出响应。
那么接下来我们讲讲搭建框架,最好自己亲自搭建一遍或者跟着旧的项目把流程走一遍:
首先,将下载好的Struts2中的jar包拷贝到你构建的web project中,根据上图的设计流程,我们知道Struts2是通过过滤器,将所有的请求过滤,然后分发到各个action(action其实就是一个类,就是一个POJO类),然后根据返回的String字符串,查找Struts2的配置文件,找到应该返回到哪个页面,即可。具体如下:
拷贝jar包到项目中,
再来配置web.xml:
<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>
再来配置struts.xml配置文件(struts.xml要在src目录下)
<struts>
<package name="default" extends="struts-default" namespace="/">
</package>
</struts>
再来创建Action(它就是一个POJO类)
public class HelloAction{
public String execute(){
System.out.println("Hello Struts2!");
return "success";
}
}
再在struts.xml中配置action和返回结果集:
<struts>
<package name="default" extends="struts-default" namespace="/">
<action name="hello" class="com.hp.it.HelloAction">
<result name="success">/hello.jsp</result>
</action>
</package>
</struts>
注意,这里的<action>标签中的name属性要与url路径中的过滤到的string相对应;class属性要与你写的action类相对应(注意要有包名).<result>标签(result是结果集)中的那么属性要与action中返回的string字符串相对应,返回的路径是WEB-INF/下的hello.jsp,即http://localhost:8080/projectname/hello.action
再写前台的hello.jsp:
<html>
<head></head>
<body><h1>Hello</h1></body>
</html>
整个过程就是这样的,那么问题来了:当有多个请求的时候,要写很多execute()方法么?excute方法是默认的哦!
当然不是的,我们注意到,在struts.xml的配置文件中,标签<action>中除了name class 属性,还有一个method属性,很显然,这个标签可以指定我们映射到的方法。这个当然就是几个简单的扩张了。相应的struts.xml的配置文件:
<package name="default" extends="struts-default" namespace="/">
<action name="addUser" class="com.hp.it.UserAction" method="addUser">
<result name="success">/WEB-INF/user/addUser.jsp</result>
</action>
<action name="updateUser" class="com.hp.it.UserAction" method="updateUser">
<result name="success">/WEB-INF/user/updateUser.jsp</result>
</action>
</package>
相应的UserAction中如下:
public class UserAction{
public String addUser(){
System.out.println("addUser");
return "success";
}
public String updateUser(){
System.out.println("updateUser");
return "success";
}
}
那么问题又来了,一个方法写一个action,那么这样会导致配置文件中的action量很大。一种解决办法是可以在<package>标签的平行目录下,增加标签如下所示:通过增加<include>标签来导入其他的xml文件。
<struts>
<package name="default" extends="struts-default" namespace="/">
<action name="hello" class="com.hp.it.HelloAction">
<result name="success">/hello.jsp</result>
</action>
</package>
<include file="otherStruts.xml">
</struts>
引入其他的xml文件,固然可以,但是依然无法解决action配置文件过多的问题,在整理Struts2提供了两种解决方案,如下:
第一种URL:
http://localhost:8080/projectname/User!add
http://localhost:8080/projectname/User!update
http://localhost:8080/projectname/User!list
如上三个URL所示,User是Action类名,对应UserAction类:后面的!+方法名。
第二种URL:
http://localhost:8080/projectname/User?method:add
http://localhost:8080/projectname/User?method:update
http://localhost:8080/projectname/User?method:list
如上三个URL所示,User是Action类名,对应UserAction类:后面的?+method:方法名。
再接着,我们看看我们的struts.xml该如何来写?
<package name="default" extends="struts-default" namespace="/">
<action name="user" class="com.hp.it.UserAction" >
<result name="add">/WEB-INF/user/addUser.jsp</result>
</action>
<action name="user" class="com.hp.it.UserAction" >
<result name="update">/WEB-INF/user/updateUser.jsp</result>
</action>
<action name="user" class="com.hp.it.UserAction" >
<result name="list">/WEB-INF/user/listUser.jsp</result>
</action>
</package>
注意了啊,这里的action属性name是url中那个user,即是对应着UserAction.方法是由调用的时候来决定的看具体使用谁。
同样的,我们来看看在UserAction中应该这样来写:
public class UserAction{
public String addUser(){
System.out.println("addUser");
return "add";
}
public String updateUser(){
System.out.println("updateUser");
return "update";
}
public String listUser(){
System.out.println("listUser");
return "list";
}
}
这个方法虽然减少了action的配置,但是增加了大量的结果集的配置。所有问题来了,有没有解决这个问题的方法呢?有
我们可以通过通配符来解决这个问题,这儿有一个核心思想:(约定优于配置),如下:
<action name="*_*" class="com.hp.it.action.{1}Action" method="{2}">
<result>/WEB-INF/{1}/{2}.jsp</result>
</action>
这里要强调一下,标签<result>默认的属性是 name="success"。约定优于配置,那么我们的约定是对于URl来说,它的格式应该如下面这样来向服务器端发出请求:
http://localhost:8080/projectname/User_add
http://localhost:8080/projectname/User_update
http://localhost:8080/projectname/User_list
上面这种对于URL的约定,直接可以使用通配符*_*来对它过滤。大大简化了配置文件的大小。(这种情况下,注意大小写字母)
前面这些都是在说,服务器端的跳转,那么客户端的跳转怎么来实现呢?比如说,我们的User类在add完成之后,往往要跳转到它的list页面,这时候应该这样来配置:
<action name="*_*" class="com.hp.it.action.{1}Action" method="{2}">
<result>/WEB-INF/{1}/{2}.jsp</result>
<result type="redirect" name="r_list">/{1}_list.action</result>
</action>
大概看这个的含义就是说,当name=r_list的时候,进行重定向,并且重定向到{1}_list.action。相应的UserAction应该这么写
public class UserAction{
public String addUser(){
System.out.println("addUser");
return "r_list";
}
public String updateUser(){
System.out.println("updateUser");
return "update";
}
public String listUser(){
System.out.println("listUser");
return "list";
}
}
如果按上述方法来做,是不是效果会更好呢。但是我们通常看到的URL,往往很少再其屁股后面加".action"这个后缀,其实这个是可以
上面这个配置语句配置了对于.action的请求都进行过滤,同样也可以我们自己设定,如下:
<constant name="struts.action.extension" value="action,do,zxg" />
如上这种,当以.action;.do;.zxg的URL路径访问的时候,都会进入filter来过滤的。
*************************************************************************************************************************
接下来,我们再看看Struts2中是如何对参数传值做处理的(了解地址和类的对应关系;了解数据的通信(参数)的)。这部分是很关键的,而且一定要掌握清楚,不要跟SprigMVC相混淆。这段逻辑如果错误的话,调试代码的时候,介于前端和后端之间,断点加了也进不去,非常不好调控,所以在掌握原理的时候必须要掌握的清清楚楚的。那么接下来讲讲struts2传递参数的三种方案,分别如下:首先给你一个URL
http://16.158.70.172:8080/wstax-admin/report/assetsTransactionsByRegionTime?startTime=2015-06-01&endTime=2015-06-10&_=1434004102399
如上图,分析这个URL如下,前面的16.158.70.172是IP地址,相当于localhost,相当于127.0.0.1.然后是项目名称wstax-admin,然后是路径名称,然后我们看这个action name="assetsTransactionsByRegionTime"其后传过来三个参数,startTime和endTime分别是起始和截止时间,然后是后面的_1434004102399这个字符串,这是由于get请求的时候,加一个由时间随机生成的字符串,这样保证了每个url不同,这样每次就不会再去取缓存中的东西,而是去服务器上的东西,保证每次取的资源都是更新过后最新的资源。现在我们拦截了这个请求,传参的方法是在Action中,定义一个跟参数完全相同名字的变量,写getter和setter方法。如下:
@Controller("dailyMonitoringAction")
@Scope("prototype")
public class DailyMonitoringAction extends BaseAction {
private static final long serialVersionUID = -2065341145635610669L;
@Autowired
private IDailyMonitoringService dailyMonitoringService;
private String startTime = null;
private String endTime = null;
public String getStartTime() {
return startTime;
}
public void setStartTime(String startTime) {
this.startTime = startTime;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
}
然后你看我们的Action中,
/*SpringMVC传递参数和Struts传递参数 不同; Struts会调用setter方法来将值返回*/
public String loadAssetsTransactionsByRegionTime() {
lineVM = new LineChartVM();
lineVM.setTitle("Assets Transactions By Region");
lineVM.setyAxisName("Transactions");
Map<String, Map<String, Double>> assetRegionMap = dailyMonitoringService
.loadAssetRegionTransactionTime(startTime, endTime);
lineVM.setCategories(new ArrayList<String>(assetRegionMap.keySet()));
Map<String, List<Double>> seriesMap = pivotingMap(assetRegionMap, 0D);
List<ChartSerieVM> seriesList = new ArrayList<ChartSerieVM>();
for (String key : seriesMap.keySet()) {
ChartSerieVM chartSerieVM = new ChartSerieVM();
chartSerieVM.setName(key);
chartSerieVM.setData(seriesMap.get(key));
seriesList.add(chartSerieVM);
}
lineVM.setSeries(seriesList);
return SUCCESS;
}
你看我们的startTime和endTime是直接使用的,没有在函数的参数中写,而且定义的时候我们定义的是private String endTime = null;但是使用的时候,值就这么直接传递了进来,就是这么神奇啊。另外两种参数传递方法是ActionContext.getContext().put("startTime","2015-06-01");ActionContext.getContext.put("endTime","2015-06-10");(其中put进去的一对对的键值对)和通过Servlet的API来传值(ServletActionContext.getRequest.setAttribute("startTime","2015-06-01");ServletActionContext.getRequest.setAttribute("endTime","2015-06-10");)
前台在展现数据时候,可以有如下几种方法:
1.${startTime} ${endTime}直接取值.
2.通过struts2的标签库<%@taglib prefix="s" uri="/struts-tags"%> 引入struts2jar包中的一个tags标签库,然后使用如下方式:
<s:property value="#startTime">
<s:property value="#endTime">
就可以将数据展现出来。(注意这里的value中的变量名前面要加‘#’号。)
备注:对于ServletActionContext.getRequest.setAttribute("endTime","2015-06-10");这种取值方式,在前台展示的时候需要这样来用,如下:
<s:property value="#request.endTime">
************************************************************************************************************************************
接下来我们看看Struts中最核心的知识点:
:
鸣谢:
参考博客(http://www.cnblogs.com/suxiaolei/archive/2011/10/28/2228063.html)
Struts 2.3.24源码解析+Struts2拦截参数,处理请求,返回到前台过程详析的更多相关文章
- Okhttp3源码解析(5)-拦截器RetryAndFollowUpInterceptor
### 前言 回顾: [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析](htt ...
- Okhttp3源码解析(4)-拦截器与设计模式
### 前言 回顾: [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析](htt ...
- 源码解析Grpc拦截器(C#版本)
前言 其实Grpc拦截器是我以前研究过,但是我看网上相关C#版本的源码解析相对少一点,所以笔者借这篇文章给大家分享下Grpc拦截器的实现,废话不多说,直接开讲(Grpc的源码看着很方便,包自动都能还原 ...
- jquery源码解析:jQuery延迟对象Deferred(工具方法)详解2
请接着上一课继续看. $.Deferred()方法中,有两个对象,一个是deferred对象,一个是promise对象. promise对象有以下几个方法:state,always,then,prom ...
- jquery源码解析:jQuery延迟对象Deferred(工具方法)详解1
请先看上一课的回调对象.Deferred是通过extend添加到jQuery中的工具方法.如下所示: jQuery.extend({ Deferred: function( func ) { }, w ...
- jQuery 源码解析(二十八) 样式操作模块 scrollLeft和scrollTop详解
scrollLeft和scrollTop用于获取/设置滚动条的,如下: scrollLeft(val) ;读取或设置整个页面的水平滚动条距离 scrollTop(val) ;读取或设置整个页面的垂直滚 ...
- Deeplab v3+中的骨干模型resnet(加入atrous)的源码解析,以及普通resnet整个结构的构建过程
加入带洞卷积的resnet结构的构建,以及普通resnet如何通过模块的组合来堆砌深层卷积网络. 第一段代码为deeplab v3+(pytorch版本)中的基本模型改进版resnet的构建过程, 第 ...
- axios 源码解析(下) 拦截器的详解
axios的除了初始化配置外,其它有用的应该就是拦截器了,拦截器分为请求拦截器和响应拦截器两种: 请求拦截器 ;在请求发送前进行一些操作,例如在每个请求体里加上token,统一做了处理如果以后要 ...
- 谷歌BERT预训练源码解析(二):模型构建
目录前言源码解析模型配置参数BertModelword embeddingembedding_postprocessorTransformerself_attention模型应用前言BERT的模型主要 ...
随机推荐
- 描述一下C#中索引器的实现过程,是否只能根据数字进行索引?
不是.可以用任意类型. 索引器是一种特殊的类成员,它能够让对象以类似数组的方式来存取,使程序看起来更为直观,更容易编写. 1.索引器的定义 C#中的类成员可以是任意类型,包括数组和集合.当一个类包含了 ...
- Atitit 编程语言知识点tech tree v2 attilax大总结
Atitit 编程语言知识点tech tree v2 attilax大总结 大分类中分类小分类知识点原理与规范具体实现(javac#里面的实现phpjsdsl(自己实现其他语言实现 类与对象实现对象实 ...
- fir.im Weekly - 从零开始,搭建理想的直播平台
2016年苹果发布会如期而至,新一代的 iPhone 7, Apple Watch 同时亮相,可惜大家期待的 MacBookPro.AR.VR产品并未出现.不论大家对新产品是"买买买&quo ...
- 谷毅(WingKu)横空出世
天空划出一道彩虹,谷毅(WingKu)横空出世,这里的产品全由本人开发制作,如有雷同不胜荣幸,欢迎前往下载使用,如果有啥建议或者使用当中遇到什么问题,也可在此留言评论~OK,开张啦~!以后每个产品会以 ...
- iOS中的预编译指令的初步探究
目录 文件包含 #include #include_next #import 宏定义 #define #undef 条件编译 #if #else #endif #if define #ifdef #i ...
- 180分钟的python学习之旅
最近在很多地方都可以看到Python的身影,尤其在人工智能等科学领域,其丰富的科学计算等方面类库无比强大.很多身边的哥们也提到Python非常的简洁方便,比如用Django搭建一个见得网站只需要半天时 ...
- Jetty集群配置Session存储到MySQL、MongoDB
在Web开发中,Session表示HTTP服务器与客户端(例如浏览器)的“会话”,每个客户端会有其对应的Session保存在服务器端,通常用来保存和客户端关联的一些信息,例如是否登录.购物车等. Se ...
- SQL*Loader之CASE5
CASE5 1. SQL脚本 [oracle@node3 ulcase]$ cat ulcase5.sql set termout off rem host write sys$output &quo ...
- 使用ZeroClipboard解决跨浏览器复制到剪贴板的问题
Zero Clipboard的实现原理Zero Clipboard 利用透明的Flash让其漂浮在复制按钮之上,这样其实点击的不是按钮而是 Flash ,这样将需要的内容传入Flash,再通过Flas ...
- static 关键字
static对象如果出现在类中,那么该对象即使从未被使用到,它也会被构造以及析构.而函数中的static对象,如果该函数从未被调用,这个对象也就绝不会诞生,但是在函数每次被调用时检查对象是否需要诞生. ...