Struts2源码初探

来源:互联网 发布:顶管计算软件 编辑:程序博客网 时间:2024/05/08 13:41

             主要是对最新版本Struts2.3.1进行研究,之前从网上搜了一些文章,不过好些都是以前版本的,对比了一下最新版本的源码,最新版本对之前版本进行了重构,对比别人以前版本的文章还是能读懂的,现在让我们来看一下Struts2.3.1的源码。

先看一个从网上copy的struts2的体系结构图:

这图应该是以前版本的,目前struts2的核心控制器是StrutsPrepareAndExecuteFilter,从客户端发来的请求首先经过一系列的过滤器,这里我们可以配置一个ActionContextCleanUp的可选过滤器,它对于struts2和其他框架的继承很有帮助比如:SiteMesh plugin,最后核心控制器StrutsPrepareAndExecuteFilter被调用,我们来看一下核心控制器被调用后都做了什么:

StrutsPrepareAndExecuteFilter的init方法:

public void init(FilterConfig filterConfig) throws ServletException {        InitOperations init = new InitOperations();        try {            FilterHostConfig config = new FilterHostConfig(filterConfig);            init.initLogging(config);//创建dispacher,并将初始化信息传入对象中,并执行dispatcher的init方法            Dispatcher dispatcher = init.initDispatcher(config);            init.initStaticContentLoader(config, dispatcher);            prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);            execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);            postInit(dispatcher, filterConfig);        } finally {            init.cleanup();        }    }

dispatcher的init方法是读取一些配置文件,我们进入到init方法中:

public void init() {    if (configurationManager == null) {    configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);    }        try {            init_FileManager();            init_DefaultProperties(); // [1]读取properties配置文件            init_TraditionalXmlConfigurations(); // [2]读取Struts-default.xml和struts.xml文件            init_LegacyStrutsProperties(); // [3]            init_CustomConfigurationProviders(); // [5]            init_FilterInitParameters() ; // [6]            init_AliasStandardObjects() ; // [7]            Container container = init_PreloadConfiguration();            container.inject(this);            init_CheckWebLogicWorkaround(container);            if (!dispatcherListeners.isEmpty()) {                for (DispatcherListener l : dispatcherListeners) {                    l.dispatcherInitialized(this);                }            }        } catch (Exception ex) {            if (LOG.isErrorEnabled())                LOG.error("Dispatcher initialization failed", ex);            throw new StrutsException(ex);        }    }


客户端每发送一次请求,就会调用StrutsPrepareAndExecute的doFilter方法,我们用myeclipse的debug模式看一下处理请求的大致流程:

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {       //将request、response分别转换为        //HttpServletRequest、HttpServletResponse类型        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        try { //设置编码格式和语言            prepare.setEncodingAndLocale(request, response); //创建ActionContext对象            prepare.createActionContext(request, response);            prepare.assignDispatcherToThread();if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {chain.doFilter(request, response);} else {//根据不同的contentType使用不同的Request封装request = prepare.wrapRequest(request);//findActionMapping中通过ActionMapper创建新的ActionMapping对象//创建的过程包括对uri的解析,并将解析后的namespace和name、method等设置到mapping中ActionMapping mapping = prepare.findActionMapping(request, response, true);if (mapping == null) {boolean handled = execute.executeStaticResourceRequest(request, response);if (!handled) {chain.doFilter(request, response);}} else {//正式调用actionexecute.executeAction(request, response, mapping);}}        } finally {            prepare.cleanupRequest(request);        }    }


我们进入executeAction方法中:

/**     * Executes an action     * @throws ServletException     */    public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {        dispatcher.serviceAction(request, response, servletContext, mapping);    }


它调用了dispatcher的serviceAction方法,我们接着进入:

 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,                              ActionMapping mapping) throws ServletException {.............省略代码...........        String timerKey = "Handling request from Dispatcher";        try {            UtilTimerStack.push(timerKey);//得到namespace、name、method            String namespace = mapping.getNamespace();            String name = mapping.getName();            String method = mapping.getMethod();            Configuration config = configurationManager.getConfiguration();//创建Action的代理对象ActionProxy对象,通过它完成action的调用            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();            }           .............省略代码............    }


我们来看一下ActionProxy对象的创建过程,进入到createActionProxy方法中:

 public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext);

看到这傻眼了吧,还好之前学了设计模式,典型的Template Method模式,我们去它的子类找实现,发现调用的DefaultActionProxyFactory中的方法:

 public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {        //创建ActionInvocation对象        ActionInvocation inv = new DefaultActionInvocation(extraContext, true);        container.inject(inv);        return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);    }

我们接着进入createActionProxy方法中:

 public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {        DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);        container.inject(proxy);        proxy.prepare();        return proxy;    }

接着调用了DefaultActionProxy的prepare方法,进入:

 @Override    protected void prepare() {        super.prepare();    }

调用基类的prepare方法:

 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());            }            resolveMethod();            if (!config.isAllowedMethod(method)) {                throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);            }//调用invocation的init方法创建action            invocation.init(this);        } finally {            UtilTimerStack.pop(profileKey);        }    }

进入到init方法中:

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集合中        List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());        interceptors = interceptorList.iterator();    }


