改咱们就改得:取其精华,去其糟粕。不然木有意义
若是说知道@SessionAttributes
这个注解的人已经不多了,那么不须要统计我就能够肯定的说:知道@RequestAttribute
注解的更是少之又少。我以为主要有以下两个缘由:java
@RequestAttribute
这个注解很新,Spring4.3
后才有ServletRequest.getAttribute()
)来达到目的,而不用注解。且成本也不过高既然Spring推出了这个注解,那必然有它的优势。本文就带你们领略一下它的风骚之处。web
Spring
提供的这些注解好比@ModelAttribute
、@SessionAttributes
、@RequestAttribute
都是为了简化开发,提升复用性。 同时另一个目的是但愿彻底屏蔽掉源生Servlet API,增长它的扩展性。本文我以
@RequestAttribute
为例进行讲解,由于@SessionAttribute
(也是Spring4.3后推出的注解)无论从使用和原理上都是如出一辙的。你能够理解成惟一区别是ServletRequest.getAttribute()
和HttpSession.getAttribute()
的区别
此处再强调一次,这里指的是:org.springframework.web.bind.annotation.SessionAttribute
,而非org.springframework.web.bind.annotation.SessionAttributes
spring
它比前面介绍的那些@ModelAttribute
、@SessionAttributes
等注解要简单不少,它只能使用在方法入参上。做用:从request中取对应的属性值。架构
不少小伙伴对getParameter()
和getAttribute()
相关方法傻傻分不清楚。建议你能够先弄清楚param
和attribute
的区别~
// @since 4.3 @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestAttribute { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; // 默认状况下 这个属性是必须的(没有就报错了) boolean required() default true; }
接下来这句话很重要:@RequestAttribute
只负责从request里面取属性值,至于你何时往里放值,是有多种方式的能够达到的:app
@ModelAttribute
注解预存HandlerInterceptor
拦截器中预存下面分别按照这三种使用场景,给出使用Demo
:ide
@ModelAttribute
注解预存比较简单,在@ModelAttribute
标注的方法上使用源生的HttpServletRequest
放值便可测试
@RestController @RequestMapping public class HelloController { // 放置attr属性值 @ModelAttribute public Person personModelAttr(HttpServletRequest request) { request.setAttribute("myApplicationName", "fsx-application"); return new Person("非功能方法", 50); } @GetMapping("/testRequestAttr") public void testRequestAttr(@RequestAttribute("myApplicationName") String myApplicationName, HttpServletRequest request, ModelMap modelMap) { System.out.println(myApplicationName); //fsx-application // 从request里获取 System.out.println(request.getAttribute("myApplicationName")); //fsx-application // 从model里获取 System.out.println(modelMap.get("myApplicationName")); // null 获取不到attr属性的 System.out.println(modelMap.get("person")); // Person(name=非功能方法, age=50) } }
请求/testRequestAttr
,结果打印以下:ui
fsx-application fsx-application null Person(name=非功能方法, age=50)
这里务必注意:@RequestAttribute("myApplicationName")
注解若是省略,是绑定不到attr属性的哦(必需要有注解)~spa
可是,这样是可行的:@RequestAttribute String myApplicationName
(若注解没有指定,Spring MVC
会再去看形参的名字来确认自动绑定)
但若你写成了这样@RequestAttribute String aaa
,那请求就直接400错误了抛出异常:org.springframework.web.bind.ServletRequestBindingException
HandlerInterceptor
拦截器中预存简单,直接上代码:.net
public class SimpleInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { request.setAttribute("myApplicationName", "fsx-application"); return true; } ... }
测试代码:略。
forward
请求转发带过来形如这样:
request.setAttribute("myApplicationName", "fsx-application"); request.getRequestDispatcher("/index").forward(request, response);
其实往里放置属性值只须要遵循一个原则:在调用处理器目标方法以前
(参数封装以前)任意地方放置便可,属性值是都能被取到的。
按照个人习惯,即便它很简单,我也会扒开来看看它的原理部分嘛。
根据经验很容易想到解析它的是一个HandlerMethodArgumentResolver
,它就是RequestAttributeMethodArgumentResolver
很明显,它也是@since 4.3
才出现的,命名上也很配套嘛。
public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { // 只处理标注了@RequestAttribute注解的入参 @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestAttribute.class); } // 封装此注解的属性到NamedValueInfo 这里关于参数名的处理有这么一个处理 // info.name.isEmpty()也就说若是本身没有指定,就用形参名parameter.getParameterName() @Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { RequestAttribute ann = parameter.getParameterAnnotation(RequestAttribute.class); Assert.state(ann != null, "No RequestAttribute annotation"); return new NamedValueInfo(ann.name(), ann.required(), ValueConstants.DEFAULT_NONE); } // 从request请求域去找属性值 @Override @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request){ return request.getAttribute(name, RequestAttributes.SCOPE_REQUEST); } // 若值不存在,抛出异常ServletRequestBindingException @Override protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException { throw new ServletRequestBindingException("Missing request attribute '" + name + "' of type " + parameter.getNestedParameterType().getSimpleName()); } }
源码短小精悍,很是简单。
其实它解析入参方面的核心解析流程在其父类AbstractNamedValueMethodArgumentResolver
身上,但并非本文的重点,请详见HandlerMethodArgumentResolver
的章节~
@RequestAttribute
属性required
默认为true,request.getAttribute
获取不到参数就会抛出异常ServletRequestBindingException
;required设置为false,即便没有从request中获取到就忽略跳过,赋值为null;
这篇文章介绍了@RequestAttribute
的使用Demo
,以及它很简单的原理分析。相较于以前全部文章,这篇是很是轻松的,但愿能够提供给你们一个思路,来使用@RequestAttribute
提升你的逼格,哈哈(友情提示:装逼需谨慎哦~)
说明:由于
@SessionAttribute
的使用甚至原理几乎一毛同样,因此不用再重复篇幅了
这篇文章介绍了@SessionAttributes
的核心处理原理,以及也给了一个Demo
来介绍它的基本使用,不出意外阅读下来你对它应该是有很好的收获的,但愿能帮助到你简化开发~
从原理层面掌握HandlerMethod、InvocableHandlerMethod、ServletInvocableHandlerMethod的使用【一块儿学Spring MVC】
从原理层面掌握@SessionAttributes的使用【一块儿学Spring MVC】
从原理层面掌握@ModelAttribute的使用(核心原理篇)【一块儿学Spring MVC】
从原理层面掌握@ModelAttribute的使用(使用篇)【一块儿学Spring MVC】
==The last:若是以为本文对你有帮助,不妨点个赞呗。固然分享到你的朋友圈让更多小伙伴看到也是被做者本人许可的~
==
**若对技术内容感兴趣能够加入wx群交流:Java高工、架构师3群
。
若群二维码失效,请加wx号:fsx641385712
(或者扫描下方wx二维码)。而且备注:"java入群"
字样,会手动邀请入群**
若文章格式混乱
或者图片裂开
,请点击`: 原文连接-原文连接-原文连接