使用Struts框架时,需要在web应用程序的web.xml文件中增加filter配置。如下代码
<
filter
>
<
filter-name
>
struts
</
filter-name
>
<
filter-class
>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</
filter-class
>
</
filter
>
web容器(如Tomcat)在加载应用的时候,会根据这里配置的filter标签,去实例化一个StrutsPrepareAndExecuteFilter对象,然后调用
StrutsPrepareAndExecuteFilter的
init方法做一些初始化的操作。
StrutsPrepareAndExecuteFilter实现了标准Filter接口。filter是由web容器调用的,在初次加载应用的时候会调用init方法来做一些初始化,在客户每次向web容器发送请求的时候,都会调用filter对象的doFilter方法。请求结束,就会调用destory方法。Filter机制很容易理解,
StrutsPrepareAndExecuteFilter的init代码如下所示。
StrutsPrepareAndExecuteFilter.init
public
void
init(FilterConfig filterConfig)
throws
ServletException {
InitOperations init =
new
InitOperations();
try
{
FilterHostConfig config =
new
FilterHostConfig(filterConfig);
init.initLogging(config);
//initialize the logger if it was configured
Dispatcher dispatcher = init.initDispatcher(config);
//create a dispatcher, and initialize it
init.initStaticContentLoader(config, dispatcher);
prepare
=
new
PrepareOperations(filterConfig.getServletContext(), dispatcher);
execute
=
new
ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this
.
excludedPatterns
= init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
}
finally
{
init. cleanup();
}
}
FilterHostConfig对FilterConfig实现了包装,个人理解是为了适应struts而来,但在功能上几乎未作变动。初始化所作的事情主要涉及到三个元素:PrepareOperations、ExecuteOperations和Dispatcher。prepare的类型是
org.apache.struts2.dispatcher.ng.
PrepareOperations,包括在一个请求在真正被执行前所要做的动作。execute的类型是
org.apache.struts2.dispatcher.ng.ExecuteOperations,包括了filter中所要执行的全部操作。
看这里的init方法,主要做的事情就是初始化日子系统(initLogging),初始化Dispatcher对象,然后就是对prepare和execute对象的初始化。
这两个类都容易理解,难点是Dispatcher的引入。在initDispatcher方法调用中,首先创建一个dispatcher对象,然后调用这个dispatcher对象的初始化方法。创建dispatcher对象的内容很简单,取出request请求中的参数和上下文,新建一个dispatcher对象,但在init方法中,则做了大量的事情。Dispatcher.java中init函数代码如下
public
void
init() {
if
(
configurationManager
==
null
) {
configurationManager
= createConfigurationManager(BeanSelectionProvider.
DEFAULT_BEAN_NAME
);
}
try
{
init_FileManager();
init_DefaultProperties();
// [1]
init_TraditionalXmlConfigurations();
// [2]
init_LegacyStrutsProperties();
// [3]
init_CustomConfigurationProviders();
// [5]
init_FilterInitParameters() ;
// [6]
init_AliasStandardObjects() ;
// [7]
Container container = init_PreloadConfiguration();//在这里,配置对象会重新加载容器
container.inject(
this
);
init_CheckWebLogicWorkaround(container);
if
(!
dispatcherListeners
.isEmpty()) {
for
(DispatcherListener l :
dispatcherListeners
) {
l.dispatcherInitialized(
this
);
}
}
}
catch
(Exception ex) {
if
(
LOG
.isErrorEnabled())
LOG
.error(
"Dispatcher initialization failed"
, ex);
throw
new
StrutsException(ex);
}
}
Dispatcher对象初始化的时候要做哪些工作呢?首先初始化一个配置管理器对象configurationManager。然后是初始化各种形式的配置加载方式,通过configuratoinManager来将这些配置加载方式保存起来,像init_FileManager初始化文件管理器、init_DefaultProperties初始化default.properties的加载方式、init_TraditionalXmlConfiguration初始化struts-default.xml、struts.xml、struts-plugin.xml的加载方式,这里只是定义了配置的加载方式,但是并没有真正执行配置文件的加载。 再然后通过init_PreloadConfiguration初始化容器和初始化配置对象configuration,并且会将前面configurationManager保存的初始化的配置项的加载方式添加到configurationManager包含的配置对象configuration中去,实现容器的依赖注入。那么,一个配置对象configuration中所包含的内容有哪些呢?从DefaultConfiguration类中声明的相关成员变量可以略知一二。
protected
static
final
Logger
LOG
= LoggerFactory.getLogger(DefaultConfiguration.
class
);
// Programmatic Action Configurations
protected
Map<String, PackageConfig>
packageContexts
=
new
LinkedHashMap<String, PackageConfig>();
protected
RuntimeConfiguration
runtimeConfiguration
;
protected
Container
container
;
protected
String
defaultFrameworkBeanName
;
protected
Set<String>
loadedFileNames
=
new
TreeSet<String>();
protected
List<UnknownHandlerConfig>
unknownHandlerStack
;
ObjectFactory
objectFactory
;
最后通过init_CheckWebLogicWorkaround来检查是否需要支持WebLogic服务器的特殊设置。
通过这样的一段dispatcher对象的初始化动作,实现了对配置的加载、容器的注入和一些服务项的设置,也就是说通过dispatcher完成了资源的初始化、容器和服务之间的关联关系。
StrutsPrepareAndExecuteFilter初始化在应用加载的时候被调用一次,初始化了dispatcher对象,并且通过dispatcher对象完成了配置项的初始化、容器关联等。在每次接收请求的时候,就doFilter方法就会被调用。doFilter的代码如下所示
public
void
doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws
IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try
{
prepare
.setEncodingAndLocale(request, response);
prepare
.createActionContext(request, response);
prepare
.assignDispatcherToThread();
if
(
excludedPatterns
!=
null
&&
prepare
.isUrlExcluded(request,
excludedPatterns
)) {
chain.doFilter(request, response);
}
else
{
request =
prepare
.wrapRequest(request);
ActionMapping mapping =
prepare
.findActionMapping(request, response,
true
);
if
(mapping ==
null
) {
boolean
handled =
execute
.executeStaticResourceRequest(request, response);
if
(!handled) {
chain.doFilter(request, response);
}
}
else
{
execute
.executeAction(request, response, mapping);
}
}
}
finally
{
prepare
.cleanupRequest(request);
}
}
如果请求的URL在当前这个filter所负责的模式之外,就把这个请求传递给下一个filter链。否则,就对这个请求进行处理。请求进行处理通过execute.executeAction来完成。ExecuteOperations类的executeAction方法代码如下所示
public
void
executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
throws
ServletException {
dispatcher
.serviceAction(request, response,
servletContext
, mapping);
}
所以,真正的任务的执行要是要通过dispatcher来完成。
应用结束后,会调用
StrutsPrepareAndExecuteFilter的destroy方法,通过调用prepare.cleanupDispatcher()。PrepareOperations类的cleanupDispatcher函数代码如下
public
void
cleanupDispatcher() {
if
(
dispatcher
==
null
) {
throw
new
StrutsException(
"Something is seriously wrong, Dispatcher is not initialized (null) "
);
}
else
{
try
{
dispatcher
.cleanup();
}
finally
{
ActionContext. setContext(
null
);
}
}
}
最后还是由dispatcher对象完成真正的请求动作。
综上来看,Dispatcher在框架中起到了核心作用,它是Struts2框架的核心分发器,它的职责涉及到系统的初始化、接收和预处理http请求以及系统清理任务,贯穿了应用的完整的生命周期。