struts2工作原理

废话不多说

以下为struts工作原理官方参考图

StrutsPrepareAndExecuteFilter - init方法

因为StrutsPrepareAndExecuteFilter是一个filter,随着服务器的启动而初始化,init方法也会仅此一次地被调用

public void init(FilterConfig filterConfig) throws ServletException {
    InitOperations init = createInitOperations();
    Dispatcher dispatcher = null;
    try {
        FilterHostConfig config = new FilterHostConfig(filterConfig); // 加载web配置
        init.initLogging(config);
        dispatcher = init.initDispatcher(config);  // 初始化dispatcher,包括加载default-struts.xml和struts.xml等等
        init.initStaticContentLoader(config, dispatcher); 

		/* 以下为初始化对象实例操作 */

		// PrepareOperations中包含以下操作
		// 1. 创建ActionContext,并绑定到当前线程
		// 2. 将dispatcher绑定到当前线程
		// 所以每一条线程只有一个 ActionContext 和一个 dispatcher
        prepare = createPrepareOperations(dispatcher); 

		// 静态资源请求操作
        execute = createExecuteOperations(dispatcher); 
        this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);

		//该方法没做任何处理
		//源码注释:Callback for post initialization
        postInit(dispatcher, filterConfig); 
    } finally {
        if (dispatcher != null) {
            dispatcher.cleanUpAfterInit();
        }
        init.cleanup();
    }
}

StrutsPrepareAndExecuteFilter - doFilter方法

该方法每次请求来到都会被调用,服务器会将当前请求封装成request和response对象作为参数传入(FilterChain为过滤器链,这里不加以说明)

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    try {
        String uri = RequestUtils.getUri(request);
        if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
            LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
            chain.doFilter(request, response);
        } else {
            LOG.trace("Checking if {} is a static resource", uri);
            boolean handled = execute.executeStaticResourceRequest(request, response);
            if (!handled) {
                LOG.trace("Assuming uri {} as a normal action", uri);
                prepare.setEncodingAndLocale(request, response);
				
				// 创建 ActionContext ,并绑定到当前线程上
				// 使用 static ThreadLocal<ActionContext> actionContext = new ThreadLocal<>();
                prepare.createActionContext(request, response);

				// 将 dispatcher 绑定到当前线程上:
				// 使用 static ThreadLocal<Dispatcher> instance = new ThreadLocal<>();
                prepare.assignDispatcherToThread();
				
                request = prepare.wrapRequest(request);

				// 获取 ActionMapping 对象,该对象封装了以下内容
				/* 假设当前请求为:http://localhost:8080/ssh/list.action

				    private String name; 			// 当前请求的名称,即为 list
				    private String namespace; 		// 当前请求的名称空间,即为 /
				    private String method; 			// 处理方法,null
				    private String extension; 		// 访问后缀,即action
				    private Map<String, Object> params; //参数,无
				    private Result result; 			// null
				*/
                ActionMapping mapping = prepare.findActionMapping(request, response, true);  // 【1】

                if (mapping == null) {
                    LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
                    chain.doFilter(request, response);
                } else {
                    LOG.trace("Found mapping {} for {}", mapping, uri);
					
					// 【2】往下执行操作
                    execute.executeAction(request, response, mapping);
                }
            }
        }
    } finally {
        prepare.cleanupRequest(request);
    }
}

【1】 - findActionMapping方法

public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
    ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
	
	// 一开始request对应的STRUTS_ACTION_MAPPING_KEY属性并没有值,所以为null
    if (mapping == null || forceLookup) { 
        try {

			// dispatcher.getConfigurationManager()中存放着struts.xml配置文件的信息
			// 如 struts.xml 中的package标签中定义信息(由PackageConfig封装)
            mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());

            if (mapping != null) {

				// 将当前获取到的 ActionMapping 存放到request对应的STRUTS_ACTION_MAPPING_KEY属性中
                request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping); 
            }
        } catch (Exception ex) {
            if (dispatcher.isHandleException() || dispatcher.isDevMode()) {
                dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
            }
        }
    }

    return mapping;
}

【2】 - executeAction方法, 以下为executeAction执行过程中要注意的方法

public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
        throws ServletException {

	// 将 request 、 session、 parameters封装到 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) { //一开始值栈为null

		// 获取绑定在当前线程上的ActionContext(map)
        ActionContext ctx = ActionContext.getContext();

        if (ctx != null) {
			//  获取值栈对象
            stack = ctx.getValueStack();
        }
    }

    if (stack != null) {
		// 注意:valueStackFactory.createValueStack(stack)返回的是list栈
        extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
    }

    String timerKey = "Handling request from Dispatcher";
    try {
        UtilTimerStack.push(timerKey);

		// 从 mapping 中获处理请求的action信息
        String namespace = mapping.getNamespace(); 
        String name = mapping.getName();
        String method = mapping.getMethod();

		
		// 【注意】
		// 【3】ActionProxy是处理当前请求的控制器的代理类
		// 包含处理请求的方法名称 和 处理请求的action对象 以及 调用action的invocation实例 等成员
        ActionProxy proxy = 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 {
			// 【注意】
			// 这里会将执行权交给ActionProxy中的invocation对象
			// 随后invocation对象会按顺序调用 拦截器 -> 控制器 -> 返回相应字符串 -> 拦截器
			// 到此为止往后的操作就是封装相应输信息到response相应浏览器,或继续执行剩余过滤器或servlet
            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) {
        e.printStackTrace();
        if (handleException || devMode) {
            sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
        } else {
            throw new ServletException(e);
        }
    } finally {
        UtilTimerStack.pop(timerKey);
    }
}

【3】- createActionProxy方法,以下为createActionProxy执行过程中要注意的方法

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());
        }

		// 解析出处理请求的方法名称,如果没有则使用默认处理方法execute
        resolveMethod();

        if (config.isAllowedMethod(method)) {

			// 创建处理请求的 action 并将其压入栈顶(list栈)
            invocation.init(this);
        } else {
            throw new ConfigurationException(prepareNotAllowedErrorMessage());
        }
    } finally {
        UtilTimerStack.pop(profileKey);
    }
}

总结

  1. 核心过滤器接受请求,并以request和ConfigurationManager作为参数返回处理当前请求的ActionMapping的信息类实例
  2. 随后核心过滤器会通过ActionMapping中的信息创建出一个控制器代理类ActionProxy,该类包含处理当前请求的一切信息,如处理请求的 控制器action,方法名称,名称控制,执行对象等等
  3. 往下继续执行时ActionProxy会将执行权,交给ActionInvocation,由ActionInvocation执行相应的拦截器、控制器、方法处理结果等操作
原文地址:https://www.cnblogs.com/tandi19960505/p/9565627.html