Struts的前端控制器是StrutsPrepareAndExecuteFilter过滤器,这个过滤器和普通过滤器一样,主要有两个比较重要的方法:init(FilterConfig filterConfig)、doFilter(ServletRequest req, ServletResponse res, FilterChain chain)。下面就主要说一下这个两个方法具体做了什么事,
Init()方法详细步骤:
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
dispatcher = init.initDispatcher(config);
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 {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
- 首先是创建FilterHostConfig类,用于封装filterConfig参数。
在这个类里就是下get、set方法,该类封装的是filterConfig对象,filterConfiguration是一个接口具体实现类是MockFilterConfig,封装的就是在web.xml配置文件该filter的键值对。 - 接着是根据config初始化Struts内部使用的日志。
在initLogging的方法内部,其实就是传进去一个config对象,然后从该对象中获得一个日志工厂的工厂名,然后根据这个工厂名通过反射的方式创建一个该工厂的实例。 - 然后是根据config初始化Dispatcher
//dispatcher的初始化方法
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这一步其实做了很多事情,首先是根据servletContext和filterConfig构造一个Dispatcher对象,然后执行Dispatcher的init()方法,这个方法内部就开始加载Struts的几个配置文件了。
可以看到这个init()方法的内部就是用来依次加载Struts的几个配置文件,依次是default.properties 、struts-default.xml、struts-plugin.xml、struts.xml,
其他的一些方法没有仔细深究。
4. 根据servletContext和Dispatcher实例化一个prepareOperation对象和ExecuteOperation对象。
5. 后面的没有细看。
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);
}
}
- 在这个方法里首先是处理编码问题和国际化问题,编码方式默认会设置为“ut-8”,区域是使用默认区域。
- 创建action容器,这个action容器用来装Request、response、servletContext等,这个方法内部有一个计数器,暂时没看明白这个计数器是用来做什么的,感觉像是用来清理对象但是的,不确定
关于这个action容器,首先会检查这个容器是否有值,如果有值的话说明应该由一个请求转发,转过来的请求,这是只要将转发前的那个action容器中的值拿过来就好,如果目前容器没有值,那就说明这个请求是一个新的请求,需要重新构建一个action容器供action使用。
将Dispatcher对象分配给本地线程变量,这一步也不是很懂。
- 然后就是执行模式匹配,如果匹配到了就放行。