最近工做任務不是很緊,時間也不能白白浪費,之前經常使用的struts2框架源碼沒去了解過,因此就跟蹤一下struts2的整個執行過程.因爲本人也是抱着學習的態度來閱讀掩碼,若文章在表述和代碼方面若有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!但願能互相學習。java
我這裏的struts2源碼是maven導jar包來查看源碼的,這樣方便多了,能夠在IDE下查看源碼。pom.xml文件以下web
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>Struts_test_lishun</groupId> <artifactId>Struts_test_lishun</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.24</version> </dependency> </dependencies> </project>
1.實現從web.xml提及:apache
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <!-- 添加一個過濾器--> <filter> <filter-name>action2</filter-name> <!-- struts2處理過程的入口 --> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> <init-param> <param-name >struts.serve.static.browserCache</param-name> <param-value>true</param-value> </init-param> <!-- 初始化參數(訪問後綴爲.do),這裏的param-name均可以從後面提到的源碼中查看到,具體的其餘功能本人也不是所有了解, 有興趣的能夠本身再去深刻,--> <init-param> <param-name>struts.action.extension</param-name> <param-value>do</param-value> </init-param> </filter> <filter-mapping> <filter-name>action2</filter-name> <!-- 過濾全部的請求 --> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
2:開始查看StrutsPrepareAndExecuteFilter 過濾器tomcat
2-1:初始化:(tomcat一啓動就會執行到這裏)session
public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); Dispatcher dispatcher = null; try { //封裝filterConfig,便於對xml文件的操做 FilterHostConfig config = new FilterHostConfig(filterConfig); //初始化日誌 init.initLogging(config); //初始化dispatcher,struts2的核心類,大部分的操做都是在這裏完成的 dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config, dispatcher); //初始化當前類屬性:prepare 、execute(這兩個屬性會在doFilter裏設置ActionContext和encoding的值) prepare = new PrepareOperations(dispatcher); execute = new ExecuteOperations(dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); //這是是空函數。說是回調方法,這裏不太懂,有了解能夠給小弟指引下 postInit(dispatcher, filterConfig); } finally { if (dispatcher != null) { dispatcher.cleanUpAfterInit(); } init.cleanup(); } }
2-2:對上面init.initDispatcher(config)的跟蹤:app
public Dispatcher initDispatcher( HostConfig filterConfig ) { //這裏主要是對新建一個dispatcher,並加載配置文件中的初始化參數 Dispatcher dispatcher = createDispatcher(filterConfig); //初始化dispatcher dispatcher.init(); return dispatcher; }
2-2-1:dispatcher.init()跟蹤:框架
public void init() { if (configurationManager == null) { //把struts.xml文件進行封裝(默認是名字是struts) configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME); } try { init_FileManager(); //加載org/apache/struts2/default.properties init_DefaultProperties(); // [1] //加載struts-default.xml,struts-plugin.xml,struts.xml init_TraditionalXmlConfigurations(); // [2] //下面的初始化代碼。沒去研究,到此已經把web.xml,和struts.xml文件給加載進來了 init_LegacyStrutsProperties(); // [3] init_CustomConfigurationProviders(); // [5] init_FilterInitParameters() ; // [6] init_AliasStandardObjects() ; // [7] Container container = init_PreloadConfiguration(); //注入dispatcher container.inject(this); init_CheckWebLogicWorkaround(container); if (!dispatcherListeners.isEmpty()) { for (DispatcherListener l : dispatcherListeners) { l.dispatcherInitialized(this); } } errorHandler.init(servletContext); } catch (Exception ex) { if (LOG.isErrorEnabled()) LOG.error("Dispatcher initialization failed", ex); throw new StrutsException(ex); } }
初始化就在此告一段落了。maven
----------------------華麗的分割線-------------------------ide
3-1:訪問某個action,StrutsPrepareAndExecuteFilter對全部的的請求都會過濾,此事就跟蹤到;函數
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { //設置類屬性編碼和國際化 prepare.setEncodingAndLocale(request, response); //設置類屬性action的上下文 prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); //對request進行封裝,繼續跟蹤下去代碼會發現這裏的做用就是根據不一樣的請求返回不一樣request的封裝類(這裏就是用到裝飾者模式) request = prepare.wrapRequest(request); //返回ActionMapping:裏面有經過struts.xml文件經過反射獲取到action對應的類和方法 ActionMapping mapping = prepare.findActionMapping(request, response, true); //若返回的ActionMapping爲null,則表示不是調用action if (mapping == null) { boolean handled = execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { //這裏進行了不少操做,比較重要就是頁面參數值的注入,和執行action execute.executeAction(request, response, mapping); } } } finally { prepare.cleanupRequest(request); } }
3-2:追蹤代碼-生成映射action的映射:
ActionMapping mapping = prepare.findActionMapping(request, response, true); 在PrepareOperations中,主要執行ActionMapper的getMapping(..)方法 public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) { ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY); if (mapping == null || forceLookup) { try { //new一個ActionMapper實例並執行getMapping(..)方法 mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager()); if (mapping != null) { request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping); } } catch (Exception ex) { dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); } } return mapping; }
3-2-1:追蹤到ActionMapper的getMapping(..)方法;而ActionMapper是一個接口,方法由DefaultActionMapper類實現;
定位到DefaultActionMapper類的getMapping(..);
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) { ActionMapping mapping = new ActionMapping(); //獲取到請求的url String uri = RequestUtils.getUri(request); //截取url,把含有";"的後面的字符截取掉 int indexOfSemicolon = uri.indexOf(";"); uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri; //繼續截取url,url的後綴截取掉(如.action和.do等),在這裏就能夠看見extensions變量,struts默認的後綴名就是.action uri = dropExtension(uri, mapping); if (uri == null) { return null; } //從url獲取namespace和name並和mapping匹配 parseNameAndNamespace(uri, mapping, configManager); handleSpecialParameters(request, mapping); return parseActionName(mapping); }
3-3:如今返回到doFilter方法,追蹤到
execute.executeAction(request, response, mapping);方法;
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { dispatcher.serviceAction(request, response, mapping); }
最終調用的是Dispatcher的serviceAction(...)方法,
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { //封轉上下文環境,主要將request、params、session等Map封裝成爲一個上下文Map、 Map<String, Object> extraContext = createContextMap(request, response, mapping); // 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); //從mapping(這裏是已經封裝好的mapping,struts.xml與請求url共同映射出來的數據)中獲取命名空間 String namespace = mapping.getNamespace(); //從mapping中獲取action的name String name = mapping.getName(); //從mapping中獲取請求方法(是獲取動態的請求方法:在url後面加上 ‘!+方法名’) String method = mapping.getMethod(); //生成action的代理類,執行頁面參數值和根據反射執行請求方法 ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy( namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); //若是配置文件中執行的這個action配置了result,就直接轉到result 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) { logConfigurationException(request, e); sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { if (handleException || devMode) { sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } else { throw new ServletException(e); } } finally { UtilTimerStack.pop(timerKey); } }
3-3-1:追蹤 ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
建立ActionProxy是由ActionProxyFactory實現類完成
DefaultActionProxyFactory createActionProxy方法
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) { ActionInvocation inv = new DefaultActionInvocation(extraContext, true); container.inject(inv); //StrutsActionProxyFactory 的createActionProxy方法, StrutsActionProxyFactory是DefaultActionProxyFactory的子類,調用的是StrutsActionProxyFactory的方法 return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); }
--------- StrutsActionProxyFactory-----------------
public class StrutsActionProxyFactory extends DefaultActionProxyFactory { @Override public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) { StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); container.inject(proxy); //繼續跟蹤代碼: prepare()調用的是父類DefaultActionProxy的prepare()方法 proxy.prepare(); return proxy; } }
--------------------DefaultActionProxy-------------------
protected void prepare() { String profileKey = "create DefaultActionProxy: "; try { UtilTimerStack.push(profileKey); config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName); if (config == null && unknownHandlerManager.hasUnknownHandlers()) { config = unknownHandlerManager.handleUnknownAction(namespace, actionName); } if (config == null) { throw new ConfigurationException(getErrorMessage()); } //獲取執行method爲空的方法名, 若爲空則默認設置爲"execute" resolveMethod(); if (!config.isAllowedMethod(method)) { throw new ConfigurationException("Invalid method: " + method + " for action " + actionName); } //建立action invocation.init(this); } finally { UtilTimerStack.pop(profileKey); } }
----------------- invocation.init(this)--------------
public void init(ActionProxy proxy) { this.proxy = proxy; Map<String, Object> contextMap = createContextMap(); // Setting this so that other classes, like object factories, can use the ActionProxy and other // contextual information to operate ActionContext actionContext = ActionContext.getContext(); if (actionContext != null) { actionContext.setActionInvocation(this); } //這裏開始建立action createAction(contextMap); if (pushAction) { stack.push(action); contextMap.put("action", action); } invocationContext = new ActionContext(contextMap); invocationContext.setName(proxy.getActionName()); // get a new List so we don't get problems with the iterator if someone changes the list List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors()); interceptors = interceptorList.iterator(); }
至此action就建立成功了,而後就是給action的屬性賦值和執行action裏的方法
3-4:從新回到Dispatcher的serviceAction(...)方法,當mapping.getResult() != null此時就跟蹤到
proxy.execute();方法
ActionProxy是接口,execute()由StrutsActionProxy實現;
public String execute() throws Exception { ActionContext previous = ActionContext.getContext(); ActionContext.setContext(invocation.getInvocationContext()); try { //開始執行action和注入屬性值,由DefaultActionInvocation實現這個方法 return invocation.invoke(); } finally { if (cleanupContext) ActionContext.setContext(previous); } }
---------------DefaultActionInvocation--------------------------------
.....其餘代碼省略 try { //最主要執行了intercept方法,這裏就是執行設置action的屬性值和執行方法,由接口Interceptor的抽象類AbstractInterceptor的子類MethodFilterInterceptor執行intercept方法,
//因爲AbstractInterceptor的實現類不少,因此這段代碼會執行不少次,至於爲何會執行屢次,本人也還在研究,如有仁兄瞭解能夠給小弟一點指引 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); } finally { UtilTimerStack.pop(interceptorMsg); } .....其餘代碼省略
---------------MethodFilterInterceptor---------------------------------
@Override public String intercept(ActionInvocation invocation) throws Exception { if (applyInterceptor(invocation)) { //執行了ParametersInterceptor的doIntercept方法, return doIntercept(invocation); } return invocation.invoke(); }
---------------ParametersInterceptor---------------------
@Override public String doIntercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); if (!(action instanceof NoParameters)) { ActionContext ac = invocation.getInvocationContext(); final Map<String, Object> parameters = retrieveParameters(ac); if (LOG.isDebugEnabled()) { LOG.debug("Setting params " + getParameterLogMap(parameters)); } if (parameters != null) { Map<String, Object> contextMap = ac.getContextMap(); try { ReflectionContextState.setCreatingNullObjects(contextMap, true); ReflectionContextState.setDenyMethodExecution(contextMap, true); ReflectionContextState.setReportingConversionErrors(contextMap, true); ValueStack stack = ac.getValueStack(); //給action的參數賦值 setParameters(action, stack, parameters); } finally { ReflectionContextState.setCreatingNullObjects(contextMap, false); ReflectionContextState.setDenyMethodExecution(contextMap, false); ReflectionContextState.setReportingConversionErrors(contextMap, false); } } } //最後執行action return invocation.invoke(); }
至此,整個action的請求就結束了;