Struts2运行过程以及StrutsPrepareAndExecuteFilter源码阅读

来源:互联网 发布:淘宝直通车软件 编辑:程序博客网 时间:2024/04/29 18:30

Struts2运行过程

1.Struts2启动过程

Created with Raphaël 2.1.0Struts2启动过程TomcatTomcatStrutsPrepareAndExcuteFilterStrutsPrepareAndExcuteFilterInitOperationsInitOperationsDispatcherDispatcherinit()initDispatcher()init()

在Tomcat启动的时候对Struts2 访问的过程:
1.先访问StrutsPrepareAndExcuteFilter类中的init()方法
2.在init方法中调用InitOperations的initDispatcher()方法
3.调用Dispatcher中的init()方法

最终在init()方法中加载
1.default.properties 默认的常量配置文件
2.struts-default.xml 内部的xml
3.struts-plugin.xml 插件机制的xml文件
4.struts.xml 自己写的xml文件

注:后面三个xml文件的did约束是一样的,如果出现相同的项目后者覆盖前者

2.action请求时的过程

当我们在浏览器输入一个url进行action的访问时,tomcat会访问web.xml配置文件,又因为我们在web.xml这样定义拦截器:
<filter>    <filter-name>struts2</filter-name>    <filterclass>org.apache.struts2.dispatcher.ng.    filter.StrutsPrepareAndExecuteFilter</filter-class></filter> 

所以Struts2将进行StrutsPrepareAndExecuteFilter类的初始化

public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter

可以看到StrutsPrepareAndExecuteFilter实现了StrutsStatics 以及 Filter接口
下面看看StrutsStatics以及Filter接口声明了什么

public interface StrutsStatics {    /**     * Constant for the HTTP request object.     */    public static final String HTTP_REQUEST = "com.opensymphony.xwork2.dispatcher.HttpServletRequest";    /**     * Constant for the HTTP response object.     */    public static final String HTTP_RESPONSE = "com.opensymphony.xwork2.dispatcher.HttpServletResponse";    /**     * Constant for an HTTP {@link javax.servlet.RequestDispatcher request dispatcher}.     */    public static final String SERVLET_DISPATCHER = "com.opensymphony.xwork2.dispatcher.ServletDispatcher";    /**     * Constant for the {@link javax.servlet.ServletContext servlet context} object.     */    public static final String SERVLET_CONTEXT = "com.opensymphony.xwork2.dispatcher.ServletContext";    /**     * Constant for the JSP {@link javax.servlet.jsp.PageContext page context}.     */    public static final String PAGE_CONTEXT = "com.opensymphony.xwork2.dispatcher.PageContext";    /** Constant for the PortletContext object */    public static final String STRUTS_PORTLET_CONTEXT = "struts.portlet.context";    /**     * Set as an attribute in the request to let other parts of the framework know that the invocation is happening inside an     * action tag     */    public static final String STRUTS_ACTION_TAG_INVOCATION= "struts.actiontag.invocation";}

从源码中我们可以看出在StrutsStatics 定义了一堆常量
从JavaDoc中我们可以了解到

/** * Constants used by Struts. The constants can be used to get or set objects * out of the action context or other collections. * * <p/> * * Example: * <ul><code>ActionContext.getContext().put(HTTP_REQUEST, request);</code></ul> * <p/> * or * <p/> * <ul><code> * ActionContext context = ActionContext.getContext();<br> * HttpServletRequest request = (HttpServletRequest)context.get(HTTP_REQUEST);</code></ul> */

这些常量是用来对对象进行静态注入。

在StrutsStatics 中我们看出是一个常量接口,那么在Filter接口中又有什么方法呢?

