FilterDispatcher是struts2的核心控制器,每一個請求都會進入FilterDispatcher對象的doFilter()方法。而後再struts.xml文件中根據action的name屬性進行匹配,找到要執行的action類和方法,執行完方法返回結果。 java
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個功能: web
(1)encoding和Local是web頁面上的重要屬性,也是web程序進行國際化il8n處理的核心參數。prepareDispatcherAndWrapRequest()設置encoding和Local參數。並對httpServletRequest進行必定的封裝 apache
(2)ActionContext的建立老是伴隨着valueStack的建立。valueStackFactory負責建立valueStack,併爲valueStack設置上下文環境;緊接着valueStack建立的就是ActionContext,而且ActionContext的建立以valueStack的上下文環境做爲參數。 session
valueStack的上下文環境與ActionContext的數據存儲空間是等同的。代碼證實: ctx = new ActionContext(stack.getContext()); app
(3)解析請求url,而且把值賦值給ActionMapping對象 jsp
(4)serviceAction執行action ide
如今咱們就重點分析解析url,serviceAction執行action後面再詳細分析 測試
二、getMapping()方法:ActionMapper接口的實現類 DefaultActionMapper的getMapping() this
/* * (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 url
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.三、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; } } } }註釋;主要是去除鼠標參數,橫豎座標。