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日

相关文章

  • Java中IO流文件读取、写入和复制的实例

    下面是Java中IO流文件读取、写入和复制的实例的完整攻略。 IO流简介 在Java中,输入输出都是通过流(Stream)来实现的,也就是将数据源或者目的地以流的形式组织起来,以字节为基础的流就是字节流,以字符为基础的流就是字符流。在Java中,IO流分为四个抽象类:InputStream、OutputStream、Reader和Writer。 文件读取 J…

    Java 2023年5月20日
    00
  • 常见的线程池调度算法有哪些?

    以下是关于常见的线程池调度算法的完整使用攻略: 常见的线程池调度算法 常见的线程调度算法以下几种: 1. 固定大小线程池 固定大小线程池是指线程池中的线程数量是固定的,不随着任务的增加而增加。当线程池中的线程都在执行任务时,新的任务会被放入任务队列中等待。 以下是一个使用固定大小线程池的示例: ExecutorService executorService …

    Java 2023年5月12日
    00
  • 什么是虚拟机?

    以下是关于虚拟机的完整使用攻略: 什么是虚拟机? 虚拟机是一种软件,它模拟了一台计算机的硬件和操作系统,使得用户可以在一台计算机上运行多个操作系统和应用程序。虚拟机可以在不同的操作系统之间提供互操作性,同时也可以提供更好的资源利用率和更好的安全性。 如何使用虚拟机? 使用虚拟机需要以下步骤: 下载并安装虚拟机软件,如VirtualBox、VMware等。 下…

    Java 2023年5月12日
    00
  • springMVC 用户登录权限验证实现过程解析

    SpringMVC 用户登录权限验证实现过程解析 为什么需要用户登录权限验证 在Web应用程序中,用户登录权限验证通常被认为是必不可少的功能。这是因为在实际应用中往往会存在很多需要进行特殊权限验证的操作。 例如,用户在购物网站上进行订单提交前必须先进行登录验证,用户在博客网站上进行评论前必须先进行登录验证等等。 这些验证不仅能够保证系统的安全性,也能够使得用…

    Java 2023年5月16日
    00
  • Mybatis批量插入数据的两种方式总结与对比

    我会为您提供一个详细的攻略,以讲解 Mybatis 批量插入数据的两种方式,分别是 foreach 方式和 batch 方式。 1. foreach 方式 foreach 方式是通过迭代集合或数组的方式来实现批量插入数据的。示例如下: 1.1 示例 <insert id="batchInsert"> insert into t…

    Java 2023年5月20日
    00
  • Java连接sqlserver2008数据库代码

    下面是连接sqlserver2008数据库的完整攻略。 安装sqljdbc驱动 首先需要安装sql jdbc驱动,可以到以下网址下载对应版本的驱动:https://docs.microsoft.com/zh-cn/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-s…

    Java 2023年6月1日
    00
  • Java实现简单的银行管理系统的示例代码

    下面我将详细介绍如何实现一个简单的银行管理系统,包括设计思路、代码实现和示例演示。 设计思路 这个银行管理系统需要实现以下功能:1. 新增账户2. 存款3. 取款4. 查询账户信息 考虑到以上需要,我们可以设计出如下的类结构:- Account类,用于存储账户信息,包括账户号、姓名、余额等属性,以及存款和取款的方法。- Bank类,用于管理所有的账户,包括新…

    Java 2023年5月19日
    00
  • InputStreamReader 和FileReader的区别及InputStream和Reader的区别

    关于InputStreamReader和FileReader的区别,以及InputStream和Reader的区别,我们需要从数据输入、数据输出两个方面来讲解。 InputStreamReader 和 FileReader 的区别 InputStreamReader和FileReader都是读取字符流的类,主要区别在于它们输入的数据源不同。 InputStr…

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