原文转载自:
一、概述
Struts2的核心是一个Filter,Action可以脱离web容器,那么是什么让http请求和action关联在一起的,下面我们深入源码来分析下Struts2是如何工作的。
鉴于常规情况官方推荐使用StrutsPrepareAndExecuteFilter替代FilterDispatcher,我们此文 将剖析StrutsPrepareAndExecuteFilter,其在工程中作为一个Filter配置在web.xml中,配置如下:
- < filter > < filter-name > </ filter-name > < filter-class ></ filter-class > </ filter > < filter-mapping > < filter-name > </ filter-name > < url-pattern > </ url-pattern > </ filter-mapping >
- <filter> <filter-name></filter-name> <filter-class></filter-class> </filter> <filter-mapping> <filter-name></filter-name> <url-pattern></url-pattern> </filter-mapping>
二、源码属性方法简介
下面我们研究下StrutsPrepareAndExecuteFilter源码,类的主要信息如下:
属性摘要 | |
---|---|
protected < > | |
protected | |
protected | |
StrutsPrepareAndExecuteFilter与普通的Filter并无区别,方法除继承自Filter外,仅有一个回调方法,第三部分我 们将按照Filter方法调用顺序,由init—>doFilter—>destroy顺序地分析源码。
方法摘要 | |
---|---|
void | () 继承自Filter,用于资源释放 |
void | ( req, res, chain) 继承自Filter,执行方法 |
void | ( filterConfig) 继承自Filter,初始化参数 |
protected void | ( dispatcher, filterConfig) Callback for post initialization(一个空的方法,用于方法回调初始化) |
三、源码剖析
1、init方法
init是Filter第一个运行的方法,我们看下struts2的核心Filter在调用init方法初始化时做哪些工作:
- public void throws new try //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中 new // 初始化struts内部日志 //<strong>创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源</strong> //初始化类属性:prepare 、execute new new this //回调空的postInit方法 finally }
- publicvoidthrows new try //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中 new // 初始化struts内部日志 //<strong>创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源</strong> //初始化类属性:prepare 、execute new new this //回调空的postInit方法 finally }
首先看下FilterHostConfig ,源码如下:
- public class implements private *构造函数
- */ public this * 根据init-param配置的param-name获取param-value的值
- */ public return * 返回初始化参数名的List
- */ public return public return }
- publicclassimplements private *构造函数
- */ public this * 根据init-param配置的param-name获取param-value的值
- */ public return * 返回初始化参数名的List
- */ public return public return }
只有短短的几行代码,getInitParameterNames是这个类的核心,将Filter初始化参数名称有枚举类型转为Iterator。此类的主要作为是对filterConfig 封装。
重点来了,创建并初始化Dispatcher
- public return }
- public return }
创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,然后根绝servlet上下文和参数Map构造Dispatcher :
- private new for return new }
- private new for returnnew }
Dispatcher初始化,加载struts2的相关配置文件,将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.xml,……
- /**
- *初始化过程中依次加载如下配置文件
- */ public void if null new try
- this if for this catch if throw new }
- /**
- *初始化过程中依次加载如下配置文件
- */ publicvoid ifnull new try
- this if for this catch if thrownew }
初始化default.properties,具体的初始化操作在DefaultPropertiesProvider类中
- private void new }
- privatevoid new }
下面我们看下DefaultPropertiesProvider类源码:
- public void throws null try new );
- catch throw new }
- publicvoid throws null try new);
- catch thrownew }
其他的我们再次省略,大家可以浏览下各个初始化操作都加载了那些文件
3、doFilter方法
doFilter是过滤器的执行方法,它拦截提交的HttpServletRequest请求,HttpServletResponse响应,作为strtus2的核心拦截器,在doFilter里面到底做了哪些工作,我们将逐行解读其源码,源码如下:
- public void throws
- try
- if null else true if null boolean if else finally }
- publicvoidthrows
- try
- ifnull else true ifnull boolean if else finally }
setEncodingAndLocale调用了dispatcher方法的prepare方法:
- /**
- * Sets the request encoding and locale on the response
- */ public void }
- /**
- * Sets the request encoding and locale on the response
- */ publicvoid }
下面我们看下prepare方法,这个方法很简单只是设置了encoding 、locale ,做的只是一些辅助的工作:
- public void null if null null if null if null try catch if null if ); }
- publicvoid null ifnull null ifnull ifnull try catch ifnull if ); }
Action上下文创建(重点)
ActionContext是一个容器,这个容易主要存储request、session、application、parameters等相关信 息.ActionContext是一个线程的本地变量,这意味着不同的action之间不会共享ActionContext,所以也不用考虑线程安全问 题。其实质是一个Map,key是标示request、session、……的字符串,值是其对应的对象:
- static new Map<String, Object> context;
- staticnew Map<String, Object> context;
下面我们看下如何创建action上下文的,代码如下:
- /**
- *创建Action上下文,初始化thread local
- */ public ;
- if null ;
- if null
- new new else class null
- new
- return }
- /**
- *创建Action上下文,初始化thread local
- */ public ;
- ifnull ;
- ifnull
- newnew else class null
- new
- return }
上面代码中dispatcher.createContextMap,如何封装相关参数:
- public
- new
- new
- new
- new
- if null return }
- public
- new
- new
- new
- new
- ifnull return }
我们简单看下RequestMap,其他的省略。RequestMap类实现了抽象Map,故其本身是一个Map,主要方法实现:
- //map的get实现 public return //map的put实现 public null return }
- //map的get实现 public return //map的put实现 public null return }
下面是源码展示了如何执行Action控制器:
- public voidthrows public void throws
- boolean null if if null if null try
- class true false
- if null else
- if catch
- if if null + request.getQueryString();
- else catch finally }
- publicvoidthrows publicvoid throws
- booleannull if ifnull ifnull try
- class truefalse
- ifnull else
- if catch
- if ifnull + request.getQueryString();
- else catch finally }
文中对如何解析Struts.xml,如何将URL与action映射匹配为分析,有需要的我后续补全,因为 StrutsXmlConfigurationProvider继承XmlConfigurationProvider,并在register方法回调父 类的register,有兴趣的可以深入阅读下下XmlConfigurationProvider源码:
- public void throws if null class class new public throws return
- super }
- publicvoidthrows ifnullclass classnew publicthrows return
- super }
struts2-core-2.2.1.jar包中struts-2.1.7.dtd对于Action的定义如下:
- <!ELEMENT action (param|result|interceptor-ref|exception-mapping)* > >
- <!ELEMENT action (param|result|interceptor-ref|exception-mapping)*> >
从上述DTD中可见Action元素可以含有name 、class 、method 、converter 属性。
XmlConfigurationProvider解析struts.xml配置的Action元素:
- protected void throws );
- );
- );
- if null
- ) ? methodName.trim() : null
- if
- className = packageContext.getDefaultClassRef();
- } else {
- className = ActionSupport.class.getName();
- }*/ else if if return try catch throw new new if ) : ) + name + }
- protectedvoidthrows );
- );
- );
- ifnull
- ) ? methodName.trim() : null
- if
- className = packageContext.getDefaultClassRef();
- } else {
- className = ActionSupport.class.getName();
- }*/ else if if return try catch thrownew new if ) : ) + name + }
工作中不涉及Struts2,本周工作有个2天的空档期,稍微看了下struts2的文档,写了个demo,从源码的角度研究了下运行原理,如有分析不当请指出,我后续逐步完善更正,大家共同提高。
一、概述
Struts2的核心是一个Filter,Action可以脱离web容器,那么是什么让http请求和action关联在一起的,下面我们深入源码来分析下Struts2是如何工作的。
鉴于常规情况官方推荐使用StrutsPrepareAndExecuteFilter替代FilterDispatcher,我们此文 将剖析StrutsPrepareAndExecuteFilter,其在工程中作为一个Filter配置在web.xml中,配置如下:
- < filter > < filter-name > </ filter-name > < filter-class ></ filter-class > </ filter > < filter-mapping > < filter-name > </ filter-name > < url-pattern > </ url-pattern > </ filter-mapping >
- <filter> <filter-name></filter-name> <filter-class></filter-class> </filter> <filter-mapping> <filter-name></filter-name> <url-pattern></url-pattern> </filter-mapping>
二、源码属性方法简介
下面我们研究下StrutsPrepareAndExecuteFilter源码,类的主要信息如下:
属性摘要 | |
---|---|
protected < > | |
protected | |
protected | |
StrutsPrepareAndExecuteFilter与普通的Filter并无区别,方法除继承自Filter外,仅有一个回调方法,第三部分我 们将按照Filter方法调用顺序,由init—>doFilter—>destroy顺序地分析源码。
方法摘要 | |
---|---|
void | () 继承自Filter,用于资源释放 |
void | ( req, res, chain) 继承自Filter,执行方法 |
void | ( filterConfig) 继承自Filter,初始化参数 |
protected void | ( dispatcher, filterConfig) Callback for post initialization(一个空的方法,用于方法回调初始化) |
三、源码剖析
1、init方法
init是Filter第一个运行的方法,我们看下struts2的核心Filter在调用init方法初始化时做哪些工作:
- public void throws new try //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中 new // 初始化struts内部日志 //<strong>创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源</strong> //初始化类属性:prepare 、execute new new this //回调空的postInit方法 finally }
- publicvoidthrows new try //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中 new // 初始化struts内部日志 //<strong>创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源</strong> //初始化类属性:prepare 、execute new new this //回调空的postInit方法 finally }
首先看下FilterHostConfig ,源码如下:
- public class implements private *构造函数
- */ public this * 根据init-param配置的param-name获取param-value的值
- */ public return * 返回初始化参数名的List
- */ public return public return }
- publicclassimplements private *构造函数
- */ public this * 根据init-param配置的param-name获取param-value的值
- */ public return * 返回初始化参数名的List
- */ public return public return }
只有短短的几行代码,getInitParameterNames是这个类的核心,将Filter初始化参数名称有枚举类型转为Iterator。此类的主要作为是对filterConfig 封装。
重点来了,创建并初始化Dispatcher
- public return }
- public return }
创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,然后根绝servlet上下文和参数Map构造Dispatcher :
- private new for return new }
- private new for returnnew }
Dispatcher初始化,加载struts2的相关配置文件,将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.xml,……
- /**
- *初始化过程中依次加载如下配置文件
- */ public void if null new try
- this if for this catch if throw new }
- /**
- *初始化过程中依次加载如下配置文件
- */ publicvoid ifnull new try
- this if for this catch if thrownew }
初始化default.properties,具体的初始化操作在DefaultPropertiesProvider类中
- private void new }
- privatevoid new }
下面我们看下DefaultPropertiesProvider类源码:
- public void throws null try new );
- catch throw new }
- publicvoid throws null try new);
- catch thrownew }
其他的我们再次省略,大家可以浏览下各个初始化操作都加载了那些文件
3、doFilter方法
doFilter是过滤器的执行方法,它拦截提交的HttpServletRequest请求,HttpServletResponse响应,作为strtus2的核心拦截器,在doFilter里面到底做了哪些工作,我们将逐行解读其源码,源码如下:
- public void throws
- try
- if null else true if null boolean if else finally }
- publicvoidthrows
- try
- ifnull else true ifnull boolean if else finally }
setEncodingAndLocale调用了dispatcher方法的prepare方法:
- /**
- * Sets the request encoding and locale on the response
- */ public void }
- /**
- * Sets the request encoding and locale on the response
- */ publicvoid }
下面我们看下prepare方法,这个方法很简单只是设置了encoding 、locale ,做的只是一些辅助的工作:
- public void null if null null if null if null try catch if null if ); }
- publicvoid null ifnull null ifnull ifnull try catch ifnull if ); }
Action上下文创建(重点)
ActionContext是一个容器,这个容易主要存储request、session、application、parameters等相关信 息.ActionContext是一个线程的本地变量,这意味着不同的action之间不会共享ActionContext,所以也不用考虑线程安全问 题。其实质是一个Map,key是标示request、session、……的字符串,值是其对应的对象:
- static new Map<String, Object> context;
- staticnew Map<String, Object> context;
下面我们看下如何创建action上下文的,代码如下:
- /**
- *创建Action上下文,初始化thread local
- */ public ;
- if null ;
- if null
- new new else class null
- new
- return }
- /**
- *创建Action上下文,初始化thread local
- */ public ;
- ifnull ;
- ifnull
- newnew else class null
- new
- return }
上面代码中dispatcher.createContextMap,如何封装相关参数:
- public
- new
- new
- new
- new
- if null return }
- public
- new
- new
- new
- new
- ifnull return }
我们简单看下RequestMap,其他的省略。RequestMap类实现了抽象Map,故其本身是一个Map,主要方法实现:
- //map的get实现 public return //map的put实现 public null return }
- //map的get实现 public return //map的put实现 public null return }
下面是源码展示了如何执行Action控制器:
- public voidthrows public void throws
- boolean null if if null if null try
- class true false
- if null else
- if catch
- if if null + request.getQueryString();
- else catch finally }
- publicvoidthrows publicvoid throws
- booleannull if ifnull ifnull try
- class truefalse
- ifnull else
- if catch
- if ifnull + request.getQueryString();
- else catch finally }
文中对如何解析Struts.xml,如何将URL与action映射匹配为分析,有需要的我后续补全,因为 StrutsXmlConfigurationProvider继承XmlConfigurationProvider,并在register方法回调父 类的register,有兴趣的可以深入阅读下下XmlConfigurationProvider源码:
- public void throws if null class class new public throws return
- super }
- publicvoidthrows ifnullclass classnew publicthrows return
- super }
struts2-core-2.2.1.jar包中struts-2.1.7.dtd对于Action的定义如下:
- <!ELEMENT action (param|result|interceptor-ref|exception-mapping)* > >
- <!ELEMENT action (param|result|interceptor-ref|exception-mapping)*> >
从上述DTD中可见Action元素可以含有name 、class 、method 、converter 属性。
XmlConfigurationProvider解析struts.xml配置的Action元素:
- protected void throws );
- );
- );
- if null
- ) ? methodName.trim() : null
- if
- className = packageContext.getDefaultClassRef();
- } else {
- className = ActionSupport.class.getName();
- }*/ else if if return try catch throw new new if ) : ) + name + }
- protectedvoidthrows );
- );
- );
- ifnull
- ) ? methodName.trim() : null
- if
- className = packageContext.getDefaultClassRef();
- } else {
- className = ActionSupport.class.getName();
- }*/ else if if return try catch thrownew new if ) : ) + name + }