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与C++有什么不同?

    Java和C++是两种广受欢迎的编程语言,它们有许多不同之处,下面我将详细讲解Java与C++的不同点: 内存管理 C++程序员需要手动分配和释放内存。在C++中,我们使用new和delete操作符来实现动态内存管理。如果没有正确地释放内存,会导致内存泄漏。 而Java使用垃圾收集器来管理内存,程序员无需手动分配或释放内存。垃圾收集器自动回收无用的对象,使程…

    Java 2023年4月28日
    00
  • Tomcat 实现WebSocket详细介绍

    Tomcat 实现WebSocket详细介绍 什么是WebSocket? WebSocket是HTML5中的一个协议,它的目标是在Web浏览器和服务器之间建立实时、双向的通信会话,以便实现更好的实时性和降低通信量。以前,为了实现实时通信,使用一些技术如Ajax、Comet等技术,但它们都存在着一些问题,而WebSocket协议能够有效地解决这些问题,因此得到…

    Java 2023年5月19日
    00
  • Java实现学生管理系统(IO版)

    Java实现学生管理系统(IO版)攻略 简介 本文将介绍如何使用Java语言实现学生管理系统,使用的是Java中的IO流处理方式,实现添加学生信息、查询学生信息、删除学生信息、修改学生信息等功能。 实现步骤 1.创建项目 首先,我们需要创建一个Java项目,可以使用Eclipse或者其他Java开发工具。 2.创建类和文件 在src目录下创建”com.stu…

    Java 2023年5月23日
    00
  • Maven入门教程之如何在idea中配置Maven

    首先,我们需要确保已经安装了Maven和IntelliJ IDEA。 接下来,按照以下步骤配置Maven: 步骤一:在IntelliJ IDEA中创建一个新的Maven项目 打开IntelliJ IDEA,点击“Create New Project”。 在左侧面板中选择“Maven”。 在右侧面板中选择“Create from archetype”。 在下拉…

    Java 2023年5月20日
    00
  • javap命令的使用技巧

    当你需要了解Java程序在编译后生成的字节码时,javap命令是一个很有用的工具。它能够反编译.class文件并输出字节码指令的信息,甚至还能够显示源代码中的行号和本地变量信息。接下来,我们将详细讲解javap命令的使用技巧,以便你能够充分利用这个强大的工具。 命令格式 首先,我们来介绍一下javap命令的基本格式: javap [options] clas…

    Java 2023年5月20日
    00
  • Spring Security实现HTTP认证

    下面是关于“Spring Security实现HTTP认证”的完整攻略。 什么是Spring Security Spring Security是基于Spring框架的安全框架。它提供了一系列的安全服务,包括身份验证(Authentication)、授权(Authorization)等,用于保护Web应用或Web服务。 实现HTTP认证的步骤 下面是实现HTT…

    Java 2023年5月20日
    00
  • JAVA velocity模板引擎使用实例

    下面是关于“JAVA velocity模板引擎使用实例”的详细讲解。 什么是Velocity模板引擎 Velocity模板引擎是一种基于文本模板的渲染引擎,支持将变量、逻辑处理、循环等结构和语法写在模板里面,然后使用程序将模板中的变量值替换成真正的值后输出。Velocity模板引擎是一种轻量级的模板引擎,具有易读易懂、易扩展等特点,被广泛应用于各种JAVA …

    Java 2023年5月19日
    00
  • SpringBoot快速配置数据源的方法

    SpringBoot快速配置数据源的方法 在SpringBoot中,可以非常简单快速地配置数据源,一般使用Spring Boot Starter来简化开发过程。 步骤1:添加依赖 在pom.xml中添加如下依赖: <dependency> <groupId>org.springframework.boot</groupId&gt…

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