这个filter是struts中最重要的filter,主要完成以下四个功能:
1、处理action
2、清理ActionContext
3、处理静态资源
4、触发指定请求生命周期内的拦截器链
这个filter是武断性的,也就是说它通过ActonMapper来寻找特定的action,如果找到了,那么其他剩下的filter就不会再被引用,转而去处理action,这意味着其他过滤器需要放置到这个filter前面,否则将不会被引用到。
public void setDevMode(String mode) {
devMode = "true".equals(mode);
}
这样的set方法还是值得我们借鉴的。
public void init(FilterConfig filterConfig) throws ServletException {
try {
this.filterConfig = filterConfig;
initLogging();
dispatcher = createDispatcher(filterConfig);
dispatcher.init();
dispatcher.getContainer().inject(this);
staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
} finally {
ActionContext.setContext(null);
}
}
在初始化方法中,会初始化log,创建调度者,并对静态资源做一些处理。
初始化log
private void initLogging() {
//查看web.xml中filter的初始化参数中有无配置loggerFactory
String factoryName = filterConfig.getInitParameter("loggerFactory");
if (factoryName != null) {
try {
Class cls = ClassLoaderUtils.loadClass(factoryName, this.getClass());
LoggerFactory fac = (LoggerFactory) cls.newInstance();
LoggerFactory.setLoggerFactory(fac);
} catch (InstantiationException e) {
System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default");
e.printStackTrace();
} catch (IllegalAccessException e) {
System.err.println("Unable to access logger factory: " + factoryName + ", using default");
e.printStackTrace();
} catch (ClassNotFoundException e) {
System.err.println("Unable to locate logger factory class: " + factoryName + ", using default");
e.printStackTrace();
}
}
log = LoggerFactory.getLogger(FilterDispatcher.class);
}
接下来,是初始化dispatcher的方法
/**
*加载配置文件,包括xml和一些默认的配置,
*更新一些配置,包括是否重新加载一些配置或资源.
*/
public void init() {
//struts.xml是配置文件的入口
if (configurationManager == null) {
configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//初始化配置,添加到configurationManager#containerProviders List中
try {
//加载org/apache/struts2/default.properties struts常量
init_DefaultProperties(); // [1]
//web.xml中的config
//若没有则加载struts-default.xml,struts-plugin.xml,struts.xml
init_TraditionalXmlConfigurations(); // [2]
init_LegacyStrutsProperties(); // [3]
//用户自定义的ConfigurationProvider web.xml 中 configProviders
init_CustomConfigurationProviders(); // [5]
//web.xml中配置的init-param参数
init_FilterInitParameters() ; // [6]
//根据我们在struts.xml中配置的常量,初始化一些插件类
//比如我们设置的devmode
init_AliasStandardObjects() ; // [7]
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
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);
}
}
Container container = init_PreloadConfiguration(),这个方法负责解析xml
//方法调用了configurationManager的getConfiguration方法
private Container init_PreloadConfiguration() {
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();
boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
LocalizedTextUtil.setReloadBundles(reloadi18n);
return container;
}
//defaultFrameworkBeanName==“struts”
//方法又调用了configuration.reloadContainer(getContainerProviders());
public synchronized Configuration getConfiguration() {
if (configuration == null) {
setConfiguration(createConfiguration(defaultFrameworkBeanName));
try {
configuration.reloadContainer(getContainerProviders());
} catch (ConfigurationException e) {
setConfiguration(null);
throw new ConfigurationException("Unable to load configuration.", e);
}
} else {
conditionalReload();
}
return configuration;
}
然后会load在struts.xml中配置的package信息。
在源码中,我们发现这样一段代码:includeFileName.indexOf('*') != -1
说明在struts.xml中的include中,可以配置*进行模糊匹配
默认的,会在org.apache.struts2.static template中寻找,如果你想添加自己的静态资源包,可以在filter的init中传name为packages的值,用逗号隔开(还没搞明白)
在prepareDispatcherAndWrapRequest方法中,会对请求进行封装,包括设置字符编码,并对multipart/form-data的请求进行封装
在filter的doFilter方法中
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
ServletContext servletContext = getServletContext();
String timerKey = "FilterDispatcher_doFilter: ";
try {
// FIXME: this should be refactored better to not duplicate work with the action invocation
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
ActionContext ctx = new ActionContext(stack.getContext());
ActionContext.setContext(ctx);
UtilTimerStack.push(timerKey);
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
} catch (Exception ex) {
log.error("error getting ActionMapping", ex);
dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
return;
}
if (mapping == null) {
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if ("".equals(resourcePath) && null != request.getPathInfo()) {
resourcePath = request.getPathInfo();
}
if (staticResourceLoader.canHandle(resourcePath)) {
staticResourceLoader.findStaticResource(resourcePath, request, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
dispatcher.cleanUpRequest(request);
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
devModeOverride.remove();
}
}
在filter中,先看请求的是不是action,如果不是,就返回相应的资源,如果是的话,继续执行。注意,继续执行不会寻找下一个filter,而是转向action的代理,开始处理action。
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException {
Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
boolean nullStack = stack == null;
if (nullStack) {
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
stack = ctx.getValueStack();
}
}
if (stack != null) {
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
}
String timerKey = "Handling request from Dispatcher";
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
Configuration config = configurationManager.getConfiguration();
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
// if the ActionMapping says to go straight to a result, do it!
if (mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
proxy.execute();
}
// If there was a previous value stack then set it back onto the request
if (!nullStack) {
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch (ConfigurationException e) {
// WW-2874 Only log error if in devMode
if(devMode) {
String reqStr = request.getRequestURI();
if (request.getQueryString() != null) {
reqStr = reqStr + "?" + request.getQueryString();
}
LOG.error("Could not find action or result\n" + reqStr, e);
}
else {
LOG.warn("Could not find action or result", e);
}
sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
} catch (Exception e) {
sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
} finally {
UtilTimerStack.pop(timerKey);
}
}
我们注意到在serviceAction方法中,会创建ActionProxy,如果有拦截器那么就继续执行拦截器,否则返回结果。