Like starting struts2, it also has an entry, that is, the doFilter method of org. apache. struts2.dispatcher. ng. filter. StrutsPrepareAndExecuteFilter.
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); prepare.createActionContext(request, response); prepare.assignDispatcherToThread();if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {chain.doFilter(request, response);} else {request = prepare.wrapRequest(request);ActionMapping mapping = prepare.findActionMapping(request, response, true);if (mapping == null) {boolean handled = execute.executeStaticResourceRequest(request, response);if (!handled) {chain.doFilter(request, response);}} else {execute.executeAction(request, response, mapping);}} } finally { prepare.cleanupRequest(request); } }
This part includes setting encoding, creating actioncontext, and setting the Distance variable to the local copy instance of this thread.
private static ThreadLocal
instance = new ThreadLocal
();
Next, obtain actionmapping. This actionmapping matches the settings in the configuration file according to the uri of our request to get the corresponding action.
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) { ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY); if (mapping == null || forceLookup) { try { 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, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); } } return mapping; }
Do you still remember the principle of dispatcher. getContainer (). getInstance (ActionMapper. class? As mentioned in the previous article, in the initialization of struts2, the container has been created successfully, and the container contains the map of factories, each item is composed of a Key consisting of name and type and the Value of the corresponding object factory. Let's see how getMapping is implemented.
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) { ActionMapping mapping = new ActionMapping(); String uri = getUri(request); int indexOfSemicolon = uri.indexOf(";"); uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri; uri = dropExtension(uri, mapping); if (uri == null) { return null; } parseNameAndNamespace(uri, mapping, configManager); handleSpecialParameters(request, mapping); if (mapping.getName() == null) { return null; } parseActionName(mapping); 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)); mapping.setMethod(name.substring(exclamation + 1)); } } return mapping; }
protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) { String namespace, name; int lastSlash = uri.lastIndexOf("/"); if (lastSlash == -1) { namespace = ""; name = uri; } else if (lastSlash == 0) { // ww-1046, assume it is the root namespace, it will fallback to // default // namespace anyway if not found in root namespace. namespace = "/"; name = uri.substring(lastSlash + 1); } else if (alwaysSelectFullNamespace) { // Simply select the namespace as everything before the last slash namespace = uri.substring(0, lastSlash); name = uri.substring(lastSlash + 1); } else { // Try to find the namespace in those defined, defaulting to "" Configuration config = configManager.getConfiguration(); String prefix = uri.substring(0, lastSlash); namespace = ""; boolean rootAvailable = false; // Find the longest matching namespace, defaulting to the default for (Object cfg : config.getPackageConfigs().values()) { String ns = ((PackageConfig) cfg).getNamespace(); if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) { if (ns.length() > namespace.length()) { namespace = ns; } } if ("/".equals(ns)) { rootAvailable = true; } } name = uri.substring(namespace.length() + 1); // Still none found, use root namespace if found if (rootAvailable && "".equals(namespace)) { namespace = "/"; } } if (!allowSlashesInActionNames && name != null) { int pos = name.lastIndexOf('/'); if (pos > -1 && pos < name.length() - 1) { name = name.substring(pos + 1); } } mapping.setNamespace(namespace); mapping.setName(name); }
This code should be simple. It is nothing more than parsing the request uri, obtaining its namespace, name, method, and so on, and setting it to actionmapping. After obtaining actionmapping, the system starts to process the request.
execute.executeAction(request, response, mapping);
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { dispatcher.serviceAction(request, response, servletContext, mapping); }
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { Map
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(); } //................ }
The previous section deals with actioncontext and valuestack. In createContextMap, the native request and response are encapsulated into the map type, and then an overloaded method of createContextMap is called to encapsulate these native requests, session and encapsulated map requests and sessions are put into a large map. If you want to obtain these objects in action, you can also.
For example, Map Request = (Map ) ActionContext. getContext (). get ("request"); the string used for Map-type requests is "request". If you want to obtain a native request, it is also possible, but not "request", but StrutsStatics. HTTP_REQUEST:
Httprequest = (HttpServletRequest) ActionContext. getContext (). get (StrutsStatics. HTTP_REQUEST); In map, the key is StrutsStatics. HTTP_REQUEST.
But you may ask: why can the session be obtained like this? Map Session = ActionContext. getContext (). getSession (); but does the request need to pass the get method?
This is because the getRequest method is not provided in ActionContext, and you do not know why it is not provided. In fact, getSession () is implemented through the get method.
Next, obtain the ing namespace, action name, and action method name. Let's execute the action. Do you just need to know this. Then obtain the proxy of an action: ActionProxy proxy.
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
extraContext, boolean executeResult, boolean cleanupContext) { ActionInvocation inv = new DefaultActionInvocation(extraContext, true); container.inject(inv); return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); }
Create actioninvocation. The container IOC mechanism is used for injection.
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); proxy.prepare(); return proxy; }
In this case, the action proxy object is created and will be executed later.
protected void prepare() { String profileKey = "create DefaultActionProxy: "; try { UtilTimerStack.push(profileKey); config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName); //......... resolveMethod(); if (!config.isAllowedMethod(method)) { throw new ConfigurationException("Invalid method: " + method + " for action " + actionName); } invocation.init(this); } //......... }
public void init(ActionProxy proxy) { this.proxy = proxy; Map
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); } 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
interceptorList = new ArrayList
(proxy.getConfig().getInterceptors()); interceptors = interceptorList.iterator(); }
The preceding settings are also used to set the context. I will not analyze the settings.
Actioninvocation is initialized here. After actioninvocation, We need to execute a series of interceptor and real actions, so these things must be initialized.
Return to serviceAction. Because we have obtained actionmapping and actionproxy. Then you can execute interceptor and action.
Their execution is to execute the next interceptor if there is an interceptor. If there is no interceptor, they will execute the real action and adopt a responsibility chain mode. This part is very simple and I will not analyze it. After the action is executed, each interceptor is returned in turn, and then each valve in each container of the web server (for example, StandardContextValve of the StandardContext container ). This completes the entire process.