FilterDispatcher是struts2的核心控制器,每个请求都会进入FilterDispatcher对象的doFilter()方法。然后再struts.xml文件中根据action的name属性进行匹配,找到要执行的action类和方法,执行完方法返回结果。
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 {//根据url取得对应的Action的配置信息 //看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存储在 ActionMapping对象中 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; } //如果找不到对应的action配置,则直接返回。比如你输入***.jsp等 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 { dispatcher.cleanUpRequest(request); try { ActionContextCleanUp.cleanUp(req); } finally { UtilTimerStack.pop(timerKey); } devModeOverride.remove(); } }
由于这个doFilter()是重点,主要是3个功能:
(1)encoding和Local是web页面上的重要属性,也是web程序进行国际化il8n处理的核心参数。prepareDispatcherAndWrapRequest()设置encoding和Local参数。并对httpServletRequest进行一定的封装
(2)ActionContext的创建总是伴随着valueStack的创建。valueStackFactory负责创建valueStack,并为valueStack设置上下文环境;紧接着valueStack创建的就是ActionContext,并且ActionContext的创建以valueStack的上下文环境作为参数。
valueStack的上下文环境与ActionContext的数据存储空间是等同的。代码证明: ctx = new ActionContext(stack.getContext());
(3)解析请求url,并且把值赋值给ActionMapping对象
(4)serviceAction执行action
现在我们就重点分析解析url,serviceAction执行action后面再详细分析
2、getMapping()方法:ActionMapper接口的实现类 DefaultActionMapper的getMapping()
/* * (non-Javadoc) * * @see org.apache.struts2.dispatcher.mapper.ActionMapper#getMapping(javax.servlet.http.HttpServletRequest) */ public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) { ActionMapping mapping = new ActionMapping();//创建一个ActionMapping String uri = getUri(request);//得到请求路径的URI,如:login.action int indexOfSemicolon = uri.indexOf(";");//修正url的带;jsessionid uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri; uri = dropExtension(uri, mapping);//删除扩展名,默认扩展名为action if (uri == null) { return null; } parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace handleSpecialParameters(request, mapping);//去掉特殊参数 //如果Action的name没有解析出来,直接返回 if (mapping.getName() == null) { return null; } parseActionName(mapping);//主要是处理name!add 格式的解析 return mapping; } protected ActionMapping parseActionName(ActionMapping mapping) { if (mapping.getName() == null) { return mapping; } if (allowDynamicMethodCalls) { // handle "name!method" convention. String name = mapping.getName(); int exclamation = name.lastIndexOf("!"); if (exclamation != -1) { mapping.setName(name.substring(0, exclamation));//提取左边为name mapping.setMethod(name.substring(exclamation + 1));//提取右边的method } } return mapping; }
注释:getMapping()方法返回ActionMapping类型的对象,该对象包含三个参数:Action的name、namespace和要调用的方法method
2.1查看ActionMapping对象:里面有name等等属性
public class ActionMapping { private String name; private String namespace; private String method; private String extension; private Map<String, Object> params; private Result result; }2.2getUri(request)获取请求路径:
/** * Gets the uri from the request * * @param request The request * @return The uri */ protected String getUri(HttpServletRequest request) { // handle http dispatcher includes. String uri = (String) request .getAttribute("javax.servlet.include.servlet_path"); if (uri != null) { return uri; } uri = RequestUtils.getServletPath(request); if (uri != null && !"".equals(uri)) { return uri; } uri = request.getRequestURI(); return uri.substring(request.getContextPath().length()); }注释:request.getContextPath() 其作用是获取当前的系统路径
我做了一个测试,先看看打印结果:
uRequestUtils.getServletPath(request) = /skip.action
request.getRequestURI() = /gaokao/skip.action
request.getContextPath() = /gaokao
不难看出getUri(request)是为了获得请求路径 /skip.action
2.3、dropExtension(uri, mapping)删除扩展名
protected List<String> extensions = new ArrayList<String>() {{ add("action"); add(""); }};
protected String dropExtension(String name, ActionMapping mapping) { if (extensions == null) { return name; } for (String ext : extensions) { if ("".equals(ext)) { int index = name.lastIndexOf('.'); if (index == -1 || name.indexOf('/', index) >= 0) { return name; } } else {//删除扩展名.action String extension = "." + ext; if (name.endsWith(extension)) { name = name.substring(0, name.length() - extension.length()); mapping.setExtension(ext); return name; } } } return null; }
extensions中默认的保存了action,其他事怎么加入的呢?
@Inject(StrutsConstants.STRUTS_ACTION_EXTENSION) public void setExtensions(String extensions) { if (extensions != null && !"".equals(extensions)) { List<String> list = new ArrayList<String>(); String[] tokens = extensions.split(","); for (String token : tokens) { list.add(token); } if (extensions.endsWith(",")) { list.add(""); } this.extensions = Collections.unmodifiableList(list); } else { this.extensions = null; } }原来是预处理, 通过注解在初始化的时候赋值,STRUTS_ACTION_EXTENSION = "struts.action.extension"
想想以前在struts.properties文件中设置struts.action.extension=action,原来是这的道理啊,哈哈。
2.4 parseNameAndNamespace(uri, mapping, configManager)从uri中解析出来name和namespace,并且把name和names赋值给mapping。
2.5 handleSpecialParameters(request, mapping)去除特殊参数:
public void handleSpecialParameters(HttpServletRequest request, ActionMapping mapping) { // handle special parameter prefixes. Set<String> uniqueParameters = new HashSet<String>(); Map parameterMap = request.getParameterMap(); for (Iterator iterator = parameterMap.keySet().iterator(); iterator .hasNext();) { String key = (String) iterator.next(); // Strip off the image button location info, if found if (key.endsWith(".x") || key.endsWith(".y")) { key = key.substring(0, key.length() - 2); } // Ensure a parameter doesn't get processed twice if (!uniqueParameters.contains(key)) { ParameterAction parameterAction = (ParameterAction) prefixTrie .get(key); if (parameterAction != null) { parameterAction.execute(key, mapping); uniqueParameters.add(key); break; } } } }注释;主要是去除鼠标参数,横竖坐标。