Struts2 从2.1.3版本开始使用strutsPrepareAndExecuteFilter 启动struts2,在此版本之前使用的DispatcherFilter。
FilterDispatcher启动流程:
struts2是实现filter接口,所以我们在使用struts2的时候需要在web.xml文件中配置一个filter过滤器来启动struts2.
第一步: 初始化
public void init(FilterConfig filterConfig) throws ServletException {
try {
this.filterConfig = filterConfig;
initLogging(); //初始化struts2本身的日志工厂
dispatcher = createDispatcher(filterConfig); //创建dispatcher对象,具体见 ①
dispatcher.init(); //初始化dispatcher,具体见②
dispatcher.getContainer().inject(this);
//filterConfig设置到struts资源加载器中
staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
} finally {
ActionContext.setContext(null);
}
}
①
protected Dispatcher createDispatcher(FilterConfig filterConfig) {
Map<String, String> params = new HashMap<String, String>();
//从web.xml文件中读取初始化参数
for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {
String name = (String) e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
return new Dispatcher(filterConfig.getServletContext(), params); (1.1)
}
(1.1)
public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {
this.servletContext = servletContext; //保存servletContext对象
this.initParams = initParams; //保存初始化参数
}
②public void init() {
if (configurationManager == null) {
configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
try {
init_DefaultProperties(); // [1] 初始化struts的默认属性配置文件加载到配置管理器中
init_TraditionalXmlConfigurations(); // [2]读取web.xml中的config参数(如果没有则将读取系统默认的参数),将对应的配置文件加载到配置文件管理器
init_LegacyStrutsProperties(); // [3]
init_CustomConfigurationProviders(); // [5] 自定义配置文件加载器加载到配置管理器中
init_FilterInitParameters() ; // [6] 将Filter中的初始化参数加载配置文件中
init_AliasStandardObjects() ; // [7] 初始化一些可选属性配置
Container container = init_PreloadConfiguration();//国际化修改时是否重新加载国际化文件设置
container.inject(this);
init_CheckConfigurationReloading(container); //设置struts.xml文件修改是是否重新加载struts.xml文件
init_CheckWebLogicWorkaround(container);//STRUTS_DISPATCHER_PARAMETERSWORKAROUND 初始化
if (!dispatcherListeners.isEmpty()) {//dispatcherListeners在struts2自己本身是没有使用的,但是我们在对FilterDispatcher扩展的时候可以使用这个做一些我们想要的事
for (DispatcherListener l : dispatcherListeners) {
l.dispatcherInitialized(this);
}
}
} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
第二步:对请求做过滤处理
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 {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();//创建值栈对象
ActionContext ctx = new ActionContext(stack.getContext()); //将值栈中的参数保存到ActionContext容器中,保证值栈与容器中的值一致
ActionContext.setContext(ctx);
UtilTimerStack.push(timerKey);
request = prepareDispatcherAndWrapRequest(request, response);//第一次调用action时对dispatcher进行线程本地化,同时对request的编码和local进行设置,然后根据request类型进行装饰 ③
ActionMapping mapping;
try {
mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager()); //根据request获得一个actionMapping ④
} 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;
}
//正式开始对action进行处理 ⑤
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
③
protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {
Dispatcher du = Dispatcher.getInstance();
if (du == null) {//第一次调用时对dispatcher进行预处理
Dispatcher.setInstance(dispatcher); //将dispatcher保存到本地线程中
dispatcher.prepare(request, response);//dispatcher 的defaultEncoding、defaultLocale属性设置,同时对request的Encoding和Locale属性进行设置
} else {
dispatcher = du;
}
try {
request = dispatcher.wrapRequest(request, getServletContext());//根据request请求类型,返回一个被装饰成StrutsRequestWrapper或者MultiPartRequestWrapper(用于文件上传)
} catch (IOException e) {
String message = "Could not wrap servlet request with MultipartRequestWrapper!";
log.error(message, e);
throw new ServletException(message, e);
}
return request;
}
④
public ActionMapping getMapping(HttpServletRequest request,
ConfigurationManager configManager) {
ActionMapping mapping = new ActionMapping();
String uri = getUri(request);//从request中获取uri(uri对应于request中的javax.servlet.include.servlet_path参数值)
int indexOfSemicolon = uri.indexOf(";");
uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;
uri = dropExtension(uri, mapping); //去掉uri中的.action或其他后缀的扩展
if (uri == null) {
return null;
}
//解析uri获取命名空间和action名字
parseNameAndNamespace(uri, mapping, configManager);
//对.x或.y结尾的参数名进行处理
handleSpecialParameters(request, mapping);
if (mapping.getName() == null) {
return null;
}
//对mapping的name进行解析,如果开启的动态方法name要重新设置
parseActionName(mapping);
return mapping;
}
⑤
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping) throws ServletException {
//将request、response、actionMapping、parameter装到map中
Map<String, Object> extraContext = createContextMap(request, response, mapping, context); //(5.1)
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);//从request中获取值栈
boolean nullStack = stack == null;
if (nullStack) {
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
stack = ctx.getValueStack(); //从ActionContext中获取值栈
}
}
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代理对象
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
if (mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
proxy.execute();
}
if (!nullStack) {request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch (ConfigurationException e) {
// WW-2874 Only log error if in devMode
if(devMode) {
LOG.error("Could not find action or result", 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);
}
}
(5.1)
public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,ActionMapping mapping, ServletContext context) {
Map requestMap = new RequestMap(request);
Map params = new HashMap(request.getParameterMap());
Map session = new SessionMap(request);
Map application = new ApplicationMap(context);
Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
if (mapping != null) {
extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
}
return extraContext;
}
strutsPrepareAndExecuteFilter 方式启动struts2
①
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
Dispatcher dispatcher = init.initDispatcher(config);//等价于FilterDispatcher的createDispatcher(filterConfig)加dispatcher.init();
init.initStaticContentLoader(config, dispatcher); //等价于FilterDispatcher的 staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);//创建prepare对象;设置dispatcher和ServletContext属性
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);//创建execute对象;设置dispatcher和ServletContext属性
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);//action后缀扩展获取
postInit(dispatcher, filterConfig); //可以通过继承该类,然后重写此方法来定制自己的功能
} finally {
init.cleanup();
}
}
//struts2本身对这个方法是没有做任何操作的
protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
}
②
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);//设置request的编码格式和local
prepare.createActionContext(request, response);//创建ActionContext容器环境
prepare.assignDispatcherToThread();//将dispatcher保存到本地线程中
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
//如果请求的扩展后缀与我们设置的后缀像匹配,匹配失败就跳过struts的操作
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request); //request进行装饰
ActionMapping mapping = prepare.findActionMapping(request, response, true);//通过url获取对应的actionmapping
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
//判断请求是否struts请求,如果是进入struts的工作,不是就跳过
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);
}
}
} finally {
prepare.cleanupRequest(request);
}
}