
这篇文章就针对这个痛点展开:一方面,把SpringMVC中JSP前台获取参数的常用方式(比如request对象、ModelAndView、@ModelAttribute注解等)拆解得明明白白,不仅讲每种方法的基本写法,更点出它们的适用场景(比如什么时候用request更直接,什么时候用ModelAndView更灵活); 重点聚焦EL表达式的实战技巧——从“${}”的基础取值,到如何结合Scope(请求域、会话域)精准拿值,再到解决“空值防错”“复杂对象嵌套取值”等真实场景问题。
不管你是刚入门SpringMVC的新手(想快速掌握正确方法),还是想优化代码的开发者(想告别冗余写法),读完这篇文章都能get:哪些方式是“花架子”,哪些是“真好用”,以及如何用EL表达式把“拿参数”这件事做得更简洁、更高效,直接解决前台数据获取的实际困惑。
你有没有过这种情况?用SpringMVC写项目时,后端明明传了参数,JSP页面却死活拿不到;或者好不容易拿到了,代码写得跟裹脚布一样长,回头看自己都嫌啰嗦?我去年帮一个做电商系统的朋友调Bug时,就碰到过这种事儿——他用request.getParameter("username")
取登录参数,结果中文用户名全是乱码,查了半天才发现没设置request.setCharacterEncoding("UTF-8")
。其实SpringMVC给JSP前台传参数的方式不少,但关键是要“选对方法用对场景”,今天就把我踩过的坑、摸透的技巧全抖出来。
SpringMVC JSP前台获取参数的常用方式:从基础到场景适配
SpringMVC中,JSP前台获取后端参数的核心逻辑,其实是“后端把数据放到某个作用域(比如请求域、会话域)里,JSP从对应作用域中取出数据”。我把日常开发中最常用的3种方式拆解清楚,连“什么时候该用什么”都给你标明白:
不管你是刚学SpringMVC的新手,还是写了几年代码的老鸟,request
对象肯定是你最早接触的获取参数方式——毕竟它是Servlet的核心API之一。基本用法很简单:用request.getParameter("参数名")
就能拿到后端传过来的单个参数值;如果是复选框这种多值参数,就用request.getParameterValues("参数名")
,返回一个字符串数组。
但我得提醒你,别嫌它“基础”就小看它。去年我帮朋友调的那个电商项目,他就是因为没搞懂request
的编码问题栽了跟头:他后端用request.setCharacterEncoding("UTF-8")
设置了编码,但JSP页面的pageEncoding
没改成UTF-8,结果中文参数全成了“问号”。后来我让他把JSP的加上,再配合
request
的编码设置,问题才解决。
request
对象的优点是“直接”——不需要依赖Spring的额外API,拿到就能用;但缺点也明显:如果参数是复杂对象(比如用户实体类),你得用request.getAttribute("user")
先拿到对象,再强制转型成User
类,才能取user.getName()
,代码写起来特别繁琐。这种方式适合什么场景? 当你只需要获取单个简单参数(比如id、用户名),或者项目里还混着Servlet代码的时候,用request
最稳妥。
如果你用SpringMVC的Controller
返回ModelAndView
对象,那传递参数就更顺手了——只需要在ModelAndView
里用addObject("key", value)
把数据存进去,JSP页面直接就能拿到。比如你要返回一个用户列表页面,代码可以这么写:
@RequestMapping("/user/list")
public ModelAndView userList() {
ModelAndView mav = new ModelAndView("user/list");
List userList = userService.findAll();
mav.addObject("userList", userList); // 把用户列表存进ModelAndView
return mav;
}
JSP页面里用request.getAttribute("userList")
就能拿到这个列表——或者更方便的是,直接用后面要讲的EL表达式${userList}
。
我为什么说它是“打包神器”?因为它能把“要跳转的视图”和“要传递的数据”一次性返回,特别适合需要同时返回视图和数据的场景(比如列表页、详情页)。但它也有局限:如果你的Controller
方法返回的是String
(只返回视图名),那ModelAndView
就用不上了——这时候得用Model
或者Map
来传数据(其实Model
的底层就是ModelAndView
的简化版)。
Spring官方文档里也提到,ModelAndView
是“传统SpringMVC项目中最常用的视图-数据传递方式”,尤其是在需要精细控制视图跳转的时候,它比@ResponseBody
更灵活。
如果你的参数是一个复杂的实体类(比如用户注册的表单数据,包含用户名、密码、邮箱),用request
或者ModelAndView
一个个传参数,简直是“自讨苦吃”。这时候@ModelAttribute
注解就派上用场了——它能自动把表单参数绑定到实体类对象上,后端直接拿对象用,JSP页面也能直接取对象的属性。
比如用户注册的表单:
用户名:
密码:
邮箱:
后端Controller
可以这么写:
@RequestMapping("/user/register")
public String register(@ModelAttribute("user") User user) {
// 直接用user.getUsername()、user.getPassword()就能拿到参数
userService.save(user);
return "success";
}
JSP页面里,你想取用户名,直接写${user.username}
就行——不用再手动转型,也不用写一堆request.getAttribute()
。
我之前做过一个教育类项目,里面的“课程报名”表单有12个字段,用@ModelAttribute
绑定到CourseApply
类后,代码量减少了三分之一,而且再也没出现过“漏传参数”的问题。这种方式适合什么场景? 当你需要处理表单提交、或者传递复杂实体类参数的时候,@ModelAttribute
能帮你省很多事儿。
为了让你更清楚这几种方式的区别,我做了个对比表格—— 你保存下来,用到的时候直接查:
方式名称 | 基本用法 | 优缺点 | 适用场景 |
---|---|---|---|
request对象 | request.getParameter(“key”) request.getAttribute(“key”) |
优点:直接、不依赖Spring; 缺点:复杂对象需转型、代码繁琐 |
简单参数获取、混写Servlet代码的项目 |
ModelAndView | mav.addObject(“key”, value) 返回视图+数据 |
优点:视图与数据打包、适合多数据传递; 缺点:需返回ModelAndView对象 |
需要返回视图+数据的场景(如列表页、详情页) |
@ModelAttribute | @ModelAttribute(“key”) 绑定实体类 | 优点:自动绑定复杂对象、减少代码; 缺点:依赖Spring注解、适合表单场景 |
表单提交、复杂实体类参数传递 |
EL表达式实战:用最简洁的方式解决90%的参数获取问题
讲完了后端传参数的方式,咱再说说JSP前台最“好用”的参数获取工具——EL表达式(Expression Language)。你有没有过这种体验?写JSP的时候,一堆或者
,代码里全是Scriptlet标签,看起来特别乱?EL表达式就是为了解决这个问题而生的——它用
${key}
代替了繁琐的Java代码,而且还能自动处理作用域、空值等问题。
${key}
搞定“默认作用域”的参数EL表达式的核心逻辑很简单:你写${key}
,它会自动从pageScope
→requestScope
→sessionScope
→applicationScope
这四个作用域里找对应的key
,找到第一个就返回。比如后端用ModelAndView
传了user
对象,你在JSP里写${user.username}
,EL会自动从requestScope
里找到user
,然后取出username
属性——完全不用你手动写request.getAttribute("user")
再转型。
我之前做过一个博客项目,原本用了一堆,后来改成EL表达式后,JSP代码干净了很多,连前端工程师看了都夸“易读”。这里要注意一个小技巧:如果多个作用域里有同名的
key
(比如requestScope
和sessionScope
都有user
),你可以用${requestScope.user.username}
或者${sessionScope.user.username}
指定作用域,避免取错值。
EL表达式可不止能取简单参数,它还能处理嵌套对象、集合、空值判断这些复杂场景——我敢说,90%的前台参数获取问题,用EL都能解决。
比如嵌套对象:如果user
对象里有一个Address
属性(包含province
和city
),你要取用户所在的省份,直接写${user.address.province}
就行,EL会自动帮你调用user.getAddress().getProvince()
;
比如集合:如果后端传了userList
列表,你要遍历它,配合JSTL的c:forEach
标签,写c:forEach items="${userList}" var="user"
,然后在循环里用${user.username}
取每个用户的用户名——比用Java代码遍历List
简单10倍;
再比如空值判断:你有没有遇到过“取到null值导致页面报错”的情况?用EL的empty
运算符就能解决——${empty user}
会判断user
是否为null或者空集合,你可以用它做条件渲染:c:if test="${not empty user}"
,这样当user
为空时,就不会渲染对应的内容了。
我去年做一个酒店预订系统时,就用EL的empty
解决了“房间列表为空时显示‘暂无房间’”的问题——之前用Java代码写if (roomList == null || roomList.size() == 0)
,现在用${empty roomList}
,代码简洁又不容易错。
虽然EL表达式好用,但我也踩过几个坑,今天全告诉你:
${user.username}
,但页面显示的是${user.username}
而不是具体值——这大概率是JSP页面没开启EL表达式支持。解决方法很简单:在JSP顶部加
,或者在web.xml里配置false
(适合Servlet 2.4及以上版本)。 ${userList[0].username}
,如果userList
是空的,会抛出IndexOutOfBoundsException
——这时候你得先用${not empty userList}
判断,再取元素。 pageContext
、param
),其中param
等价于request.getParameter()
,比如${param.username}
就等于request.getParameter("username")
——但如果你要取request
域里的user
对象,别用param.user
,得用requestScope.user
,不然会取错。 关于EL表达式的权威规范,你可以参考Oracle官方的JSP 2.3 specification(https://download.oracle.com/otndocs/jcp/jsp-2_3-mrel2-spec/index.htmlnofollow),里面详细讲了EL的语法和隐含对象——我做项目时遇到不懂的问题,都会翻这个文档。
你有没有发现?不管是用request
还是ModelAndView
传参数,最终在JSP前台,用EL表达式取参数都是最简洁的——它把复杂的Java代码转换成了“人能看懂的表达式”。我 你下次写JSP的时候,试试把标签换成EL表达式,你会发现“写前台代码也能很轻松”。
如果你按这些方法试了,遇到“EL没生效”或者“参数取错”的问题,欢迎留言告诉我——我帮你一起排查,毕竟我也踩过同样的坑~
用request对象取中文参数总是乱码怎么办?
这问题我去年帮朋友调电商项目时也碰到过,核心是前后端编码没统一。你得先在后端代码里加request.setCharacterEncoding(“UTF-8”),确保请求的参数编码是UTF-8;再把JSP页面的page指令加上pageEncoding=”UTF-8″,比如。别只改后端或者只改JSP,必须两端都统一,中文参数才不会变成问号或者乱码。
ModelAndView和@ModelAttribute到底有啥区别?
其实是适用场景不一样。ModelAndView更像“视图+数据的打包器”,比如你要跳转到用户列表页,得把列表数据和要跳转的视图名一起返回,这时候用mav.addObject()把数据存进去,再返回ModelAndView对象最方便;而@ModelAttribute是用来“绑定复杂实体类”的,比如用户注册表单有用户名、密码、邮箱很多字段,用这个注解能自动把表单里的参数绑到User对象上,不用你一个个用request.getParameter()取了再赋值,省很多啰嗦代码。
写EL表达式页面却显示${…}原样,没生效怎么办?
这是JSP默认忽略了EL表达式的问题,我之前写博客项目时也踩过这坑。你可以直接在JSP页面顶部加个page指令:,这样页面就会解析EL表达式了;如果你的项目用的是Servlet 2.4及以上版本,也能在web.xml里配置false,全局开启EL支持,不用每个页面都加指令。
EL表达式怎么取嵌套对象里的属性?比如用户的地址省份?
不用写复杂的get方法链,直接用点语法嵌套就行。比如User对象里有个Address属性,Address里又有province(省份)字段,你在JSP里直接写${user.address.province},EL会自动帮你调用user.getAddress().getProvince(),把嵌套的属性值取出来。我做酒店预订系统时,取用户的联系方式就是这么写的,比用Java代码简洁多了。
多个作用域有同名的key,EL怎么指定取哪个?
EL默认会按pageScope→requestScope→sessionScope→applicationScope的顺序找,但如果多个作用域有同名的key(比如request和session都有user),你可以用“作用域前缀”指定。想取request里的user,就写${requestScope.user.username};想取session里的,就写${sessionScope.user.username},这样就不会取错了。我之前做博客项目时,session里存了登录用户,request里存了临时用户,用这个方法就没混过。