成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從原理層面掌握@ModelAttribute的使用(使用篇)【一起學(xué)Spring MVC】

BenCHou / 1064人閱讀

摘要:和一起使用參照博文從原理層面掌握的使用一起學(xué)。至于具體原因,可以移步這里輔助理解從原理層面掌握的使用核心原理篇一起學(xué)再看下面的變種例子重要訪問。

每篇一句
每個(gè)人都應(yīng)該想清楚這個(gè)問題:你是祖師爺賞飯吃的,還是靠老天爺賞飯吃的
前言

上篇文章 描繪了@ModelAttribute的核心原理,這篇聚焦在場(chǎng)景使用上,演示@ModelAttribute在不同場(chǎng)景下的使用,以及注意事項(xiàng)(當(dāng)然有些關(guān)聯(lián)的原理也會(huì)涉及)。

為了進(jìn)行Demo演示,首先得再次明確一下@ModelAttribute的作用。

@ModelAttribute的作用

雖然說你可能已經(jīng)看過了核心原理篇,但還是可能會(huì)缺乏一些上層概念的總結(jié)。下面我以我的理解,總結(jié)一下 @ModelAttribute這個(gè)注解的作用,主要分為如下三個(gè)方面:

綁定請(qǐng)求參數(shù)到命令對(duì)象(入?yún)?duì)象):放在控制器方法的入?yún)⑸蠒r(shí),用于將多個(gè)請(qǐng)求參數(shù)綁定到一個(gè)命令對(duì)象,從而簡(jiǎn)化綁定流程,而且自動(dòng)暴露為模型數(shù)據(jù)用于視圖頁面展示時(shí)使用;

暴露表單引用對(duì)象為模型數(shù)據(jù):放在處理器的一般方法(非功能處理方法,也就是沒有@RequestMapping標(biāo)注的方法)上時(shí),是為表單準(zhǔn)備要展示的表單引用數(shù)據(jù)對(duì)象:如注冊(cè)時(shí)需要選擇的所在城市等靜態(tài)信息。它在執(zhí)行功能處理方法(@RequestMapping 注解的方法)之前,自動(dòng)添加到模型對(duì)象中,用于視圖頁面展示時(shí)使用;

暴露@RequestMapping方法返回值為模型數(shù)據(jù):放在功能處理方法的返回值上時(shí),是暴露功能處理方法的返回值為模型數(shù)據(jù),用于視圖頁面展示時(shí)使用。

下面針對(duì)這些使用場(chǎng)景,分別給出Demo用例,供以大家在實(shí)際使用中參考。

@ConstructorProperties講解

因?yàn)樵谠砥镏v過,自動(dòng)創(chuàng)建模型對(duì)象的時(shí)候不僅僅可以使用空的構(gòu)造函數(shù),還可以使用java.beans.ConstructorProperties這個(gè)注解,因此有必須先把它介紹一波:

官方解釋:構(gòu)造函數(shù)上的注釋,顯示該構(gòu)造函數(shù)的參數(shù)如何對(duì)應(yīng)于構(gòu)造對(duì)象的getter方法。

// @since 1.6
@Documented 
@Target(CONSTRUCTOR)  // 只能使用在構(gòu)造器上
@Retention(RUNTIME)
public @interface ConstructorProperties {
    String[] value();
}

如下例子:

@Getter
@Setter
public class Person {
    private String name;
    private Integer age;

    // 標(biāo)注注解
    @ConstructorProperties({"name", "age"})
    public Person(String myName, Integer myAge) {
        this.name = myName;
        this.age = myAge;
    }
}

這里注解上的nameage的意思是對(duì)應(yīng)著Person這個(gè)JavaBeangetName()getAge()方法。
它表示:構(gòu)造器的第一個(gè)參數(shù)可以用getName()檢索,第二個(gè)參數(shù)可以用getAge()檢索,由于方法/構(gòu)造器的形參名在運(yùn)行期就是不可見了,所以使用該注解可以達(dá)到這個(gè)效果。

此注解它的意義何在???
其實(shí)說實(shí)話,在現(xiàn)在去xml,完全注解驅(qū)動(dòng)的時(shí)代它的意義已經(jīng)不大了。它使用得比較多的場(chǎng)景是之前像使用xml配置Bean這樣:


    
    

