Struts2运行过程以及StrutsPrepareAndExecuteFilter源码阅读
来源:互联网 发布:淘宝直通车软件 编辑:程序博客网 时间:2024/04/29 18:30
Struts2运行过程
1.Struts2启动过程
在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、按照倒叙的方式执行拦截器
- Struts2运行过程以及StrutsPrepareAndExecuteFilter源码阅读
- struts2源码-StrutsPrepareAndExecuteFilter
- Struts2源码深究:StrutsPrepareAndExecuteFilter
- struts2 StrutsPrepareAndExecuteFilter 源码分析
- struts2 StrutsPrepareAndExecuteFilter 源码分析
- Struts2 StrutsPrepareAndExecuteFilter 源码分析
- Struts2 源码学习 一 StrutsPrepareAndExecuteFilter
- Struts2中StrutsPrepareAndExecuteFilter源码浅析
- 探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
- 探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
- 探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
- 探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
- 探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
- 深入研究Struts2(二)-StrutsPrepareAndExecuteFilter源码剖析
- 探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
- 探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
- Struts2源码分析——StrutsPrepareAndExecuteFilter
- 探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
- WEB服务器的搭建(腾讯云) ubuntu14.04
- 深入理解结构体指针
- [数据结构] 表达式求值(转化为后缀表达式再求值或直接求值)
- Java的Atomic类分析
- n皇后问题
- Struts2运行过程以及StrutsPrepareAndExecuteFilter源码阅读
- PAT乙级—1023. 组个最小数 (20)-native
- 删除集合里重复的字符串
- 调查管理系统(2)---数据库
- CSS position属性详解
- echartsJS初探--图形报表
- sed命令详解
- 第七届蓝桥杯—第二题||生日蜡烛
- Spring面试题和答案