下面我将就“Struts2源码分析之ParametersInterceptor拦截器”的完整攻略给您讲解,全文将分别从以下几个方面展开:
- ParametersInterceptor介绍
- ParametersInterceptor源码分析
- ParametersInterceptor示例
1. ParametersInterceptor介绍
ParametersInterceptor是Struts2框架中的一个拦截器,主要用于获取请求中的参数,并将其封装到ActionContext中的parameters键下。当Action类的参数与请求参数对应时,ParametersInterceptor能够帮助我们自动注入请求参数到Action实例中,方便我们使用。
2. ParametersInterceptor源码分析
2.1 ParametersInterceptor拦截器代码
public class ParametersInterceptor extends AbstractInterceptor {
protected static final Logger LOG = LoggerFactory.getLogger(ParametersInterceptor.class);
private static final long serialVersionUID = 3931216350513943349L;
protected boolean ordered = true;
protected boolean enableSMD = false;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
if (!(action instanceof ActionSupport)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Action class [{}] does not extend ActionSupport - skipping parameter injection!", action.getClass().getName());
}
return invocation.invoke();
}
try {
if (ServletActionContext.getRequest().getMethod().equalsIgnoreCase("get")) {
// fix WW-2035, let request parameters and query string parameters both work.
// Request parameters should override query string parameters with same name.
Map<String, Object> parametersMap = createActionParametersMap(invocation);
ServletActionContext.getContext().getActionInvocation().getInvocationContext().setParameters(parametersMap);
} else {
boolean acceptable = acceptableParameterName(invocation);
if (!acceptable && !enableSMD) {
if (LOG.isWarnEnabled()) {
LOG.warn("Parameter [" + invocation.getInvocationContext().getName() + "] is not on the whitelist!, returning null.");
}
return null;
}
Map<String, Object> parameters = retrieveParameters(invocation);
Map<String, Object> contextMap = createContextMap(parameters, invocation);
if (acceptable) {
SMDAction smdAction = new SMDAction(parameters);
addActionError(invocation, smdAction.getActionErrors(), smdAction.getFieldErrors());
ValueStack stack = invocation.getStack();
Map<String, Object> smdData = smdAction.toMap(stack);
// As a convenience for non-annotations users, convert the
// request parameter store to a Map and add it to the context
Map<String, String[]> params = (Map<String, String[]>) contextMap.get("request");
if (params != null && params.size() > 0) {
contextMap.putAll(params);
}
contextMap.put("smd", smdData);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Setting params " + parameters);
}
invocation.getInvocationContext().setParameters(parameters);
// make parameters available via ${parameters} and ${parameters.name}
for (Entry<String, Object> entry : contextMap.entrySet()) {
invocation.getInvocationContext().put(entry.getKey(), entry.getValue());
}
if (LOG.isDebugEnabled()) {
LOG.debug("Setting params " + parameters);
}
}
} catch (Exception e) {
LOG.error("Error setting expression 'parameters' with value '", e);
throw e;
}
return invocation.invoke();
}
...
可以看到,ParametersInterceptor是继承了AbstractInterceptor,它的intercept()方法主要有如下几个操作:
1. 检查当前action是否是ActionSupport类型;
2. 判断请求方式是get还是post;
3. 如果是get,就创建Action的参数map;
4. 如果是post,就获取请求参数,将所有请求参数封装成一个Map,然后设置到InvocationContext中;
5. 如果当前方法在参数白名单中,则将其转换为一个SMDAction,并将其封装到contextMap中,最后添加到InvocationContext中;
6. 将请求参数map和contextMap中的所有参数添加到InvocationContext中。
2.2 创建Action的参数map
接下来让我们看看createActionParametersMap()方法的代码:
protected Map<String, Object> createActionParametersMap(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
Map<String, Object> parameters = new TreeMap<String, Object>();
if (action == null) {
return parameters;
}
ValueStack stack = invocation.getStack();
Map<String, Object> context = stack.getContext();
if (context instanceof ContextMap) {
context = ((ContextMap) context).getInternalMap(); // make sure we aren't using the ContextMap itself as a parameter
}
Map<String, Object> conversionErrors = new HashMap<String, Object>();
ActionContext ac = invocation.getInvocationContext();
Map<String, Object> extraContext = ac.getContextMap();
ReflectionContextFactory reflectionContextFactory = actionProxyFactory.getContainer().getInstance(ReflectionContextFactory.class);
OgnlContext contextMap = reflectionContextFactory.createOgnlContext(ac);
Map<String, Object> params = ActionContext.getContext().getParameters();
for (Iterator<String> iterator = params.keySet().iterator(); iterator.hasNext();) {
String name = iterator.next();
Object[] values = (Object[]) params.get(name);
if (values.length == 1) {
String parm = (String) values[0];
try {
ValueStack stackCopy = stack;
if (action instanceof CompoundRoot) {
CompoundRoot compound = (CompoundRoot) action;
List<Object> rootList = compound.getRoots();
if (rootList != null && rootList.size() == 1) {
stackCopy = new ValueStackFactory().createValueStack(stackCopy, rootList.get(0));
}
}
ReflectionContext reflectionContext = reflectionContextFactory.createActionContext(contextMap, action, stackCopy, context, parameters, true);
Object value = ReflectionContextState.get(context).get(name);
if (value instanceof List) {
List<Object> l = (List<Object>) value;
l.add(parm);
} else if (value instanceof String) {
List<Object> l = new ArrayList<Object>();
l.add(value);
l.add(parm);
ReflectionContextState.get(context).put(name, l);
} else if (value == null) {
ReflectionContextState.get(context).put(name, parm);
}
} catch (RuntimeException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Caught exception while setting property [" + name + "] to value [" + parm + "]", e);
}
if ((Boolean.TRUE.equals(invocation.getInvocationContext().get(StrutsStatics.STRUTS_DEVMODE)))) {
throw e;
} else {
Object existing = conversionErrors.get(name);
if (existing == null) {
conversionErrors.put(name, parm);
} else {
if (existing instanceof List) {
((List<Object>) existing).add(parm);
} else {
List<Object> l = new ArrayList<Object>();
l.add(existing);
l.add(parm);
conversionErrors.put(name, l);
}
}
}
}
}
}
if (conversionErrors.size() > 0) {
ActionMessage errors = new ActionMessage("struts.messages.conversion.errors");
List<Object> values = new ArrayList<Object>();
values.add(conversionErrors);
errors.setValues(values);
invocation.getInvocationContext().addError(errors);
}
return parameters;
}
该方法主要根据请求参数创建Action的参数map。具体来说,它是通过在ReflectionContext中存储参数来实现参数注入的。
2.3 获取请求参数
而retrieveParameters()方法则是用来获取请求参数的,其代码如下:
protected Map<String, Object> retrieveParameters(ActionInvocation invocation) throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
if (request instanceof StrutsRequestWrapper) {
request = ((StrutsRequestWrapper) request).getHttpRequest();
}
Map<String, Object> params = new HashMap<String, Object>(request.getParameterMap());
Map<String, String[]> uriParams = (Map<String, String[]>) request.getAttribute(ATTR_URI_PARAMETERS);
if (uriParams != null) {
for (Entry<String, String[]> entry : uriParams.entrySet()) {
if (!params.containsKey(entry.getKey())) {
params.put(entry.getKey(), entry.getValue());
}
}
}
return params;
}
可以看到,该方法主要是利用HttpServletRequest的getParameterMap()方法获取参数,同时从request的Attribute中获取URI参数。
2.4 SMDAction转换
如果当前方法在白名单中,则需要将其转换为SMDAction,并将其添加到contextMap中,最后添加到InvocationContext中,具体可参考如下代码:
if (acceptable) {
SMDAction smdAction = new SMDAction(parameters);
addActionError(invocation, smdAction.getActionErrors(), smdAction.getFieldErrors());
ValueStack stack = invocation.getStack();
Map<String, Object> smdData = smdAction.toMap(stack);
// As a convenience for non-annotations users, convert the
// request parameter store to a Map and add it to the context
Map<String, String[]> params = (Map<String, String[]>) contextMap.get("request");
if (params != null && params.size() > 0) {
contextMap.putAll(params);
}
contextMap.put("smd", smdData);
}
此时,我们可以通过${smd}
来访问该SMDAction对象。
3. ParametersInterceptor示例
下面将展现两个示例,以便更好地理解ParametersInterceptor的作用。
3.1 在Action类中接收参数
考虑一个类似于用户注册的场景,我们需要在Action类中接收用户的基本信息。在index.jsp页面中,我们使用如下表单向服务器发送请求:
<form action="register" method="post">
Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
Email: <input type="text" name="email" /><br />
Gender: <input type="text" name="gender" /><br />
<input type="submit" value="Register" /><br />
</form>
此时,我们需要在UserAction中接收这些参数,代码如下:
public class UserAction extends ActionSupport {
private String username;
private String password;
private String email;
private String gender;
// setters and getters
public String execute() {
System.out.println(username);
System.out.println(password);
System.out.println(email);
System.out.println(gender);
return SUCCESS;
}
}
这里可以看到,我们并没有手动向User类中注入参数,而是使用了自动绑定。此时,ParametersInterceptor就会自动帮我们将请求参数封装到Action类中去。
3.2 使用中文名接收参数
在有些场景中,我们需要使用中文名作为参数名。在使用ParametersInterceptor时,我们需要配置一个名字转换器,它能够将中文名转换为可以被Struts2识别的参数名。
<constant name="struts2.action.name.parser" value="com.opensymphony.xwork2.util.DefaultActionNameParser"/>
<constant name="struts.enable.SlashesInActionNames" value="true"/>
<!-- 确定所用的名称转换器 -->
<constant name="struts.i18n.encoding" value="UTF-8"/>
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<constant name="struts.devMode" value="false"/>
<!-- 中文参数名转换 -->
<constant name="struts.action.extension" value="action,/"/>
<constant name="struts.mapper.action.prefix.enabled" value="true"/>
<constant name="struts.mapper.action.prefix.crossNamespaces" value="true"/>
<constant name="struts.mapper.alwaysSelectFullNamespace" value="false"/>
<constant name="struts.mapper.idParameterName" value="id"/>
<constant name="struts.ui.theme" value="simple"/>
<constant name="struts.custom.i18n.resources" value="demo_message"/>
<action name="中文参数名测试" class="com.struts.ex03.action.Ex03Action">
<result name="success">/ex03.jsp</result>
<!-- 这里没有使用@Result注解 -->
</action>
此时,我们需要为接收中文参数的字段添加@ParamName
注解,具体如下:
public class Ex03Action extends ActionSupport {
private String 姓名;
private String 年龄;
private String 性别;
@Override
public String execute() throws Exception {
System.out.println(姓名);
System.out.println(年龄);
System.out.println(性别);
return SUCCESS;
}
@ParamName("姓名")
public void set姓名(String 姓名) {
this.姓名 = 姓名;
}
@ParamName("年龄")
public void set年龄(String 年龄) {
this.年龄 = 年龄;
}
@ParamName("性别")
public void set性别(String 性别) {
this.性别 = 性别;
}
public String get姓名() {
return 姓名;
}
public String get年龄() {
return 年龄;
}
public String get性别() {
return 性别;
}
}
以上就是对ParametersInterceptor拦截器的完整攻略了。如果您需要了解更多的Struts2源码分析的内容,可以关注我的博客,我会不断更新相关的文章。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Struts2源码分析之ParametersInterceptor拦截器 - Python技术站