我们接着看invocation是如何创建action的,进入createAction方法中:

 protected void createAction(Map<String, Object> contextMap) {        // load action        String timerKey = "actionCreate: " + proxy.getActionName();        try {            UtilTimerStack.push(timerKey); //这儿默认建立Action是StrutsObjectFactory,实际中我使用的时候都是使用Spring创建的Action,这个时候使用的是SpringObjectFactory            action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);        } catch (InstantiationException e) {            throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());        } ...........省略代码..........    }


到这我们应该就能确定action已经创建完毕,进入buildAction内部是通过反射机制创建的action,这里就不再详细说明,我们接着回到dispatcher的serviceAction方法中,创建完ActionProxy对象后,通过debug模式发现调用了ActionProxy的execute方法,进入:调用了StrutsActionProxy的execute方法,方法中又调用了DefaultActionInvocation的invoke方法,进入Invoke方法中:

 public String invoke() throws Exception {        String profileKey = "invoke: ";        try {            UtilTimerStack.push(profileKey);            if (executed) {                throw new IllegalStateException("Action has already executed");            }//首先经过一系列的拦截器            if (interceptors.hasNext()) {                final InterceptorMapping interceptor = interceptors.next();                String interceptorMsg = "interceptor: " + interceptor.getName();                UtilTimerStack.push(interceptorMsg);                try {                                resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);                            }                finally {                    UtilTimerStack.pop(interceptorMsg);                }            } else {//拦截器执行完后执行action相应的方法                resultCode = invokeActionOnly();            }            // this is needed because the result will be executed, then control will return to the Interceptor, which will            // return above and flow through again            if (!executed) {//在结果返回之前调用preResultListeners                if (preResultListeners != null) {                    for (Object preResultListener : preResultListeners) {                        PreResultListener listener = (PreResultListener) preResultListener;                        String _profileKey = "preResultListener: ";                        try {                            UtilTimerStack.push(_profileKey);                            listener.beforeResult(this, resultCode);                        }                        finally {                            UtilTimerStack.pop(_profileKey);                        }                    }                }                // now execute the result, if we're supposed to                if (proxy.getExecuteResult()) {//对返回的结果做出响应                    executeResult();                }                executed = true;            }            return resultCode;        }        finally {            UtilTimerStack.pop(profileKey);        }    }


进入到invokeActionOnly方法中发现其调用了DefaultActionInvocation的invokeAction方法:

 protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {........省略代码........            if (!methodCalled) {//通过反射调用action中的方法返回字符串                methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);            }//返回一个字符串            return saveResult(actionConfig, methodResult);        }...............省略代码..........    }


结果返回我我们接着看上面DefaultActionInvocation的invoke方法中,result返回后调用executeResult方法:

 private void executeResult() throws Exception {        result = createResult();        String timerKey = "executeResult: " + getResultCode();        try {            UtilTimerStack.push(timerKey);            if (result != null) {//对结果进行处理,跳转到相应的页面                result.execute(this);            } else if (resultCode != null && !Action.NONE.equals(resultCode)) {                throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()                        + " and result " + getResultCode(), proxy.getConfig());            } else {                if (LOG.isDebugEnabled()) {                    LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());                }            }        } finally {            UtilTimerStack.pop(timerKey);        }    }

再调用完result.execute(this)方法后还要以倒叙的顺序执行一遍拦截器。Ok,一个Struts2的请求流程基本上就结束了。等以后仔细研究后再补充吧





 



 

 

 

原创粉丝点击