這樣就不需要按照自然順序參數(shù)index(不靈活且容易出錯(cuò)有木有)來了,可以按照屬性名來對(duì)應(yīng),靈活了很多。本來xml配置基本不用了,但恰好在@ModelAttribute解析這塊讓它又換發(fā)的新生,具體例子下面會(huì)給出的~

java.beans中還提供了一個(gè)注解java.beans.Transient(1.7以后提供的):指定該屬性或字段不是永久的。 它用于注釋實(shí)體類,映射超類或可嵌入類的屬性或字段。(可以標(biāo)注在屬性上和get方法上)
Demo Show 標(biāo)注在非功能方法上
@Getter
@Setter
@ToString
public class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    public Person(String myName, int myAge) {
        this.name = myName;
        this.age = myAge;
    }
}

@RestController
@RequestMapping
public class HelloController {

    @ModelAttribute("myPersonAttr")
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }

    @GetMapping("/testModelAttr")
    public void testModelAttr(Person person, ModelMap modelMap) {
        //System.out.println(modelMap.get("person")); // 若上面注解沒有指定value值,就是類名首字母小寫
        System.out.println(modelMap.get("myPersonAttr"));
    }
}

訪問:/testModelAttr?name=wo&age=10。打印輸出:

Person(name=wo, age=10)
Person(name=非功能方法, age=50)

可以看到入?yún)⒌?b>Person對(duì)象即使沒有標(biāo)注@ModelAttribute也是能夠正常被封裝進(jìn)值的(并且還放進(jìn)了ModelMap里)。

因?yàn)闆]有注解也會(huì)使用空構(gòu)造創(chuàng)建一個(gè)Person對(duì)象,再使用ServletRequestDataBinder.bind(ServletRequest request)完成數(shù)據(jù)綁定(當(dāng)然還可以@Valid校驗(yàn))

有如下細(xì)節(jié)需要注意:
1、Person即使沒有空構(gòu)造,借助@ConstructorProperties也能完成自動(dòng)封裝

    // Person只有如下一個(gè)構(gòu)造函數(shù)
    @ConstructorProperties({"name", "age"})
    public Person(String myName, int myAge) {
        this.name = myName;
        this.age = myAge;
    }

打印的結(jié)果完全同上。

2、即使上面@ConstructorProperties的name寫成了myName,結(jié)果依舊正常封裝。因?yàn)橹灰獩]有校驗(yàn)bindingResult == null的時(shí)候,仍舊還會(huì)執(zhí)行ServletRequestDataBinder.bind(ServletRequest request)再封裝一次的。除非加了@Valid校驗(yàn),那就只會(huì)使用@ConstructorProperties封裝一次,不會(huì)二次bind了~(因?yàn)镾pring認(rèn)為你已經(jīng)@Valid過了,那就不要在湊進(jìn)去了

3、即使上面構(gòu)造器上沒有標(biāo)注@ConstructorProperties注解,也依舊是沒有問題的。原因:BeanUtils.instantiateClass(ctor, args)創(chuàng)建對(duì)象時(shí)最多args是[null,null]唄,也不會(huì)報(bào)錯(cuò)嘛(so需要注意:如果你是入?yún)⑹腔绢愋蚷nt那就報(bào)錯(cuò)啦~~)

4、雖然說@ModelAttribute寫不寫效果一樣。但是若寫成這樣@ModelAttribute("myPersonAttr") Person person,也就是指定為上面一樣的value值,那打印的就是下面:

Person(name=wo, age=10)
Person(name=wo, age=10)

至于原因,就不用再解釋了(參考原理篇)。

==另外還需要知道的是:@ModelAttribute標(biāo)注在本方法上只會(huì)對(duì)本控制器有效。但若你使用在@ControllerAdvice組件上,它將是全局的。(當(dāng)然可以指定basePackages來限制它的作用范圍~)==

標(biāo)注在功能方法(返回值)上

形如這樣:

    @GetMapping("/testModelAttr")
    public @ModelAttribute Person testModelAttr(Person person, ModelMap modelMap) {
        ...
    }

