博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
聊聊springmvc中controller的方法的参数注解
阅读量:6269 次
发布时间:2019-06-22

本文共 10090 字,大约阅读时间需要 33 分钟。

绪论

相信接触过springmvc的同学都知道,在springmvc的控制层中,我们在方法的参数中可以使用注解标识。比如下面例子:

public Map
login(@PathVariable("loginParams") String loginParams)复制代码

@PathVariable注解就标识了这个参数是作为一个请求地址模板变量的(不清楚的同学可以先学习一下restful设计风格)。这些注解都是spring内置注解,那么我们可不可以自定义注解来实现自己的业务逻辑处理呢?答案是可以的,spring团队的一大设计哲学思想就是让自己的系统有无限可能性的拓展。spring框架底层又是如何解析这些参数的注解的呢?

那么在学习自定义参数注解之前,我们先了解一下spring底层是怎么来解析这些注解参数的。实际上,这些处理过程是要涉及到配置文件的加载和解析以及一堆的各种处理,小弟功力尚浅,就分析不到那么多了,只是简单过一下。

内置参数注解的解析

下面,我们从源码角度来分析:

首先,sping定义了一个统一的方法参数注解解析接口HandlerMethodArgumentResolver,所有方法参数解析类都需要实现这个接口,接口很简单,定义了两个方法:

public interface HandlerMethodArgumentResolver {    /**     * 判断方法参数是否包含指定的参数注解     * 含有返回true,不含有返回false     */    boolean supportsParameter(MethodParameter parameter);    /**     * 在给定的具体的请求中,把方法的参数解析到参数值里面,返回解析到的参数值,没有返回null     * 只有在supportsParameter返回true的时候,resolveArgument方法才会执行     */    Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;}复制代码

现在,带着大家看看@PathVariable参数注解的解析具体过程,源代码如下:

