從原理層面掌握@RequestAttribute、@SessionAttribute的使用【一起學Spring MVC】

每篇一句

改我們就改得:取其精華,去其糟粕。否則木有意義

前言

如果說知道@SessionAttributes這個注解的人已經(jīng)很少了,那么不需要統(tǒng)計我就可以確定的說:知道@RequestAttribute注解的更是少之又少。我覺得主要有如下兩個原因:

  1. @RequestAttribute這個注解很新,Spring4.3后才有
  2. 我們可以使用API調(diào)用的方式(ServletRequest.getAttribute())來達到目的,而不用注解。且成本也不太高

既然Spring推出了這個注解,那必然有它的優(yōu)點。本文就帶大家領(lǐng)略一下它的風騷之處。

Spring提供的這些注解比如@ModelAttribute、@SessionAttributes、@RequestAttribute都是為了簡化開發(fā),提高復用性。同時另外一個目的是希望完全屏蔽掉源生Servlet API,增加它的擴展性。

本文我以@RequestAttribute為例進行講解,因為@SessionAttribute(也是Spring4.3后推出的注解)不管從使用和原理上都是一模一樣的。你可以理解成唯一區(qū)別是ServletRequest.getAttribute()HttpSession.getAttribute()的區(qū)別
此處再強調(diào)一次,這里指的是:org.springframework.web.bind.annotation.SessionAttribute,而非org.springframework.web.bind.annotation.SessionAttributes

@RequestAttribute

它比前面介紹的那些@ModelAttribute、@SessionAttributes等注解要簡單很多,它只能使用在方法入?yún)⑸?/strong>。作用:從request中取對應的屬性值。

很多小伙伴對getParameter()getAttribute()相關(guān)方法傻傻分不清楚。建議你可以先弄清楚paramattribute的區(qū)別~

// @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. 請求轉(zhuǎn)發(fā)帶過來

下面分別按照這三種使用場景,給出使用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,結(jié)果打印如下:

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請求轉(zhuǎn)發(fā)帶過來

形如這樣:

request.setAttribute("myApplicationName", "fsx-application");
request.getRequestDispatcher("/index").forward(request, response); 

其實往里放置屬性值只需要遵循一個原則:在調(diào)用處理器目標方法之前(參數(shù)封裝之前)任意地方放置即可,屬性值是都能被取到的。


原理剖析

按照我的習慣,即使它很簡單,我也會扒開來看看它的原理部分嘛。
根據(jù)經(jīng)驗很容易想到解析它的是一個HandlerMethodArgumentResolver,它就是RequestAttributeMethodArgumentResolver

RequestAttributeMethodArgumentResolver

很明顯,它也是@since 4.3才出現(xiàn)的,命名上也很配套嘛。

public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {

    // 只處理標注了@RequestAttribute注解的入?yún)?    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestAttribute.class);
    }

    // 封裝此注解的屬性到NamedValueInfo 這里關(guān)于參數(shù)名的處理有這么一個處理
    // 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());
    }

}

源碼短小精悍,非常簡單。
其實它解析入?yún)⒎矫娴暮诵慕馕隽鞒淘谄涓割?code>AbstractNamedValueMethodArgumentResolver身上,但并不是本文的重點,請詳見HandlerMethodArgumentResolver的章節(jié)~

@RequestAttribute屬性required默認為true, request.getAttribute獲取不到參數(shù)就會拋出異常ServletRequestBindingException;required設(shè)置為false,即使沒有從request中獲取到就忽略跳過,賦值為null;

總結(jié)

這篇文章介紹了@RequestAttribute的使用Demo,以及它很簡單的原理分析。相較于之前所有文章,這篇是非常輕松的,希望可以提供給大家一個思路,來使用@RequestAttribute提高你的逼格,哈哈(友情提示:裝逼需謹慎哦~)

說明:因為@SessionAttribute的使用甚至原理幾乎一毛一樣,所以不用再重復篇幅了

總結(jié)

這篇文章介紹了@SessionAttributes的核心處理原理,以及也給了一個Demo來介紹它的基本使用,不出意外閱讀下來你對它應該是有很好的收獲的,希望能幫助到你簡化開發(fā)~

相關(guān)閱讀

從原理層面掌握HandlerMethod、InvocableHandlerMethod、ServletInvocableHandlerMethod的使用【一起學Spring MVC】
從原理層面掌握@SessionAttributes的使用【一起學Spring MVC】
從原理層面掌握@ModelAttribute的使用(核心原理篇)【一起學Spring MVC】
從原理層面掌握@ModelAttribute的使用(使用篇)【一起學Spring MVC】

知識交流

==The last:如果覺得本文對你有幫助,不妨點個贊唄。當然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==

若對技術(shù)內(nèi)容感興趣可以加入wx群交流:Java高工、架構(gòu)師3群
若群二維碼失效,請加wx號:fsx641385712(或者掃描下方wx二維碼)。并且備注:"java入群" 字樣,會手動邀請入群

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容