ActionMappingParametersInteceptor
ActionMappingParametersInteceptor拦截器处于defaultStack第十四的位置,该拦截器继承自ParametersInterceptor,也是用于把参数设置到ValueStack中,只不过该拦截器的参数是来源于ActionMapping中,而不是来自请求参数。往ValueStack中设置值的逻辑与ParametersInterceptor一模一样,正是这个原因才把ParametersInterceptor拦截器放在了该拦截器之前讲解。大家可以发现该拦截器只是覆盖了ParametersInterceptor的两个方法:
@Override
protected Map<String, Object> retrieveParameters(ActionContext ac) {
//获取AcitonMapping对象
ActionMapping mapping = (ActionMapping) ac.get(ServletActionContext.ACTION_MAPPING);
if (mapping != null) {
return mapping.getParams();//获取AcitonMapping参数
} else {
return Collections.emptyMap();
}
}
@Override//将参数设置到ActionContext的parameters Map中
protected void addParametersToContext(ActionContext ac, Map newParams) {
//获取请求参数
Map previousParams = ac.getParameters();
Map combinedParams = null;
if (previousParams != null) {
combinedParams = new TreeMap(previousParams);
} else {
combinedParams = new TreeMap();
}
//newParams为ActionMapping中的经过了合法性检查后的合法参数
combinedParams.putAll(newParams);
//将请求参数与合法参数一起设置到ActionContext的parameters Map中
ac.setParameters(combinedParams);
}
因为现在执行的是ActionMappingParametersInteceptor拦截器所以在ParametersInterceptor的doIntercept方法中调用的retrieveParameters方法是ActionMappingParametersInteceptor拦截器中的retrieveParameters,这是java多态的表现,所以该拦截器通过覆盖父类的retrieveParameters方法达到了改变参数来源的目的。
在ParametersInterceptor拦截器的setParameters方法中最后一句调用了addParametersToContext方法,而在ActionMappingParametersInteceptor子类中覆盖了addParametersToContext方法,所以执行的也是ActionMappingParametersInteceptor拦截器定义的addParametersToContext方法,将将请求参数与合法参数一起设置到ActionContext的parameters Map中。
但有一点需要大家注意,就是ActionMapping的参数永远为null,所以执行该拦截器时为ValueStack设置值的操作根本就不会发生,下面通过源代码进行证明。在证明之前ActionConfig,ActionMapping,ActionMapper之间的关系需要明白,ActionConfig对象保存的是Action的配置信息,ActionMapping对象保存的是Action的映射信息,ActionMapper是根据你访问的URL去ActionConfig中进行匹配再映射成ActionMapping对象。例如,一个Action的配置中可以有多个方法,可以有多个Result,但是在执行的时候只能执行Action中的一个方法,返回的时候只能有一个Result而ActionMapper的功能就是根据URL与AcitonConfig映射成ActionMapping对象。
StrutsPrepareAndExecuteFilter的doFilter方法:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//省略...
ActionMapping mapping = prepare.findActionMapping(request, response, true);//调用prepare对象的findActionMapping
//省略...
}
下面是findActionMapping方法:
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mapping == null || forceLookup) {
try {
//去容器中找ActionMapper对象,并调用ActionMapper对象的getMapping方法返回ActionMapping对象
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, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
}
return mapping;
}
ActionMapper是一个接口,在struts2中其默认实现类是org.apache.struts2.dispatcher.mapper.DefaultActionMapper,下面是该类的getMapping方法:
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
ActionMapping mapping = new ActionMapping();//新建一个ActionMapping对象
String uri = getUri(request);//获取URI
int indexOfSemicolon = uri.indexOf(";");
uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;//处理 URI
uri = dropExtension(uri, mapping);//去掉URI后缀
if (uri == null) {
return null;
}
//解析Action的namespace与name
parseNameAndNamespace(uri, mapping, configManager);
//处理特殊参数
handleSpecialParameters(request, mapping);
if (mapping.getName() == null) {
return null;
}
//解析ActionName,因为struts2可以支持动态方法调用,如:xxxAction!login
parseActionName(mapping);
return mapping;
}
一开始以为会在handleSpecialParameters设置ActionMapping参数,但进去看了以后发现也没有为ActionMapping设置参数的操作,所以对于用DefaultActionMapper作为ActionMapper的的实现类的话,ActionMapping中的参数是为null的。
到此该拦截器就讲到这里了,准备下一个拦截器......