public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver        implements UriComponentsContributor {        /*         * 这里省略其它方法         *         /    @Override        public boolean supportsParameter(MethodParameter parameter) {            // 不含有PathVariable注解,返回false            if (!parameter.hasParameterAnnotation(PathVariable.class)) {                return false;            }            // PathVariable注解的参数类型是Map类型            if (Map.class.isAssignableFrom(parameter.getParameterType())) {                String paramName = parameter.getParameterAnnotation(PathVariable.class).value();                return StringUtils.hasText(paramName);            }            return true;        }     // PathVariableMethodArgumentResolver没有重写resolveArgument,直接使用AbstractNamedValueMethodArgumentResolver默认行为     /*      * 如果supportsParameter返回true,在这里真正处理参数      *      */     protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,                 ModelAndViewContainer mavContainer, NativeWebRequest request) {             String key = View.PATH_VARIABLES;             int scope = RequestAttributes.SCOPE_REQUEST;             Map
pathVars = (Map
) request.getAttribute(key, scope); if (pathVars == null) { pathVars = new HashMap
(); request.setAttribute(key, pathVars, scope); } // 把参数的key-value放进请求域,也就是把值赋给了方法参数,比如请求路径是: api/v1/task/{id},方法参数@PathVariable("id") String taskId,那么此时name=taskId, org=id的值 // 当然,怎么把请求地址中对应的值获取出来,不在这篇博客的讨论范畴。大家只要记得参数注解是这样解析处理的就可以了 pathVars.put(name, arg); }}复制代码

AbstractNamedValueMethodArgumentResolver的resolveArgument方法如下

public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {        Class
paramType = parameter.getParameterType(); // 获取请求参数的key-value NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); // 解析参数名 Object arg = resolveName(namedValueInfo.name, parameter, webRequest); if (arg == null) { if (namedValueInfo.defaultValue != null) { arg = resolveDefaultValue(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) { handleMissingValue(namedValueInfo.name, parameter); } arg = handleNullValue(namedValueInfo.name, arg, paramType); } else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveDefaultValue(namedValueInfo.defaultValue); } // 数据绑定 if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); try { arg = binder.convertIfNecessary(arg, paramType, parameter); } catch (ConversionNotSupportedException ex) { throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } catch (TypeMismatchException ex) { throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } } /* * 最后的处理是交给handleResolvedValue,handleResolvedValue方法是抽象方法,我们回来看看一下PathVariableMethodArgumentResolver的handleResolvedValue方法是抽象方法的具体实现 * */ handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; }复制代码

可以知道,@PathVariable标识的参数,会被对应参数解析器把对应值解析到一个Map结构中保存到request scope。

总的来说,实现处理注解参数思路还是比较简单的,定义一个类实现HandlerMethodArgumentResolver接口,在对应方法里面进行处理就可以了。接下来我们就来一次自定义注解参数解析的实战。

自定义注解参数解析演练

我们模拟一下获取当前任务信息。

首先我们定义一个注解

package top.mingzhijie.demo.springmvc.anntation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** 代表当前任务 * @author wunanliang * @date 2017/10/21 * @since 1.0.0 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public @interface CurrentTask {    String value() default "";}复制代码

接着模拟一个业务逻辑处理服务类

package top.mingzhijie.demo.springmvc.method.arguments.anntation;import top.mingzhijie.demo.springmvc.entity.Task;import java.util.HashMap;import java.util.Map;/** * 模拟任务业务类 * * @author wunanliang * @date 2017/10/21 * @since 1.0.0 */public class TaskService {    private static Map
taskMap = new HashMap
(); static { taskMap.put("001", new Task("task1", 10, true)); taskMap.put("002", new Task("task2", 1, false)); taskMap.put("003", new Task("task3", 20, false)); } public static Task findTaskById(String taskId) { return taskMap.get(taskId); }}复制代码

编写任务类

package top.mingzhijie.demo.springmvc.entity;/** * @author wunanliang * @date 2017/10/21 * @since 1.0.0 */public class Task {    private String name;    private int resolvedCount; // 参与人数    private boolean allowStudent;    public Task(){}    public Task(String name, int resolvedCount, boolean allowStudent) {        this.name = name;        this.resolvedCount = resolvedCount;        this.allowStudent = allowStudent;    }    public boolean isAllowStudent() {        return allowStudent;    }    public void setAllowStudent(boolean allowStudent) {        this.allowStudent = allowStudent;    }    public int getResolvedCount() {        return resolvedCount;    }    public void setResolvedCount(int resolvedCount) {        this.resolvedCount = resolvedCount;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public String toString() {        return "Task{" +                "name='" + name + '\'' +                ", resolvedCount=" + resolvedCount +                ", allowStudent=" + allowStudent +                '}';    }}复制代码

编写注解参数处理类

package top.mingzhijie.demo.springmvc.method.arguments.anntation;import org.springframework.core.MethodParameter;import org.springframework.web.bind.support.WebDataBinderFactory;import org.springframework.web.context.request.NativeWebRequest;import org.springframework.web.method.support.HandlerMethodArgumentResolver;import org.springframework.web.method.support.ModelAndViewContainer;import top.mingzhijie.demo.springmvc.anntation.CurrentTask;import top.mingzhijie.demo.springmvc.entity.Task;/** * @author wunanliang * @date 2017/10/21 * @since 1.0.0 */public class TaskHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {    public boolean supportsParameter(MethodParameter methodParameter) {        boolean hasAnn = methodParameter.hasParameterAnnotation(CurrentTask.class);        return hasAnn;    }    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {        Task task = null;        String curTaskId = (String) nativeWebRequest.getParameter("cur_task_id");        if (curTaskId != null && !"".equals(curTaskId)) {            task = TaskService.findTaskById(curTaskId);        }        if (task == null) {            System.out.println("为找到对应的任务");        } else {            if (task.isAllowStudent()) {                System.out.println("当前任务不允许学生参加哦");            } else {                System.out.println("学生可以参加当前任务哦");            }        }        return task;    }}复制代码

编写前端控制类

package top.mingzhijie.demo.springmvc.method.arguments.anntation;import org.springframework.web.bind.annotation.*;import top.mingzhijie.demo.springmvc.anntation.CurrentTask;import top.mingzhijie.demo.springmvc.entity.Task;import java.util.HashMap;import java.util.Map;/** * @author wunanliang * @date 2017/10/21 * @since 1.0.0 */@RestController@RequestMapping("/tasks")public class TaskController {    // 这里使用@CurrentTask来表示Task参数    @RequestMapping(value = "/join", method = RequestMethod.GET)    @ResponseBody    public Map
gJoinTask(@RequestParam("cur_task_id") String taskId, @CurrentTask Task task) { System.out.println(task); Map
map = new HashMap
(); map.put("cur_task", task); return map; }}复制代码

配置文件配置注解参数解析bean

复制代码

运行,输入地址

获取到任务信息json数据:

{    "cur_task": {        "name": "task1",        "resolvedCount": 10,        "allowStudent": true    }}复制代码

可以看到,@CurrentTask标识的参数Task,在方法中就可以获取到经过TaskHandlerMethodArgumentResolver处理过的任务

使用场景

在我们web请求中,往往需要客户端带会token来进行身份验证,这样我们可以自定义参数注解来在指定的注解解析类里面来进行token的合法性的判断。

好啦这篇文章就到这里了~~关于spring如何绑定参数值不在这篇博客讨论的范畴,这里只是简单和大家聊聊spring如何获取注解的参数进行处理,以及给处一个学习的demo。

转载地址:http://ngppa.baihongyu.com/

你可能感兴趣的文章
Fix-Mapped Addresses
查看>>
fmt标签如何计算两个日期之间相隔的天数
查看>>
Spark核心技术原理透视一(Spark运行原理)
查看>>
《Gradle权威指南》--Gradle任务
查看>>
IntelliJ IDEA创建文件时自动填入作者时间 定制格式
查看>>
Android app启动activity并调用onCreate()方法时都默默地干了什么?
查看>>
远程监视jboss应用java内存的配置
查看>>
前端如何接收 websocket 发送过来的实时数据
查看>>
JavaWeb下载文件response
查看>>
Laravel的三种安装方法总结
查看>>
SpringMVC加载配置Properties文件的几种方式
查看>>
C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginat...
查看>>
java 项目相关 学习笔记
查看>>
numpy opencv matlab eigen SVD结果对比
查看>>
WPF获取某控件的位置,也就是偏移量
查看>>
Boost C++ 库 中文教程(全)
查看>>
solr查询优化(实践了一下效果比较明显)
查看>>
jdk目录详解及其使用方法
查看>>
说说自己对RESTful API的理解s
查看>>
通过layout实现可拖拽自动排序的UICollectionView
查看>>