這塊不用給具體的示例,因?yàn)楸容^簡(jiǎn)單:把方法的返回值放入模型中。(注意void、null這些返回值是不會(huì)放進(jìn)去的~)

標(biāo)注在方法的入?yún)⑸?/b>

該使用方式應(yīng)該是我們使用得最多的方式了,雖然原理復(fù)雜,但對(duì)使用者來說還是很簡(jiǎn)單的,略。

@RequestAttribute/@SessionAttribute一起使用

參照博文:從原理層面掌握@RequestAttribute、@SessionAttribute的使用【一起學(xué)Spring MVC】。它倆合作使用是很順暢的,一般不會(huì)有什么問題,也沒有什么主意事項(xiàng)

@SessionAttributes一起使用

@ModelAttribute它本質(zhì)上來說:允許我們?cè)谡{(diào)用目標(biāo)方法前操縱模型數(shù)據(jù)。@SessionAttributes它允許把Model數(shù)據(jù)(符合條件的)同步一份到Session里,方便多個(gè)請(qǐng)求之間傳遞數(shù)值。
下面通過一個(gè)使用案例來感受一把:

@RestController
@RequestMapping
@SessionAttributes(names = {"name", "age"}, types = Person.class)
public class HelloController {

    @ModelAttribute
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }

    @GetMapping("/testModelAttr")
    public void testModelAttr(HttpSession httpSession, ModelMap modelMap) {
        System.out.println(modelMap.get("person"));
        System.out.println(httpSession.getAttribute("person"));
    }
}

為了看到@SessionAttributes的效果,我這里直接使用瀏覽器連續(xù)訪問兩次(同一個(gè)session)看效果:

第一次訪問打?。?/strong>

Person(name=非功能方法, age=50)
null

第二次訪問打?。?/strong>

Person(name=非功能方法, age=50)
Person(name=非功能方法, age=50)

可以看到@ModelAttribute結(jié)合@SessionAttributes就生效了。至于具體原因,可以移步這里輔助理解:從原理層面掌握@ModelAttribute的使用(核心原理篇)【一起學(xué)Spring MVC】

再看下面的變種例子(重要):

@RestController
@RequestMapping
@SessionAttributes(names = {"name", "age"}, types = Person.class)
public class HelloController {

    @GetMapping("/testModelAttr")
    public void testModelAttr(@ModelAttribute Person person, HttpSession httpSession, ModelMap modelMap) {
        System.out.println(modelMap.get("person"));
        System.out.println(httpSession.getAttribute("person"));
    }
}

訪問:/testModelAttr?name=wo&age=10。報(bào)錯(cuò)了:

 org.springframework.web.HttpSessionRequiredException: Expected session attribute "person"
    at org.springframework.web.method.annotation.ModelFactory.initModel(ModelFactory.java:117)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:869)

這個(gè)錯(cuò)誤請(qǐng)務(wù)必重視:這是前面我特別強(qiáng)調(diào)的一個(gè)使用誤區(qū),當(dāng)你在@SessionAttributes@ModelAttribute一起使用的時(shí)候,最容易犯的一個(gè)錯(cuò)誤。

錯(cuò)誤原因代碼如下:

    public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception {
        Map sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
        container.mergeAttributes(sessionAttributes);
        invokeModelAttributeMethods(request, container);

        // 合并完sesson的屬性,并且執(zhí)行完成@ModelAttribute的方法后,會(huì)繼續(xù)去檢測(cè)
        // findSessionAttributeArguments:標(biāo)注有@ModelAttribute的入?yún)? 并且isHandlerSessionAttribute()是SessionAttributts能夠處理的類型的話
        // 那就必須給與賦值~~~~  注意是必須
        for (String name : findSessionAttributeArguments(handlerMethod)) {
            // 如果model里不存在這個(gè)屬性(那就去sessionAttr里面找)
            // 這就是所謂的其實(shí)@ModelAttribute它是會(huì)深入到session里面去找的哦~~~不僅僅是request里
            if (!container.containsAttribute(name)) {
                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
                
                // 倘若session里都沒有找到,那就報(bào)錯(cuò)嘍
                // 注意:它并不會(huì)自己創(chuàng)建出一個(gè)新對(duì)象出來,然后自己填值,這就是區(qū)別。
                // 至于Spring為什么這么設(shè)計(jì) 我覺得是值得思考一下子的
                if (value == null) {
                    throw new HttpSessionRequiredException("Expected session attribute "" + name + """, name);
                }
                container.addAttribute(name, value);
            }
        }
    }