public interface Filter {    //初始化    public void init(FilterConfig filterConfig) throws ServletException;    //执行过滤    public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;    //销毁    public void destroy();}

通过Filter接口我们可以了解到filter的生命周期init-doFilter-destroy
下面来分析StrutsPrepareAndExecuteFilter类中的init方法

1.init()

public void init(FilterConfig filterConfig) throws ServletException {        InitOperations init = new InitOperations();        Dispatcher dispatcher = null;        try { //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中             FilterHostConfig config = new FilterHostConfig(filterConfig);            // 初始化struts内部日志             init.initLogging(config);   //创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源            dispatcher = init.initDispatcher(config);            init.initStaticContentLoader(config, dispatcher);   //初始化类属性:prepare 、execute              prepare = new PrepareOperations(dispatcher);            execute = new ExecuteOperations(dispatcher);            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);   //回调空的postInit方法               postInit(dispatcher, filterConfig);        } finally {            if (dispatcher != null) {                dispatcher.cleanUpAfterInit();            }            init.cleanup();        }    }

下面着重研究dispatcher = init.initDispatcher(config);

public Dispatcher initDispatcher( HostConfig filterConfig ) {        Dispatcher dispatcher = createDispatcher(filterConfig);        dispatcher.init();        return dispatcher;    }private Dispatcher createDispatcher( HostConfig filterConfig ) {        Map<String, String> params = new HashMap<String, String>();        for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {            String name = (String) e.next();            String value = filterConfig.getInitParameter(name);            params.put(name, value);        }        return new Dispatcher(filterConfig.getServletContext(), params);    }

在创建dispatcher的时候会调用了一个HostCofig,那么HostCofig是干嘛的呢

public interface HostConfig {    /**     * @param key The parameter key     * @return The parameter value     */    String getInitParameter(String key);    /**     * @return A list of parameter names     */    Iterator<String> getInitParameterNames();    /**     * @return The servlet context     */    ServletContext getServletContext();}

三个方法分别是:
1.通过键获得值
2.获取属性名称的迭代器
3.获得servlet上下文
通过快捷键crlt+t来查看实现HostConfig接口的类

/** * Host configuration that wraps FilterConfig * FilterConfig的包装类  */public class FilterHostConfig implements HostConfig {    private FilterConfig config;    //构造方法    public FilterHostConfig(FilterConfig config) {        this.config = config;    }    //根据init-param配置的param-name获取param-value的值     public String getInitParameter(String key) {        return config.getInitParameter(key);    }    //返回初始化参数名的List的迭代器    public Iterator<String> getInitParameterNames() {        return MakeIterator.convert(config.getInitParameterNames());    }    //返回ServletContext    public ServletContext getServletContext() {        return config.getServletContext();    }}
/** * Host configuration that just holds a ServletContext * 只存在ServletContext的配置类 */public class ListenerHostConfig implements HostConfig {    private ServletContext servletContext;    //构造函数    public ListenerHostConfig(ServletContext servletContext) {        this.servletContext = servletContext;    }    public String getInitParameter(String key) {        return null;    }    public Iterator<String> getInitParameterNames() {        return Collections.<String>emptyList().iterator();    }    //返回ServletContext    public ServletContext getServletContext() {        return servletContext;      }}
/** * Host configuration that wraps a ServletConfig * ServletConfig包装类 */public class ServletHostConfig implements HostConfig {    private ServletConfig config;    //构造函数    public ServletHostConfig(ServletConfig config) {        this.config = config;    }    //根据init-param配置的param-name获取param-value的值     public String getInitParameter(String key) {        return config.getInitParameter(key);    }    //返回初始化参数名的List的迭代器    public Iterator<String> getInitParameterNames() {        return MakeIterator.convert(config.getInitParameterNames());    }    //返回ServletContext    public ServletContext getServletContext() {        return config.getServletContext();    }}

在getInitParameterNames对配置文件中的属性名进行封装,并包装成迭代器进行返回

以上三个方法都是对配置文件进行解析,从而进行创建dispatch那么createDispatcher(filterConfig)是做了什么

private Dispatcher createDispatcher( HostConfig filterConfig ) {        Map<String, String> params = new HashMap<String, String>();        //根据迭代器中的值来对属性进行赋值封装成为一个Map,然后根据servlet上下文和参数Map构造Dispatcher         for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {            String name = (String) e.next();            String value = filterConfig.getInitParameter(name);            params.put(name, value);        }        return new Dispatcher(filterConfig.getServletContext(), params);    }

创建完了Dispatcher接着就要进行 dispatcher.init();初始化方法
而 Dispatcher初始化的过程,加载struts2的相关配置文件,将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.xml,……

/**     * Load configurations, including both XML and zero-configuration strategies,     * and update optional settings, including whether to reload configurations and resource files.     * 加载配置文件包括如下...     */    public void init() {        //如果configurationManager 不存在则创建configurationManager         if (configurationManager == null) {            configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);        }        try {            //初始化文件管理器,用于加载配置文件            init_FileManager();            //加载org/apache/struts2/default.properties            init_DefaultProperties(); // [1]            init_TraditionalXmlConfigurations(); // [2]            init_LegacyStrutsProperties(); // [3]            //用户自己实现的ConfigurationProviders类             init_CustomConfigurationProviders(); // [5]            //Filter的初始化参数              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);                }            }            errorHandler.init(servletContext);        } catch (Exception ex) {            if (LOG.isErrorEnabled())                LOG.error("Dispatcher initialization failed", ex);            throw new StrutsException(ex);        }    }
 private void init_DefaultProperties() {        configurationManager.addContainerProvider(new DefaultPropertiesProvider());    }

addContainerProvider调用xwork包中的方法

 public void addContainerProvider(ContainerProvider provider) {        if (!containerProviders.contains(provider)) {            containerProviders.add(provider);            providersChanged = true;        }    }

想providers容器添加一个provider那么DefaultPropertiesProvider中又有什么呢

/** * Loads the default properties, separate from the usual struts.properties loading */public class DefaultPropertiesProvider extends PropertiesConfigurationProvider {    public void destroy() {    }    public void init(Configuration configuration) throws ConfigurationException {    }    public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {        try {            PropertiesSettings defaultSettings = new PropertiesSettings("org/apache/struts2/default");            loadSettings(props, defaultSettings);        } catch (Exception e) {            throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);        }    }

通过javaDoc和方法体可以知道DefaultPropertiesProvider 是对各种配置文件进行加载
init主要的方法就是这些,具体就不详解了。

2.doFilter()

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {    //父类向子类转:强转为http请求、响应           HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        try {            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {            //如果prepare检测出request的uri与excludedPatterns中的pattern匹配则转发到链的下一个filter                chain.doFilter(request, response);            } else {                //设置编码格式:utf-8,值来自于org.apach.struts 包中的默认default.properties                prepare.setEncodingAndLocale(request, response);                //创建Action上下文 重点                prepare.createActionContext(request, response);                prepare.assignDispatcherToThread();                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);        }    }

其中createActionContext是重点内容我们观察下其中的方法:

 /**     * Creates the action context and initializes the thread local     * 创建action上下文并且初始化thread local      */    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {        ActionContext ctx;        Integer counter = 1;        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);        if (oldCounter != null) {            counter = oldCounter + 1;        }        //注意此处是从ThreadLocal中获取此ActionContext变量           ActionContext oldContext = ActionContext.getContext();        if (oldContext != null) {            // detected existing context, so we are probably in a forward            ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));        } else {            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();            stack.getContext().putAll(dispatcher.createContextMap(request, response, null));            ctx = new ActionContext(stack.getContext());        }        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);        ActionContext.setContext(ctx);        return ctx;    }

其中 dispatcher.createContextMap():

 public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,            ActionMapping mapping) {        // request map wrapping the http request objects        Map requestMap = new RequestMap(request);        // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately        Map params = new HashMap(request.getParameterMap());        // session map wrapping the http session        Map session = new SessionMap(request);        // application map wrapping the ServletContext        Map application = new ApplicationMap(servletContext);        Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response);        if (mapping != null) {            extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);        }        return extraContext;    }

可以看出:
通过struts2的容器获取到valueStack对象:OnglValueStack
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
1、把request,session,application等封装成一些map,再把这些map放入到大map中
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
2.把大map的引用指向了ActionContext中的Map content;
ctx = new ActionContext(stack.getContext());
把ActionContext放入到了当前线程中ActionContext.setContext(ctx);

说明:因为把ActionContext放入到了当前线程中,所以valueStack也在线程中,这样数据就可以保证安全了并且在一个线程范围内可以共享数据

接下来的进行的方法request = prepare.wrapRequest(request);
对request进行了包装:返回的对象是:
1. StrutsRequestWrapper
2. MultPartRequestWrapper 文件上传 继承了 StrutsRequestWrapper

接下来进行的就是
ActionMapping mapping = prepare.findActionMapping(request, response, true);
那么ActionMapping是干什么的?

public class ActionMapping {    private String name;    private String namespace;    private String method;    private String extension;    private Map<String, Object> params;    private Result result;

发没发现跟sturts2.xml格式很想

<package name="users" namespace="/users" extends="default">        <action name="*_*" class="action.{1}Action" method="{2}">            <result name="login_success">/users/Users_login_success.jsp</result>            <result name="login_false">/users/Users_login.jsp</result>            <result name="logout_success">/users/Users_login.jsp</result>            <result name="input">/users/Users_login.jsp</result>        </action>

可见ActionMapping就是对struts2的配置文件的包装
同时也就可以明白findActionMapping就是对struts2.xml的解析

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, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);            }        }        return mapping;    }

接下来就是进行execute.executeAction(request, response, mapping);

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

从javadoc看出是执行一个action,继续看dispatcher.serviceAction(request, response, mapping);是怎么执行的

 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)            throws ServletException {        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        //根据request获取值栈        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中            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();        //重点            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 {            //重点                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);        }    }

在ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);中

其中ActionProxyFactory.class是在struts-default.xml声明的

  <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="prefix" class="org.apache.struts2.impl.StrutsActionProxyFactory"/>

同时点开createActionProxy的实现方法

 public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {        ActionInvocation inv = createActionInvocation(extraContext, true);        container.inject(inv);        return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);}

继续点到createActionProxy

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);        proxy.prepare();        return proxy;    }}

继续 proxy.prepare();

protected void prepare() {        super.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(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());        createInterceptors(proxy);    }

最终目标

protected void createAction(Map<String, Object> contextMap) {        // load action        String timerKey = "actionCreate: " + proxy.getActionName();        try {            UtilTimerStack.push(timerKey);            //创建action            action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);        } catch (InstantiationException e) {            throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());        } catch (IllegalAccessException e) {            throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());        } catch (Exception e) {            String gripe;            if (proxy == null) {                gripe = "Whoa!  No ActionProxy instance found in current ActionInvocation.  This is bad ... very bad";            } else if (proxy.getConfig() == null) {                gripe = "Sheesh.  Where'd that ActionProxy get to?  I can't find it in the current ActionInvocation!?";            } else if (proxy.getConfig().getClassName() == null) {                gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";            } else {                gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ",  defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";            }            gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");            throw new XWorkException(gripe, e, proxy.getConfig());        } finally {            UtilTimerStack.pop(timerKey);        }        if (actionEventListener != null) {            action = actionEventListener.prepare(action, stack);        }    }

DefaultActionInvocation中的init方法
createAction(contextMap); 创建action
objectFactory.buildAction
stack.push(action); 把action放入到栈顶
contextMap.put(“action”, action);
把action放入到map中
List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
获取这次请求所有的拦截器,并且返回拦截器的迭代器的形式
最后通过proxy.execute();
调用到
DefaultActionInvocation中的invoke方法
1、按照顺序的方式执行所有的拦截器
2、执行action中的方法
3、执行结果集
4、按照倒叙的方式执行拦截器

0 0