SpringMVC--@InitBinder/@ModelAttribute/@SessionAttributes 港控/mmm° 2022-08-28 14:48 226阅读 0赞 原文网址:[SpringMVC--@InitBinder/@ModelAttribute/@SessionAttributes\_IT利刃出鞘的博客-CSDN博客][SpringMVC--_InitBinder_ModelAttribute_SessionAttributes_IT_-CSDN] ## @InitBinder ## ## 使用 ## 见:[SpringMVC/SpringBoot--全局异常处理/全局响应/全局请求--使用/用法/实例/示例\_IT利刃出鞘的博客-CSDN博客][SpringMVC_SpringBoot--_--_IT_-CSDN] ## 原理/总结 ## **其他网址** > [从原理层面掌握@InitBinder的使用【享学Spring MVC】 - 掘金][InitBinder_Spring MVC_ -] **加了前缀为何能绑定上** > 1、ModelAttributeMethodProcessor\#resolveArgument里依赖attribute = createAttribute(name, parameter, binderFactory, webRequest)方法完成数据的封装、转换 > > 2、createAttribute先request.getParameter(attributeName)看请求域里是否有值(此处为null),若木有就反射创建一个空实例,回到resolveArgument方法。 > > 3、继续利用WebDataBinder来完成对这个空对象的数据值绑定,这个时候这些FieldDefaultPrefix就起作用了。执行方法是:bindRequestParameters(binder, webRequest),实际上是((WebRequestDataBinder) binder).bind(request);。对于bind方法的原理,就不陌生了~ > > 4、完成Model数据的封装后,再进行@Valid校验... **@InitBinder总结** > @InitBinder标注的方法执行是多次的,一次请求来就执行一次 > > Controller实例中的所有@InitBinder只对当前所在的Controller有效 > > @InitBinder的value属性控制的是模型Model里的key,而不是方法名(不写代表对所有的生效) > > @InitBinder标注的方法不能有返回值(只能是void或者returnValue=null) > > @InitBinder对@RequestBody这种基于消息转换器的请求参数无效 > 因为@InitBinder它用于初始化DataBinder数据绑定、类型转换等功能,而@RequestBody它的数据解析、转换时消息转换器来完成的,所以即使你自定义了属性编辑器,对它是不生效的(它的WebDataBinder只用于数据校验,不用于数据绑定和数据转换。它的数据绑定转换若是json,一般都是交给了jackson来完成的)。 > 只有AbstractNamedValueMethodArgumentResolver才会调用binder.convertIfNecessary进行数据转换,从而属性编辑器才会生效 # @ModelAttribute # **作用** > ModelAttribute可以应用在方法参数上或方法上,它的作用主要是当注解在方法参数上时会将注解的参数对象添加到Model中。若被@ModelAttribute注释的方法不是请求方法,则此方法会在此controller每个方法执行前被执行。 **使用场景** > 当@ModelAttribute注解用于方法时,与其处于同一个处理类的所有请求方法执行前都会执行一次此方法,这可能并不是我们想要的,我们使用更多的是将其应用在请求方法的参数上。 > > @ModelAttribute的一部分功能与@RequestParam注解是一致的,只不过@RequestParam用于绑定单个参数值,而@ModelAttribute注解可以绑定所有名称匹配的,此外它自动将绑定后的数据添加到模型中。 ## 实例:用在方法上获得任意实体 ## **实体类** > package com.example.entity; > > import lombok.Data; > > @Data > public class User { > private Integer id; > private String name; > private Integer age; > } **控制器** > package com.example.controller; > > import com.example.entity.User; > import org.springframework.web.bind.annotation.RequestMapping; > import org.springframework.web.bind.annotation.RestController; > > @RestController > @RequestMapping("/hello") > public class HelloController { > > @RequestMapping("/test1") > public User test1(User user) { > System.out.println("HelloController.test1"); > return user; > } > } **@ModelAttribute类** > package com.example.advice; > > import com.example.entity.User; > import org.springframework.web.bind.annotation.ControllerAdvice; > import org.springframework.web.bind.annotation.ModelAttribute; > > import javax.servlet.http.HttpServletRequest; > > @ControllerAdvice > public class GlobalControllerAdvice { > @ModelAttribute > public User authenticationUser(HttpServletRequest httpServletRequest, User user) { > System.out.println("GlobalControllerAdvice.authenticationUser"); > System.out.println("URL:" + httpServletRequest.getRequestURL()); > if (user.getName() != null && "Tony".equals(user.getName())) { > user.setAge(20); > return user; > } > throw new RuntimeException("用户名错误"); > } > } **测试结果** > postman访问:[http://localhost:8080/hello/test1?name=Tony][http_localhost_8080_hello_test1_name_Tony] > > 后端结果 > > GlobalControllerAdvice.authenticationUser > URL:http://localhost:8080/hello/test1 > HelloController.test1 > > postman结果 > > { > "id": null, > "name": "Tony", > "age": 20 > } > postman访问:[http://localhost:8080/hello/test1?name=Stark][http_localhost_8080_hello_test1_name_Stark] > > 后端结果 > > GlobalControllerAdvice.authenticationUser > URL:http://localhost:8080/hello/test1 > 2020-09-05 18:17:41.669 ERROR 4240 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 用户名错误] with root cause > > java.lang.RuntimeException: 用户名错误 > at com.example.advice.GlobalControllerAdvice.authenticationUser(GlobalControllerAdvice.java:19) ~[classes/:na] > > postman结果 > > HTTP Status 500 – Internal Server Error ## **实例:用在参数上获得实体** ## > @ModelAttribute注释方法的一个参数表示应从模型model中取得。若在model中未找到,那么这个参数将先被实例化后加入到model中。若在model中找到,则请求参数名称和model属性字段若相匹配就会自动填充。这个机制对于表单提交数据绑定到对象属性上很有效。 > 后端 > > package com.example.controller; > > import com.example.entity.User; > import org.springframework.ui.Model; > import org.springframework.web.bind.annotation.ModelAttribute; > import org.springframework.web.bind.annotation.RequestMapping; > import org.springframework.web.bind.annotation.RestController; > > @RestController > public class HelloController { > // 方法1:通过返回值的方式默认地将添加一个属性 > // 属性名没有被显式指定的时:框架将根据属性的类型给予一个默认名称 > // 例如:本例返回一个 User 类型的对象,则默认的属性名为"user" > // 你可以通过设置 @ModelAttribute 注解的值来改变默认值 @ModelAttribute("myUser") > @ModelAttribute("user") > public User addUser(String name) { > System.out.println("addUser without model"); > User user = new User(); > user.setName(name); > return user; > } > > @RequestMapping("/test") > public User test(@ModelAttribute("user")User user) { > user.setAge(22); > return user; > } > } > **前端** > > 访问:[http://localhost:8080/test?name=Tony][http_localhost_8080_test_name_Tony] > > 响应: > > { > "id": null, > "name": "Tony", > "age": 22 > } ## **实例:用在方法上设置属性** ## > 有两种类型的@ModelAttribute方法。一种是:只加入一个属性,用方法的返回类型隐含表示。另一种是:方法接受一个Model类型的参数,这个model可以加入任意多个model属性。 > **后端** > > package com.example.controller; > > import com.example.entity.User; > import org.springframework.stereotype.Controller; > import org.springframework.ui.Model; > import org.springframework.web.bind.annotation.ModelAttribute; > import org.springframework.web.bind.annotation.RequestMapping; > import org.springframework.web.servlet.ModelAndView; > > @Controller > public class HelloController { > // 方法1:通过返回值的方式默认地将添加一个属性 > // 属性名没有被显式指定的时:框架将根据属性的类型给予一个默认名称 > // 例如:本例返回一个 User 类型的对象,则默认的属性名为"user" > // 你可以通过设置 @ModelAttribute 注解的值来改变默认值 @ModelAttribute("myUser") > @ModelAttribute > public User addUser(String name) { > System.out.println("addUser without model"); > User user = new User(); > user.setName(name); > return user; > } > > // 方法2:方法接收一个 Model 对象,然后可以向其中添加任意数量的属性 > @ModelAttribute > public void addUser(String name, Model model) { > System.out.println("addUser with model"); > model.addAttribute("name", "Tony"); > model.addAttribute("age", 20); > } > > @RequestMapping("/test") > public ModelAndView test() { > return new ModelAndView("hello"); > } > } > **前端** > > <%@ page contentType="text/html;charset=UTF-8" language="java" %> > <html> > <head> > <title>Title</title> > </head> > <body> > 账户名称:${account.name}<br/> > 年龄:${account.age}<br/> > number:${number}<br/> > other:${other}<br/> > </body> > </html> **@ModelAttribute和@RequestMapping同时注释一个方法** > **后端** > > @Controller > @RequestMapping(value="/test") > public class TestController { > > @RequestMapping(value = "/helloWorld") > @ModelAttribute("attributeName") > public String helloWorld() { > return "hi"; > } > } > > 这时这个方法的返回值并不是表示一个视图名称,而是model属性的值,视图名称由RequestToViewNameTranslator根据请求"/helloWorld"转换为helloWorld。Model属性名称由@ModelAttribute(value="")指定,相当于在request中封装了key=attributeName,value=hi。 > > **前端Jsp页面**: > > <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> > <html> > <head> > <title>helloWorld</title> > </head> > > <body> > The attributeValue is: ${attributeName} > </body> > </html> # @SessionAttributes # **简介** > 在默认情况下,ModelMap中的属性作用域是request级别,也就是说,当本次请求结束后,ModelMap 中的属性将销毁。如果希望在多个请求中共享ModelMap中的属性,必须将其属性转存到session 中,这样 ModelMap 的属性才可以被跨请求访问。Spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 session 中,以便下一个请求属对应的 ModelMap 的属性列表中还能访问到这些属性。这一功能是通过类定义处标注 @SessionAttributes 注解来实现的。 **后端:Controller** > @Controller > @RequestMapping("/anno") > @SessionAttributes(value={"msg"}) // 把Mapmodel中名字为msg的属性存入到session属性列表中 > public class AnnoController { > @RequestMapping(value="/testSessionAttributes") > public String testSessionAttributes(Model model){ > System.out.println("testSessionAttributes..."); > // 底层会存储到request域对象中 > model.addAttribute("msg","testSessionAttributes"); > return "success"; > } > > @RequestMapping(value="/getSessionAttributes") > public String getSessionAttributes(ModelMap modelMap){ > System.out.println("getSessionAttributes..."); > String msg = (String) modelMap.get("msg"); > System.out.println(msg); > return "success"; > } > > @RequestMapping(value="/delSessionAttributes") > public String delSessionAttributes(SessionStatus status){ > status.setComplete();//删除session域中的存入的数据 > return "success"; > } > } **前端:success.html** > <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> > <html> > <head> > <title>Title</title> > </head> > <body> > <h3>入门成功</h3> > ${ msg } > ${sessionScope} > </body> > </html> **测试1** > 访问:[http://localhost:8080/anno/testSessionAttributes/][http_localhost_8080_anno_testSessionAttributes] > > 前端 > > ![20190623222841107.png][] **测试2** > 访问:[http://localhost:8080/anno/getSessionAttributes/][http_localhost_8080_anno_testSessionAttributes] > > 后端打印 > > getSessionAttributes... > testSessionAttributes **测试3** > 访问:[http://localhost:8080/anno/getSessionAttributes/][http_localhost_8080_anno_testSessionAttributes] > > ![20190623223137338.png][] **测试4** > 再次访问:[http://localhost:8080/anno/getSessionAttributes/][http_localhost_8080_anno_testSessionAttributes] > > 后端打印 > > getSessionAttributes... > null > > 前端 > > ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2EyMjMxNDc2MDIw_size_16_color_FFFFFF_t_70][] [SpringMVC--_InitBinder_ModelAttribute_SessionAttributes_IT_-CSDN]: https://knife.blog.csdn.net/article/details/120315082 [SpringMVC_SpringBoot--_--_IT_-CSDN]: https://knife.blog.csdn.net/article/details/120314868 [InitBinder_Spring MVC_ -]: https://juejin.im/post/6844903940484513806#heading-4 [http_localhost_8080_hello_test1_name_Tony]: http://localhost:8080/hello/test1?name=Tony [http_localhost_8080_hello_test1_name_Stark]: http://localhost:8080/hello/test1?name=Stark [http_localhost_8080_test_name_Tony]: http://localhost:8080/test?name=Tony [http_localhost_8080_anno_testSessionAttributes]: http://localhost:8080/anno/testSessionAttributes/ [20190623222841107.png]: /images/20220828/9619060f81fe421aad48c1e17facce4d.png [20190623223137338.png]: /images/20220828/6024902239b0459b975f7d2dd5e54ee8.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2EyMjMxNDc2MDIw_size_16_color_FFFFFF_t_70]: /images/20220828/009a6fd2037e4ce2b133c73798056706.png
还没有评论,来说两句吧...