注意,這里是initModel()的時(shí)候就報(bào)錯(cuò)了喲,還沒到resolveArgument()呢。Spring這樣設(shè)計(jì)的意圖???我大膽猜測(cè)一下:控制器上標(biāo)注了@SessionAttributes注解,如果你入?yún)⑸线€使用了@ModelAttribute,那么你肯定是希望得到綁定的,若找不到肯定是你的程序失誤有問題,所以給你拋出異常,顯示的告訴你要去排錯(cuò)。

修改如下,本控制器上加上這個(gè)方法:

    @ModelAttribute
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }

(請(qǐng)注意觀察下面的幾次訪問以及對(duì)應(yīng)的打印結(jié)果)
訪問:/testModelAttr

Person(name=非功能方法, age=50)
null

再訪問:/testModelAttr

Person(name=非功能方法, age=50)
Person(name=非功能方法, age=50)

訪問:/testModelAttr?name=wo&age=10

Person(name=wo, age=10)
Person(name=wo, age=10)

注意:此時(shí)modelsession里面的值都變了哦,變成了最新的的請(qǐng)求鏈接上的參數(shù)值(并且每次都會(huì)使用請(qǐng)求參數(shù)的值)。

訪問:/testModelAttr?age=11111

Person(name=wo, age=11111)
Person(name=wo, age=11111)

可以看到是可以完成局部屬性修改的

再次訪問:/testModelAttr(無請(qǐng)求參數(shù),相當(dāng)于只執(zhí)行非功能方法)

Person(name=fsx, age=18)
Person(name=fsx, age=18)

可以看到這個(gè)時(shí)候modelsession里的值已經(jīng)不能再被非功能方法上的@ModelAttribute所改變了,這是一個(gè)重要的結(jié)論。
它的根本原理在這里:

    public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception {
        ...
        invokeModelAttributeMethods(request, container);
        ...
    }

    private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
        while (!this.modelMethods.isEmpty()) {
            ...
            // 若model里已經(jīng)存在此key 直接continue了
            if (container.containsAttribute(ann.name())) {
                ...
                continue;
            }
            // 執(zhí)行方法
            Object returnValue = modelMethod.invokeForRequest(request, container);
            // 注意:這里只判斷了不為void,因此即使你的returnValue=null也是會(huì)進(jìn)來的
            if (!modelMethod.isVoid()){
                ...
                // 也是只有屬性不存在 才會(huì)生效哦~~~~
                if (!container.containsAttribute(returnValueName)) {
                    container.addAttribute(returnValueName, returnValue);
                }
            }
        }
    }

因此最終對(duì)于@ModelAttribute@SessionAttributes共同的使用的時(shí)候務(wù)必要注意的結(jié)論:已經(jīng)添加進(jìn)session的數(shù)據(jù),在沒用使用SessionStatus清除過之前,@ModelAttribute標(biāo)注的非功能方法的返回值并不會(huì)被再次更新進(jìn)session內(nèi)

所以@ModelAttribute標(biāo)注的非功能方法有點(diǎn)初始值的意思哈~,當(dāng)然你可以手動(dòng)SessionStatus清楚后它又會(huì)生效了
總結(jié)

任何技術(shù)最終都會(huì)落到使用上,本文主要是介紹了@ModelAttribute各種使用case的示例,同時(shí)也指出了它和@SessionAttributes一起使用的坑。
@ModelAttribute這個(gè)注解相對(duì)來說還是使用較為頻繁,并且功能強(qiáng)大,也是最近講的最為重要的一個(gè)注解,因此花的篇幅較多,希望對(duì)小伙伴們的實(shí)際工作中帶來幫助,帶來代碼之美~

相關(guān)閱讀

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

知識(shí)交流

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

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

