您的位置:首页 > 编程语言 > Java开发

Struts2源码深究:StrutsPrepareAndExecuteFilter

2015-12-01 08:57 543 查看
一、概述 Struts2的核心是一个Filter,Action可以脱离web容器,那么是什么让http请求和action关联在一起的,下面我们深入源码来分析下Struts2是如何工作的。FilterDispatcher API 写道Deprecated. Since Struts 2.1.3, use StrutsPrepareAndExecuteFilter instead or StrutsPrepareFilter and StrutsExecuteFilter if needing using the
ActionContextCleanUp filter in addition to this one 鉴于常规情况官方推荐使用StrutsPrepareAndExecuteFilter替代FilterDispatcher,我们此文 将剖析StrutsPrepareAndExecuteFilter,其在工程中作为一个Filter配置在web.xml中,配置如下:Xml代码 收藏代码 < filter > < filter-name > struts2 </ filter-name > < filter-class
>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </ filter-class > </ filter > < filter-mapping > < filter-name > struts2 </ filter-name > < url-pattern > /* </ url-pattern > </ filter-mapping > [xml] view plaincopy struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2 /* 二、源码属性方法简介 下面我们研究下StrutsPrepareAndExecuteFilter源码,类的主要信息如下: 属性摘要protected List excludedPatterns protected ExecuteOperations execute protected PrepareOperations prepare StrutsPrepareAndExecuteFilter与普通的Filter并无区别,方法除继承自Filter外,仅有一个回调方法,第三部分我 们将按照Filter方法调用顺序,由init—>doFilter—>destroy顺序地分析源码。方法摘要
void destroy () 继承自Filter,用于资源释放 void doFilter (ServletRequest req, ServletResponse res, FilterChain chain) 继承自Filter,执行方法 void init (FilterConfig filterConfig) 继承自Filter,初始化参数protected void postInit (Dispatcher dispatcher, FilterConfig filterConfig) Callback
for post initialization(一个空的方法,用于方法回调初始化) 三、源码剖析 1、init方法 init是Filter第一个运行的方法,我们看下struts2的核心Filter在调用init方法初始化时做哪些工作:Java代码 收藏代码 public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); try { //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
FilterHostConfig config = new FilterHostConfig(filterConfig); // 初始化struts内部日志 init.initLogging(config); //创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源 Dispatcher dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config,
dispatcher); //初始化类属性:prepare 、execute prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); this .excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//回调空的postInit方法 postInit(dispatcher, filterConfig); } finally { init.cleanup(); } } [java] view plaincopy public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); try { //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
FilterHostConfig config = new FilterHostConfig(filterConfig); // 初始化struts内部日志 init.initLogging(config); //创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源 Dispatcher dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config,
dispatcher); //初始化类属性:prepare 、execute prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//回调空的postInit方法 postInit(dispatcher, filterConfig); } finally { init.cleanup(); } } 首先看下FilterHostConfig ,源码如下: Java代码 收藏代码 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 getInitParameterNames() { return MakeIterator.convert(config.getInitParameterNames());
} public ServletContext getServletContext() { return config.getServletContext(); } } [java] view plaincopy 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 getInitParameterNames() { return MakeIterator.convert(config.getInitParameterNames());
} public ServletContext getServletContext() { return config.getServletContext(); } } 只有短短的几行代码,getInitParameterNames是这个类的核心,将Filter初始化参数名称有枚举类型转为Iterator。此类的主要作为是对filterConfig 封装。 重点来了,创建并初始化Dispatcher Java代码 收藏代码 public Dispatcher initDispatcher( HostConfig
filterConfig ) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init(); return dispatcher; } [java] view plaincopy public Dispatcher initDispatcher( HostConfig filterConfig ) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init();
return dispatcher; } 创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,然后根绝servlet上下文和参数Map构造Dispatcher :Java代码 收藏代码 private Dispatcher createDispatcher( HostConfig filterConfig ) { Map params = new HashMap(); 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); } [java] view plaincopy private Dispatcher createDispatcher( HostConfig
filterConfig ) { Map params = new HashMap(); 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初始化,加载struts2的相关配置文件,将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.xml,…… Java代码 收藏代码 /** *初始化过程中依次加载如下配置文件 */ public void init() { if (configurationManager == null ) { configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
} try { //加载org/apache/struts2/default.properties init_DefaultProperties(); // [1] //加载struts-default.xml,struts-plugin.xml,struts.xml 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_CheckConfigurationReloading(container); 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);
} } [java] view plaincopy /** *初始化过程中依次加载如下配置文件 */ public void init() { if (configurationManager == null) { configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } try { //加载org/apache/struts2/default.properties init_DefaultProperties();
// [1] //加载struts-default.xml,struts-plugin.xml,struts.xml 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_CheckConfigurationReloading(container); 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); } } 初始化default.properties,具体的初始化操作在DefaultPropertiesProvider类中
Java代码 收藏代码 private void init_DefaultProperties() { configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } [java] view plaincopy private void init_DefaultProperties() { configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
} 下面我们看下DefaultPropertiesProvider类源码: Java代码 收藏代码 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null ; try { defaultSettings = new PropertiesSettings( "org/apache/struts2/default"
); } catch (Exception e) { throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties" , e); } loadSettings(props, defaultSettings); } [java] view plaincopy public void register(ContainerBuilder builder, LocatableProperties
props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings("org/apache/struts2/default"); } catch (Exception e) { throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties",
e); } loadSettings(props, defaultSettings); } 其他的我们再次省略,大家可以浏览下各个初始化操作都加载了那些文件3、doFilter方法 doFilter是过滤器的执行方法,它拦截提交的HttpServletRequest请求,HttpServletResponse响应,作为strtus2的核心拦截器,在doFilter里面到底做了哪些工作,我们将逐行解读其源码,源码如下: Java代码 收藏代码 public void doFilter(ServletRequest
req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //父类向子类转:强转为http请求、响应 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { //设置编码和国际化 prepare.setEncodingAndLocale(request,
response); //创建Action上下文(重点) prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { 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); } } [java] view plaincopy public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //父类向子类转:强转为http请求、响应 HttpServletRequest
request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { //设置编码和国际化 prepare.setEncodingAndLocale(request, response); //创建Action上下文(重点) prepare.createActionContext(request, response); prepare.assignDispatcherToThread();
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { 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); } } setEncodingAndLocale调用了dispatcher方法的prepare方法:
Java代码 收藏代码 /** * Sets the request encoding and locale on the response */ public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { dispatcher.prepare(request, response); } [java] view plaincopy /** * Sets the request encoding
and locale on the response */ public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { dispatcher.prepare(request, response); } 下面我们看下prepare方法,这个方法很简单只是设置了encoding 、locale ,做的只是一些辅助的工作:Java代码 收藏代码 public void prepare(HttpServletRequest
request, HttpServletResponse response) { String encoding = null ; if (defaultEncoding != null ) { encoding = defaultEncoding; } Locale locale = null ; if (defaultLocale != null ) { locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
} if (encoding != null ) { try { request.setCharacterEncoding(encoding); } catch (Exception e) { LOG.error("Error setting character encoding to '" + encoding + "' - ignoring." , e); } } if (locale != null ) { response.setLocale(locale); } if (paramsWorkaroundEnabled)
{ request.getParameter("foo" ); // simply read any parameter (existing or not) to "prime" the request } } [java] view plaincopy public void prepare(HttpServletRequest request, HttpServletResponse response) { String encoding = null; if (defaultEncoding != null)
{ encoding = defaultEncoding; } Locale locale = null; if (defaultLocale != null) { locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale()); } if (encoding != null) { try { request.setCharacterEncoding(encoding); } catch (Exception e)
{ LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e); } } if (locale != null) { response.setLocale(locale); } if (paramsWorkaroundEnabled) { request.getParameter("foo"); // simply read any parameter (existing or not) to "prime"
the request } } Action上下文创建(重点) ActionContext是一个容器,这个容易主要存储request、session、application、parameters等相关信 息.ActionContext是一个线程的本地变量,这意味着不同的action之间不会共享ActionContext,所以也不用考虑线程安全问 题。其实质是一个Map,key是标示request、session、……的字符串,值是其对应的对象:Java代码 收藏代码 static ThreadLocal actionContext
= new ThreadLocal(); Map context; [java] view plaincopy static ThreadLocal actionContext = new ThreadLocal(); Map context; 下面我们看下如何创建action上下文的,代码如下: Java代码 收藏代码 /** *创建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(oldContext.getContextMap())); } else { ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext)); //stack.getContext()返回的是一个Map,根据此Map构造一个ActionContext ctx = new ActionContext(stack.getContext()); } request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
//将ActionContext存如ThreadLocal ActionContext.setContext(ctx); return ctx; } [java] view plaincopy /** *创建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(oldContext.getContextMap())); } else { ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); stack.getContext().putAll(dispatcher.createContextMap(request,
response, null, servletContext)); //stack.getContext()返回的是一个Map,根据此Map构造一个ActionContext ctx = new ActionContext(stack.getContext()); } request.setAttribute(CLEANUP_RECURSION_COUNTER, counter); //将ActionContext存如ThreadLocal ActionContext.setContext(ctx); return
ctx; } 上面代码中dispatcher.createContextMap,如何封装相关参数: Java代码 收藏代码 public Map createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext context) { // 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(context); //requestMap、params、session等Map封装成为一个上下文Map,逐个调用了map.put(Map p). Map extraContext = createContextMap(requestMap, params, session, application, request, response,
context); if (mapping != null ) { extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); } return extraContext; } [java] view plaincopy public Map createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext
context) { // 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(context); //requestMap、params、session等Map封装成为一个上下文Map,逐个调用了map.put(Map p). Map extraContext
= createContextMap(requestMap, params, session, application, request, response, context); if (mapping != null) { extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); } return extraContext; } 我们简单看下RequestMap,其他的省略。RequestMap类实现了抽象Map,故其本身是一个Map,主要方法实现:Java代码
收藏代码 //map的get实现 public Object get(Object key) { return request.getAttribute(key.toString()); } //map的put实现 public Object put(Object key, Object value) { Object oldValue = get(key); entries = null ; request.setAttribute(key.toString(), value); return oldValue;
} [java] view plaincopy //map的get实现 public Object get(Object key) { return request.getAttribute(key.toString()); } //map的put实现 public Object put(Object key, Object value) { Object oldValue = get(key); entries = null; request.setAttribute(key.toString(), value);
return oldValue; } 下面是源码展示了如何执行Action控制器: Java代码 收藏代码 public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { dispatcher.serviceAction(request, response, servletContext, mapping);
} public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { //封装执行的上下文环境,主要讲相关信息存储入map Map extraContext = createContextMap(request, response, mapping, context);
// 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)
{ ActionContext ctx = ActionContext.getContext(); if (ctx != null ) { stack = ctx.getValueStack(); } } if (stack != null ) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from
Dispatcher" ; try { UtilTimerStack.push(timerKey); //获取命名空间 String namespace = mapping.getNamespace(); //获取action配置的name属性 String name = mapping.getName(); //获取action配置的method属性 String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration();
//根据执行上下文参数,命名空间,名称等创建用户自定义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! //执行execute方法,并转向结果 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) { // WW-2874 Only log error if in devMode if (devMode)
{ String reqStr = request.getRequestURI(); if (request.getQueryString() != null ) { reqStr = reqStr + "?" + request.getQueryString(); } LOG.error("Could not find action or result/n" + reqStr, e); } else { LOG.warn("Could not find action or result" , e); }
sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } finally { UtilTimerStack.pop(timerKey); } } [java] view plaincopy
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { dispatcher.serviceAction(request, response, servletContext, mapping); } public void serviceAction(HttpServletRequest request,
HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { //封装执行的上下文环境,主要讲相关信息存储入map Map extraContext = createContextMap(request, response, mapping, context); // 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) { ActionContext ctx = ActionContext.getContext(); if
(ctx != null) { stack = ctx.getValueStack(); } } if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); //获取命名空间
String namespace = mapping.getNamespace(); //获取action配置的name属性 String name = mapping.getName(); //获取action配置的method属性 String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration(); //根据执行上下文参数,命名空间,名称等创建用户自定义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! //执行execute方法,并转向结果 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) { // WW-2874 Only log error if in devMode if(devMode) { String reqStr = request.getRequestURI();
if (request.getQueryString() != null) { reqStr = reqStr + "?" + request.getQueryString(); } LOG.error("Could not find action or result/n" + reqStr, e); } else { LOG.warn("Could not find action or result", e); } sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND,
e); } catch (Exception e) { sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } finally { UtilTimerStack.pop(timerKey); } } 文中对如何解析Struts.xml,如何将URL与action映射匹配为分析,有需要的我后续补全,因为 StrutsXmlConfigurationProvider继承XmlConfigurationProvider,并在register方法回调父
类的register,有兴趣的可以深入阅读下下XmlConfigurationProvider源码: Java代码 收藏代码 public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException { if (servletContext != null && !containerBuilder.contains(ServletContext. class))
{ containerBuilder.factory(ServletContext.class , new Factory() { public ServletContext create(Context context) throws Exception { return servletContext; } }); } //调用父类的register,关键点所在 super .register(containerBuilder, props); } [java] view plaincopy public
void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException { if (servletContext != null && !containerBuilder.contains(ServletContext.class)) { containerBuilder.factory(ServletContext.class, new Factory() { public
ServletContext create(Context context) throws Exception { return servletContext; } }); } //调用父类的register,关键点所在 super.register(containerBuilder, props); } struts2-core-2.2.1.jar包中struts-2.1.7.dtd对于Action的定义如下:Xml代码 收藏代码 [xml] view plaincopy 从上述DTD中可见Action元素可以含有name
、class 、method 、converter 属性。 XmlConfigurationProvider解析struts.xml配置的Action元素:Java代码 收藏代码 protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException { String name = actionElement.getAttribute("name"
); String className = actionElement.getAttribute("class" ); String methodName = actionElement.getAttribute("method" ); Location location = DomHelper.getLocationObject(actionElement); if (location == null ) { LOG.warn("location null for " + className); } //methodName
should be null if it's not set methodName = (methodName.trim().length() > 0 ) ? methodName.trim() : null ; // if there isnt a class name specified for an then try to // use the default-class-ref from the if (StringUtils.isEmpty(className)) { // if there is
a package default-class-ref use that, otherwise use action support /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) { className = packageContext.getDefaultClassRef(); } else { className = ActionSupport.class.getName(); }*/ } else { if (!verifyAction(className,
name, location)) { if (LOG.isErrorEnabled()) LOG.error("Unable to verify action [#0] with class [#1], from [#2]", name, className, location.toString()); return ; } } Map results; try { results = buildResults(actionElement, packageContext); } catch (ConfigurationException
e) { throw new ConfigurationException( "Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement); } List interceptorList = buildInterceptorList(actionElement, packageContext); List exceptionMappings =
buildExceptionMappings(actionElement, packageContext); ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className) .methodName(methodName) .addResultConfigs(results) .addInterceptors(interceptorList) .addExceptionMappings(exceptionMappings)
.addParams(XmlHelper.getParams(actionElement)) .location(location) .build(); packageContext.addActionConfig(name, actionConfig); if (LOG.isDebugEnabled()) { LOG.debug("Loaded " + (StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace()
+ "/" ) : "" ) + name + " in '" + packageContext.getName() + "' package:" + actionConfig); } } [java] view plaincopy protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException { String name = actionElement.getAttribute("name");
String className = actionElement.getAttribute("class"); String methodName = actionElement.getAttribute("method"); Location location = DomHelper.getLocationObject(actionElement); if (location == null) { LOG.warn("location null for " + className); } //methodName
should be null if it's not set methodName = (methodName.trim().length() > 0) ? methodName.trim() : null; // if there isnt a class name specified for an then try to // use the default-class-ref from the if (StringUtils.isEmpty(className)) { // if there is a
package default-class-ref use that, otherwise use action support /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) { className = packageContext.getDefaultClassRef(); } else { className = ActionSupport.class.getName(); }*/ } else { if (!verifyAction(className,
name, location)) { if (LOG.isErrorEnabled()) LOG.error("Unable to verify action [#0] with class [#1], from [#2]", name, className, location.toString()); return; } } Map results; try { results = buildResults(actionElement, packageContext); } catch (ConfigurationException
e) { throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement); } List interceptorList = buildInterceptorList(actionElement, packageContext); List exceptionMappings =
buildExceptionMappings(actionElement, packageContext); ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className) .methodName(methodName) .addResultConfigs(results) .addInterceptors(interceptorList) .addExceptionMappings(exceptionMappings)
.addParams(XmlHelper.getParams(actionElement)) .location(location) .build(); packageContext.addActionConfig(name, actionConfig); if (LOG.isDebugEnabled()) { LOG.debug("Loaded " + (StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace()
+ "/") : "") + name + " in '" + packageContext.getName() + "' package:" + actionConfig); } } 工作中不涉及Struts2,本周工作有个2天的空档期,稍微看了下struts2的文档,写了个demo,从源码的角度研究了下运行原理,如有分析不当请指出,我后续逐步完善更正,大家共同提高。 一、概述 Struts2的核心是一个Filter,Action可以脱离web容器,那么是什么让http请求和action关联在一起的,下面我们深入源码来分析下Struts2是如何工作的。FilterDispatcher
API 写道Deprecated. Since Struts 2.1.3, use StrutsPrepareAndExecuteFilter instead or StrutsPrepareFilter and StrutsExecuteFilter if needing using the ActionContextCleanUp filter in addition to this one 鉴于常规情况官方推荐使用StrutsPrepareAndExecuteFilter替代FilterDispatcher,我们此文
将剖析StrutsPrepareAndExecuteFilter,其在工程中作为一个Filter配置在web.xml中,配置如下:Xml代码 收藏代码 < filter > < filter-name > struts2 </ filter-name > < filter-class >org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </ filter-class > </ filter > < filter-mapping
> < filter-name > struts2 </ filter-name > < url-pattern > /* </ url-pattern > </ filter-mapping > [xml] view plaincopy struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /* 二、源码属性方法简介 下面我们研究下StrutsPrepareAndExecuteFilter源码,类的主要信息如下:
属性摘要protected List excludedPatterns protected ExecuteOperations execute protected PrepareOperations prepare StrutsPrepareAndExecuteFilter与普通的Filter并无区别,方法除继承自Filter外,仅有一个回调方法,第三部分我 们将按照Filter方法调用顺序,由init—>doFilter—>destroy顺序地分析源码。方法摘要 void destroy () 继承自Filter,用于资源释放
void doFilter (ServletRequest req, ServletResponse res, FilterChain chain) 继承自Filter,执行方法 void init (FilterConfig filterConfig) 继承自Filter,初始化参数protected void postInit (Dispatcher dispatcher, FilterConfig filterConfig) Callback for post initialization(一个空的方法,用于方法回调初始化)
三、源码剖析 1、init方法 init是Filter第一个运行的方法,我们看下struts2的核心Filter在调用init方法初始化时做哪些工作:Java代码 收藏代码 public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); try { //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
FilterHostConfig config = new FilterHostConfig(filterConfig); // 初始化struts内部日志 init.initLogging(config); //创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源 Dispatcher dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config,
dispatcher); //初始化类属性:prepare 、execute prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); this .excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//回调空的postInit方法 postInit(dispatcher, filterConfig); } finally { init.cleanup(); } } [java] view plaincopy public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); try { //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
FilterHostConfig config = new FilterHostConfig(filterConfig); // 初始化struts内部日志 init.initLogging(config); //创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源 Dispatcher dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config,
dispatcher); //初始化类属性:prepare 、execute prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//回调空的postInit方法 postInit(dispatcher, filterConfig); } finally { init.cleanup(); } } 首先看下FilterHostConfig ,源码如下: Java代码 收藏代码 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 getInitParameterNames() { return MakeIterator.convert(config.getInitParameterNames());
} public ServletContext getServletContext() { return config.getServletContext(); } } [java] view plaincopy 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 getInitParameterNames() { return MakeIterator.convert(config.getInitParameterNames());
} public ServletContext getServletContext() { return config.getServletContext(); } } 只有短短的几行代码,getInitParameterNames是这个类的核心,将Filter初始化参数名称有枚举类型转为Iterator。此类的主要作为是对filterConfig 封装。 重点来了,创建并初始化Dispatcher Java代码 收藏代码 public Dispatcher initDispatcher( HostConfig
filterConfig ) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init(); return dispatcher; } [java] view plaincopy public Dispatcher initDispatcher( HostConfig filterConfig ) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init();
return dispatcher; } 创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,然后根绝servlet上下文和参数Map构造Dispatcher :Java代码 收藏代码 private Dispatcher createDispatcher( HostConfig filterConfig ) { Map params = new HashMap(); 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); } [java] view plaincopy private Dispatcher createDispatcher( HostConfig
filterConfig ) { Map params = new HashMap(); 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初始化,加载struts2的相关配置文件,将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.xml,…… Java代码 收藏代码 /** *初始化过程中依次加载如下配置文件 */ public void init() { if (configurationManager == null ) { configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
} try { //加载org/apache/struts2/default.properties init_DefaultProperties(); // [1] //加载struts-default.xml,struts-plugin.xml,struts.xml 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_CheckConfigurationReloading(container); 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);
} } [java] view plaincopy /** *初始化过程中依次加载如下配置文件 */ public void init() { if (configurationManager == null) { configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } try { //加载org/apache/struts2/default.properties init_DefaultProperties();
// [1] //加载struts-default.xml,struts-plugin.xml,struts.xml 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_CheckConfigurationReloading(container); 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); } } 初始化default.properties,具体的初始化操作在DefaultPropertiesProvider类中
Java代码 收藏代码 private void init_DefaultProperties() { configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } [java] view plaincopy private void init_DefaultProperties() { configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
} 下面我们看下DefaultPropertiesProvider类源码: Java代码 收藏代码 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null ; try { defaultSettings = new PropertiesSettings( "org/apache/struts2/default"
); } catch (Exception e) { throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties" , e); } loadSettings(props, defaultSettings); } [java] view plaincopy public void register(ContainerBuilder builder, LocatableProperties
props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings("org/apache/struts2/default"); } catch (Exception e) { throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties",
e); } loadSettings(props, defaultSettings); } 其他的我们再次省略,大家可以浏览下各个初始化操作都加载了那些文件3、doFilter方法 doFilter是过滤器的执行方法,它拦截提交的HttpServletRequest请求,HttpServletResponse响应,作为strtus2的核心拦截器,在doFilter里面到底做了哪些工作,我们将逐行解读其源码,源码如下: Java代码 收藏代码 public void doFilter(ServletRequest
req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //父类向子类转:强转为http请求、响应 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { //设置编码和国际化 prepare.setEncodingAndLocale(request,
response); //创建Action上下文(重点) prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { 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); } } [java] view plaincopy public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //父类向子类转:强转为http请求、响应 HttpServletRequest
request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { //设置编码和国际化 prepare.setEncodingAndLocale(request, response); //创建Action上下文(重点) prepare.createActionContext(request, response); prepare.assignDispatcherToThread();
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { 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); } } setEncodingAndLocale调用了dispatcher方法的prepare方法:
Java代码 收藏代码 /** * Sets the request encoding and locale on the response */ public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { dispatcher.prepare(request, response); } [java] view plaincopy /** * Sets the request encoding
and locale on the response */ public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { dispatcher.prepare(request, response); } 下面我们看下prepare方法,这个方法很简单只是设置了encoding 、locale ,做的只是一些辅助的工作:Java代码 收藏代码 public void prepare(HttpServletRequest
request, HttpServletResponse response) { String encoding = null ; if (defaultEncoding != null ) { encoding = defaultEncoding; } Locale locale = null ; if (defaultLocale != null ) { locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
} if (encoding != null ) { try { request.setCharacterEncoding(encoding); } catch (Exception e) { LOG.error("Error setting character encoding to '" + encoding + "' - ignoring." , e); } } if (locale != null ) { response.setLocale(locale); } if (paramsWorkaroundEnabled)
{ request.getParameter("foo" ); // simply read any parameter (existing or not) to "prime" the request } } [java] view plaincopy public void prepare(HttpServletRequest request, HttpServletResponse response) { String encoding = null; if (defaultEncoding != null)
{ encoding = defaultEncoding; } Locale locale = null; if (defaultLocale != null) { locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale()); } if (encoding != null) { try { request.setCharacterEncoding(encoding); } catch (Exception e)
{ LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e); } } if (locale != null) { response.setLocale(locale); } if (paramsWorkaroundEnabled) { request.getParameter("foo"); // simply read any parameter (existing or not) to "prime"
the request } } Action上下文创建(重点) ActionContext是一个容器,这个容易主要存储request、session、application、parameters等相关信 息.ActionContext是一个线程的本地变量,这意味着不同的action之间不会共享ActionContext,所以也不用考虑线程安全问 题。其实质是一个Map,key是标示request、session、……的字符串,值是其对应的对象:Java代码 收藏代码 static ThreadLocal actionContext
= new ThreadLocal(); Map context; [java] view plaincopy static ThreadLocal actionContext = new ThreadLocal(); Map context; 下面我们看下如何创建action上下文的,代码如下: Java代码 收藏代码 /** *创建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(oldContext.getContextMap())); } else { ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext)); //stack.getContext()返回的是一个Map,根据此Map构造一个ActionContext ctx = new ActionContext(stack.getContext()); } request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
//将ActionContext存如ThreadLocal ActionContext.setContext(ctx); return ctx; } [java] view plaincopy /** *创建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(oldContext.getContextMap())); } else { ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); stack.getContext().putAll(dispatcher.createContextMap(request,
response, null, servletContext)); //stack.getContext()返回的是一个Map,根据此Map构造一个ActionContext ctx = new ActionContext(stack.getContext()); } request.setAttribute(CLEANUP_RECURSION_COUNTER, counter); //将ActionContext存如ThreadLocal ActionContext.setContext(ctx); return
ctx; } 上面代码中dispatcher.createContextMap,如何封装相关参数: Java代码 收藏代码 public Map createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext context) { // 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(context); //requestMap、params、session等Map封装成为一个上下文Map,逐个调用了map.put(Map p). Map extraContext = createContextMap(requestMap, params, session, application, request, response,
context); if (mapping != null ) { extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); } return extraContext; } [java] view plaincopy public Map createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext
context) { // 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(context); //requestMap、params、session等Map封装成为一个上下文Map,逐个调用了map.put(Map p). Map extraContext
= createContextMap(requestMap, params, session, application, request, response, context); if (mapping != null) { extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); } return extraContext; } 我们简单看下RequestMap,其他的省略。RequestMap类实现了抽象Map,故其本身是一个Map,主要方法实现:Java代码
收藏代码 //map的get实现 public Object get(Object key) { return request.getAttribute(key.toString()); } //map的put实现 public Object put(Object key, Object value) { Object oldValue = get(key); entries = null ; request.setAttribute(key.toString(), value); return oldValue;
} [java] view plaincopy //map的get实现 public Object get(Object key) { return request.getAttribute(key.toString()); } //map的put实现 public Object put(Object key, Object value) { Object oldValue = get(key); entries = null; request.setAttribute(key.toString(), value);
return oldValue; } 下面是源码展示了如何执行Action控制器: Java代码 收藏代码 public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { dispatcher.serviceAction(request, response, servletContext, mapping);
} public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { //封装执行的上下文环境,主要讲相关信息存储入map Map extraContext = createContextMap(request, response, mapping, context);
// 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)
{ ActionContext ctx = ActionContext.getContext(); if (ctx != null ) { stack = ctx.getValueStack(); } } if (stack != null ) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from
Dispatcher" ; try { UtilTimerStack.push(timerKey); //获取命名空间 String namespace = mapping.getNamespace(); //获取action配置的name属性 String name = mapping.getName(); //获取action配置的method属性 String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration();
//根据执行上下文参数,命名空间,名称等创建用户自定义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! //执行execute方法,并转向结果 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) { // WW-2874 Only log error if in devMode if (devMode)
{ String reqStr = request.getRequestURI(); if (request.getQueryString() != null ) { reqStr = reqStr + "?" + request.getQueryString(); } LOG.error("Could not find action or result/n" + reqStr, e); } else { LOG.warn("Could not find action or result" , e); }
sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } finally { UtilTimerStack.pop(timerKey); } } [java] view plaincopy
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { dispatcher.serviceAction(request, response, servletContext, mapping); } public void serviceAction(HttpServletRequest request,
HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { //封装执行的上下文环境,主要讲相关信息存储入map Map extraContext = createContextMap(request, response, mapping, context); // 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) { ActionContext ctx = ActionContext.getContext(); if
(ctx != null) { stack = ctx.getValueStack(); } } if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); //获取命名空间
String namespace = mapping.getNamespace(); //获取action配置的name属性 String name = mapping.getName(); //获取action配置的method属性 String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration(); //根据执行上下文参数,命名空间,名称等创建用户自定义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! //执行execute方法,并转向结果 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) { // WW-2874 Only log error if in devMode if(devMode) { String reqStr = request.getRequestURI();
if (request.getQueryString() != null) { reqStr = reqStr + "?" + request.getQueryString(); } LOG.error("Could not find action or result/n" + reqStr, e); } else { LOG.warn("Could not find action or result", e); } sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND,
e); } catch (Exception e) { sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } finally { UtilTimerStack.pop(timerKey); } } 文中对如何解析Struts.xml,如何将URL与action映射匹配为分析,有需要的我后续补全,因为 StrutsXmlConfigurationProvider继承XmlConfigurationProvider,并在register方法回调父
类的register,有兴趣的可以深入阅读下下XmlConfigurationProvider源码: Java代码 收藏代码 public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException { if (servletContext != null && !containerBuilder.contains(ServletContext. class))
{ containerBuilder.factory(ServletContext.class , new Factory() { public ServletContext create(Context context) throws Exception { return servletContext; } }); } //调用父类的register,关键点所在 super .register(containerBuilder, props); } [java] view plaincopy public
void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException { if (servletContext != null && !containerBuilder.contains(ServletContext.class)) { containerBuilder.factory(ServletContext.class, new Factory() { public
ServletContext create(Context context) throws Exception { return servletContext; } }); } //调用父类的register,关键点所在 super.register(containerBuilder, props); } struts2-core-2.2.1.jar包中struts-2.1.7.dtd对于Action的定义如下:Xml代码 收藏代码 [xml] view plaincopy 从上述DTD中可见Action元素可以含有name
、class 、method 、converter 属性。 XmlConfigurationProvider解析struts.xml配置的Action元素:Java代码 收藏代码 protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException { String name = actionElement.getAttribute("name"
); String className = actionElement.getAttribute("class" ); String methodName = actionElement.getAttribute("method" ); Location location = DomHelper.getLocationObject(actionElement); if (location == null ) { LOG.warn("location null for " + className); } //methodName
should be null if it's not set methodName = (methodName.trim().length() > 0 ) ? methodName.trim() : null ; // if there isnt a class name specified for an then try to // use the default-class-ref from the if (StringUtils.isEmpty(className)) { // if there is
a package default-class-ref use that, otherwise use action support /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) { className = packageContext.getDefaultClassRef(); } else { className = ActionSupport.class.getName(); }*/ } else { if (!verifyAction(className,
name, location)) { if (LOG.isErrorEnabled()) LOG.error("Unable to verify action [#0] with class [#1], from [#2]", name, className, location.toString()); return ; } } Map results; try { results = buildResults(actionElement, packageContext); } catch (ConfigurationException
e) { throw new ConfigurationException( "Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement); } List interceptorList = buildInterceptorList(actionElement, packageContext); List exceptionMappings =
buildExceptionMappings(actionElement, packageContext); ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className) .methodName(methodName) .addResultConfigs(results) .addInterceptors(interceptorList) .addExceptionMappings(exceptionMappings)
.addParams(XmlHelper.getParams(actionElement)) .location(location) .build(); packageContext.addActionConfig(name, actionConfig); if (LOG.isDebugEnabled()) { LOG.debug("Loaded " + (StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace()
+ "/" ) : "" ) + name + " in '" + packageContext.getName() + "' package:" + actionConfig); } } [java] view plaincopy protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException { String name = actionElement.getAttribute("name");
String className = actionElement.getAttribute("class"); String methodName = actionElement.getAttribute("method"); Location location = DomHelper.getLocationObject(actionElement); if (location == null) { LOG.warn("location null for " + className); } //methodName
should be null if it's not set methodName = (methodName.trim().length() > 0) ? methodName.trim() : null; // if there isnt a class name specified for an then try to // use the default-class-ref from the if (StringUtils.isEmpty(className)) { // if there is a
package default-class-ref use that, otherwise use action support /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) { className = packageContext.getDefaultClassRef(); } else { className = ActionSupport.class.getName(); }*/ } else { if (!verifyAction(className,
name, location)) { if (LOG.isErrorEnabled()) LOG.error("Unable to verify action [#0] with class [#1], from [#2]", name, className, location.toString()); return; } } Map results; try { results = buildResults(actionElement, packageContext); } catch (ConfigurationException
e) { throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement); } List interceptorList = buildInterceptorList(actionElement, packageContext); List exceptionMappings =
buildExceptionMappings(actionElement, packageContext); ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className) .methodName(methodName) .addResultConfigs(results) .addInterceptors(interceptorList) .addExceptionMappings(exceptionMappings)
.addParams(XmlHelper.getParams(actionElement)) .location(location) .build(); packageContext.addActionConfig(name, actionConfig); if (LOG.isDebugEnabled()) { LOG.debug("Loaded " + (StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace()
+ "/") : "") + name + " in '" + packageContext.getName() + "' package:" + actionConfig); } }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: