从原理层面掌握@RequestAttribute、@SessionAttribute的使用【一起学Spring MVC】
每篇一句
改我们就改得:取其精华,去其糟粕。否则木有意义
前言
如果说知道@sessionattributes
这个注解的人已经很少了,那么不需要统计我就可以确定的说:知道@requestattribute
注解的更是少之又少。我觉得主要有如下两个原因:
-
@requestattribute
这个注解很新,spring4.3
后才有 - 我们可以使用api调用的方式(
servletrequest.getattribute()
)来达到目的,而不用注解。且成本也不太高
既然spring推出了这个注解,那必然有它的优点。本文就带大家领略一下它的风骚之处。
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
@requestattribute
它比前面介绍的那些@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里面取属性值,至于你什么时候往里放值,是有多种方式的可以达到的:
-
@modelattribute
注解预存 -
handlerinterceptor
拦截器中预存 - 请求转发带过来
下面分别按照这三种使用场景,给出使用demo
:
@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
,结果打印如下:
fsx-application fsx-application null person(name=非功能方法, age=50)
这里务必注意:@requestattribute("myapplicationname")
注解如果省略,是绑定不到attr属性的哦(必须要有注解)~
但是,这样是可行的:
@requestattribute string myapplicationname
(若注解没有指定,spring mvc
会再去看形参的名字来确认自动绑定)
但若你写成了这样@requestattribute string aaa
,那请求就直接400错误了抛出异常:org.springframework.web.bind.servletrequestbindingexception
handlerinterceptor
拦截器中预存
简单,直接上代码:
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
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入群"
字样,会手动邀请入群