Struts2源码分析之ParametersInterceptor拦截器

下面我将就“Struts2源码分析之ParametersInterceptor拦截器”的完整攻略给您讲解,全文将分别从以下几个方面展开:

  1. ParametersInterceptor介绍
  2. ParametersInterceptor源码分析
  3. 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技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • SpringMVC+MyBatis 事务管理(实例)

    SpringMVC+MyBatis 事务管理(实例) 在使用SpringMVC和MyBatis进行开发时,我们通常需要使用事务管理来确保数据的一致性和完整性。本文将介绍如何在SpringMVC和MyBatis中使用事务管理,并提供两个示例说明。 步骤一:配置数据源和事务管理器 首先,我们需要配置数据源和事务管理器。可以通过在applicationContex…

    Java 2023年5月17日
    00
  • 深入学习Java编程中的字符串的进阶使用

    深入学习Java编程中的字符串的进阶使用 Java中的字符串是程序中常用的数据类型之一,学习好字符串的使用可以极大地提高程序的处理能力,本文将深入学习Java编程中字符串的进阶使用。 1. 字符串创建 首先我们来看字符串的创建,Java中我们可以使用以下三种方式来创建字符串: 1.1 直接赋值 String str = "hello world&q…

    Java 2023年5月26日
    00
  • Spring MVC全局异常处理和单元测试_动力节点Java学院整理

    Spring MVC是一种流行的Java Web框架,其拥有全局异常处理机制,可以在程序抛出异常后,统一处理并返回指定的错误信息。本篇攻略主要包含两部分内容,分别是Spring MVC全局异常处理和单元测试。 一、Spring MVC全局异常处理 1.1 在Spring配置文件中配置异常处理拦截器 在Spring的配置文件中,可以配置一个全局的异常处理拦截器…

    Java 2023年6月15日
    00
  • jsp输出当前时间的实现代码

    实现jsp输出当前时间需要使用Java中的Date类和SimpleDateFormat类。下面是完整的攻略: 导入相关的类和包 在jsp页面中需要导入以下两个类: <%@ page import="java.util.Date" %> <%@ page import="java.text.SimpleDateF…

    Java 2023年6月15日
    00
  • 深入学习MyBatis中的参数(推荐)

    深入学习MyBatis中的参数(推荐)攻略 MyBatis作为一个高性能的ORM框架,除了SQL语句的编写,还有一个重要且常被忽略的部分就是参数的传递。本攻略将深入讲解MyBatis中参数的使用方法,带你彻底掌握参数传递的技巧。 正文 #{parameter_name} 普通类型 MyBatis中使用#{parameter_name}方式,可以直接在SQL语…

    Java 2023年5月19日
    00
  • Request获取Session的方法总结

    Request获取Session的方法总结 Session是Web开发中常见的一种用户状态管理方式,可以在不同的页面之间传递和共享数据。在Python Web框架中,常用的Session实现方式是通过Request对象获取Session。以下是关于Request获取Session的方法总结。 通过Request的cookies属性获取Session Sess…

    Java 2023年6月15日
    00
  • springboot jackson配置教程

    下面是SpringBoot Jackson配置教程的完整攻略,包括Jackson的基本功能、注解、配置、示例等详细步骤。 一、什么是Jackson Jackson是Java编程语言中用于将Java对象序列化为JSON(JavaScript对象表示)和反序列化从JSON返回Java对象的库。它是一种流行的JSON库,可以快速轻松地将Java对象转换为JSON格…

    Java 2023年5月26日
    00
  • Spring Boot之内嵌tomcat版本升级操作示例

    下面我将详细讲解“Spring Boot之内嵌tomcat版本升级操作示例”的完整攻略。该攻略包含以下步骤: 1. 查看内嵌Tomcat版本 我们首先需要查看当前Spring Boot项目内嵌的Tomcat版本。在pom.xml文件中,可以找到以下代码: <dependency> <groupId>org.springframewor…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部