zoukankan      html  css  js  c++  java
  • 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执行相应的拦截器、控制器、方法处理结果等操作
  • 相关阅读:
    加密算法使用(五):RSA使用全过程
    加密算法使用(四):AES的使用
    加密算法使用(三):用用BASE64
    加密算法使用(二):使用MD5加密字符串(另:byte数组转16进制自动补零方法写法)
    加密算法使用(一):用CRC32来压缩32uuid字符串
    【转载】阿里云ECS Linux服务器禁止某些IP访问
    【转载】Sqlserver数据库备份的几种方式
    【转载】网站域名备案相关流程介绍
    【转载】C#工具类:实现文件操作File的工具类
    【转载】 禁止国外IP访问你的网站
  • 原文地址:https://www.cnblogs.com/tandi19960505/p/9565627.html
Copyright © 2011-2022 走看看