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源码分析的内容,可以关注我的博客,我会不断更新相关的文章。

阅读剩余 87%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Struts2源码分析之ParametersInterceptor拦截器 - Python技术站

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

相关文章

  • 详解Java代码常见优化方案

    详解Java代码常见优化方案 Java作为一门常用的编程语言,其代码的性能优化是开发过程中需要考虑的一项重要问题。本文将分析常见的Java代码优化方案,以及如何在实际项目中应用这些优化方案,提高程序的运行效率。 1. 合理使用变量 在Java中,变量使用的不合理将会带来很多性能问题。例如,如果在循环中声明一个大对象,将会带来显著的内存压力,降低程序的运行效率…

    Java 2023年5月23日
    00
  • 一文秒懂通过JavaCSV类库读写CSV文件的技巧

    一文秒懂通过JavaCSV类库读写CSV文件的技巧 什么是CSV文件 CSV文件(Comma-Separated Values,逗号分隔值)是一种常见的文本格式,用来存储结构化数据,其每行都表示一条记录,每个记录使用逗号或其他分隔符分割字段,每个字段的值可以用引号括起来。 例如,以下CSV文件表示了三个人的基本信息: Name,Age,Gender &quo…

    Java 2023年5月19日
    00
  • Java线程池由浅入深掌握到精通

    Java线程池从入门到精通 Java线程池是一种多线程处理机制,用于管理和调度多个线程。通过线程池,可以复用线程、控制线程数量,从而提高程序并发处理能力和资源利用率。 1. 初识Java线程池 1.1 线程池的优点 使用线程池具有以下优点: 降低线程创建和销毁带来的性能损耗; 通过重用线程来优化程序性能; 可以对线程数量进行限制和控制,避免系统资源被消耗殆尽…

    Java 2023年5月19日
    00
  • java中字符串参数化符号${}的解析

    在Java中,字符串参数化符号${}是用于字符串模板中的占位符,可以在占位符中插入变量名,并在运行时将其动态替换为相应的值。 下面是解析${}的基本过程: 识别占位符 在字符串中找到${的位置,表示占位符开始。 识别变量名 在占位符中寻找变量名,变量名可以是任意的合法Java标识符。 识别默认值 如果占位符中包含默认值,使用冒号 : 来和变量名分隔。默认值是…

    Java 2023年5月26日
    00
  • springmvc利用jquery.form插件异步上传文件示例

    针对你的问题,我可以提供以下完整攻略,希望能够帮助你。 一、简介 在web应用中,文件上传功能是比较常见的。而在Spring MVC框架中,文件上传则可以通过SpringMVC提供的MultipartHttpServletRequest类来完成,但是这种方式需要整个页面提交才能上传文件,速度比较慢,因此在前端使用异步上传文件功能可以大幅提升用户体验。而jqu…

    Java 2023年5月31日
    00
  • java中计算字符串长度的方法及u4E00与u9FBB的认识

    Java中计算字符串长度的方法 在Java中计算字符串长度是一项常见的任务,在实际开发中也有很多场景需要计算字符串的长度。本攻略将介绍Java中计算字符串长度的方法以及相关的知识点,包括中文字符的Unicode编码等。 1. String类的length()方法 Java中的String类提供了length()方法来计算字符串的长度,它返回的是字符串中的字符…

    Java 2023年5月20日
    00
  • 经常使用的JDBC连接数据库方式

    JDBC是一种连接数据库的通用方式。在Java语言中,通过使用JDBC API,可以连接各种类型的关系型数据库,如MySQL、Oracle、PostgreSQL等。下面我们来详细讲解一下经常使用的JDBC连接数据库方式的完整攻略。 步骤一:加载驱动程序 在使用JDBC连接数据库之前,我们需要先加载相应的数据库驱动程序。常见的数据库驱动程序有mysql-con…

    Java 2023年5月20日
    00
  • 解决JAVA非对称加密不同系统加密结果不一致的问题

    为了解决JAVA非对称加密在不同系统中加密结果不一致的问题,我们需要在代码中指定加密算法的provider,这样可以使加密在不同系统中的表现一致。 下面是解决该问题的步骤: 确认加密算法的provider 我们需要明确加密算法的provider信息,以RSA算法为例,该算法的provider为BC(BouncyCastle)。 将provider添加进代码 …

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