欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

从原理层面掌握@RequestAttribute、@SessionAttribute的使用【一起学Spring MVC】

程序员文章站 2022-06-05 20:15:30
一个可以沉迷于技术的程序猿,wx加入加入技术群:fsx641385712 ......

每篇一句

改我们就改得:取其精华,去其糟粕。否则木有意义

前言

如果说知道@sessionattributes这个注解的人已经很少了,那么不需要统计我就可以确定的说:知道@requestattribute注解的更是少之又少。我觉得主要有如下两个原因:

  1. @requestattribute这个注解很新,spring4.3后才有
  2. 我们可以使用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()相关方法傻傻分不清楚。建议你可以先弄清楚paramattribute的区别~

// @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里面取属性值,至于你什么时候往里放值,是有多种方式的可以达到的:

  1. @modelattribute注解预存
  2. handlerinterceptor拦截器中预存
  3. 请求转发带过来

下面分别按照这三种使用场景,给出使用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入群" 字样,会手动邀请入群