若文章格式混亂或者圖片裂開,請(qǐng)點(diǎn)擊`:原文鏈接-原文鏈接-原文鏈接

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76200.html

相關(guān)文章

  • 原理層面掌握@ModelAttribute使用(核心原理)【一起學(xué)Spring MVC

    摘要:雖然它不是必須,但是它是個(gè)很好的輔助官方解釋首先看看官方的對(duì)它怎么說它將方法參數(shù)方法返回值綁定到的里面。解析注解標(biāo)注的方法參數(shù),并處理標(biāo)注的方法返回值。 每篇一句 我們應(yīng)該做一個(gè):胸中有藍(lán)圖,腳底有計(jì)劃的人 前言 Spring MVC提供的基于注釋的編程模型,極大的簡(jiǎn)化了web應(yīng)用的開發(fā),我們都是受益者。比如我們?cè)贎RestController標(biāo)注的Controller控制器組件上用@...

    wdzgege 評(píng)論0 收藏0
  • 原理層面掌握@RequestAttribute、@SessionAttribute使用一起學(xué)S

    摘要:同時(shí)另外一個(gè)目的是希望完全屏蔽掉源生,增加它的擴(kuò)展性。本文我以為例進(jìn)行講解,因?yàn)橐彩呛笸瞥龅淖⒔獠还軓氖褂煤驮砩隙际且荒R粯拥?。作用從中取?duì)應(yīng)的屬性值。 每篇一句 改我們就改得:取其精華,去其糟粕。否則木有意義 前言 如果說知道@SessionAttributes這個(gè)注解的人已經(jīng)很少了,那么不需要統(tǒng)計(jì)我就可以確定的說:知道@RequestAttribute注解的更是少之又少。我覺得主...

    why_rookie 評(píng)論0 收藏0
  • 原理層面掌握@SessionAttribute使用一起學(xué)Spring MVC

    摘要:見名之意,它是處理器,也就是解析這個(gè)注解的核心。管理通過標(biāo)注了的特定會(huì)話屬性,存儲(chǔ)最終是委托了來實(shí)現(xiàn)。只會(huì)清楚注解放進(jìn)去的,并不清除放進(jìn)去的它的唯一實(shí)現(xiàn)類實(shí)現(xiàn)也簡(jiǎn)單。在更新時(shí),模型屬性與會(huì)話同步,如果缺少,還將添加屬性。 每篇一句 不是你當(dāng)上了火影大家就認(rèn)可你,而是大家都認(rèn)可你才能當(dāng)上火影 前言 該注解顧名思義,作用是將Model中的屬性同步到session會(huì)話當(dāng)中,方便在下一次請(qǐng)求中...

    ARGUS 評(píng)論0 收藏0
  • 原理層面掌握HandlerMethod、InvocableHandlerMethod使用一起學(xué)

    摘要:并且,并且如果或者不為空不為且不為,將中斷處理直接返回不再渲染頁面對(duì)返回值的處理對(duì)返回值的處理是使用完成的對(duì)異步處理結(jié)果的處理使用示例文首說了,作為一個(gè)非公開,如果你要直接使用起來,還是稍微要費(fèi)點(diǎn)勁的。 每篇一句 想當(dāng)火影的人沒有近道可尋,當(dāng)上火影的人同樣無路可退 前言 HandlerMethod它作為Spring MVC的非公開API,可能絕大多數(shù)小伙伴都對(duì)它比較陌生,但我相信你對(duì)它...

    wawor4827 評(píng)論0 收藏0
  • 原理層面掌握HandlerMethod、InvocableHandlerMethod使用一起學(xué)

    摘要:并且,并且如果或者不為空不為且不為,將中斷處理直接返回不再渲染頁面對(duì)返回值的處理對(duì)返回值的處理是使用完成的對(duì)異步處理結(jié)果的處理使用示例文首說了,作為一個(gè)非公開,如果你要直接使用起來,還是稍微要費(fèi)點(diǎn)勁的。 每篇一句 想當(dāng)火影的人沒有近道可尋,當(dāng)上火影的人同樣無路可退 前言 HandlerMethod它作為Spring MVC的非公開API,可能絕大多數(shù)小伙伴都對(duì)它比較陌生,但我相信你對(duì)它...

    miya 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

BenCHou

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<