日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

學(xué)習(xí)SpringMVC

 昵稱27831725 2018-01-27

  各位前排的,后排的,都不要走,咱趁熱打鐵,就這一股勁我們今天來說說spring mvc的視圖解析器(不要搶,都有位子~~~)

  相信大家在昨天那篇如何獲取請求參數(shù)篇中都已經(jīng)領(lǐng)略到了spring mvc注解的魅力和套路了。搭上@RequestMapping的便車,我們可以去到我們想去的地方(方法)去,借助@RequestParam、@PathVariable等我們可以得到請求中想要的參數(shù)值,最終還能夠通過神奇的“return SUCCESS”到達(dá)我們的目的地。今天主要就來說說在達(dá)到目的地的路上,我們都經(jīng)歷了些什么!

 

在此之前

  我們順便說說@RequestHeader、請求參數(shù)類型為POJO(也就是Java對象類型)的情況以及ModelAndView

  1. @RequestHeader

  這個(gè)無需多說,還是原來的配方,還是一樣的套路,只要舉個(gè)例子,你就都明白了。

  在SpringMVCTest中添加測試方法

1
2
3
4
5
@RequestMapping(value="/testRequestHeader")
public String testRequestHeader(@RequestHeader(value="Accept-Language") String language){
    System.out.println("testRequestHeader Accept-Languge:" + language);
    return SUCCESS;
}

  我們知道一個(gè)請求如get請求或post都有請求頭和響應(yīng)頭,這里我們想獲取的是請求頭中“Accept-Language”的具體信息,所以就用上了@RequestHeader注解來獲取。

 

  index.jsp中

1
<a href="springmvc/testRequestHeader">testRequestHeader</a><br/><br/>

 

  啟動服務(wù)器,點(diǎn)擊超鏈接,我們得到了

1
testRequestHeader Accept-Languge:zh-CN

 

  2. 請求參數(shù)為POJO

  前面兩篇,我們看到的請求類型都是一些字符串也就是某一個(gè)字段。那么如果現(xiàn)在有一個(gè)form表單,說夸張點(diǎn),表單中有10個(gè)字段需要提交,行吧,還用原來的匹配的方式,你要用10個(gè)參數(shù)來接收,累不累?累!有沒有辦法?有!我們可以把這些要提交的字段封裝在一個(gè)對象中,從而請求類型就是一個(gè)POJO。

  這里我們新建一個(gè)類User

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.jackie.springmvc.entities;
public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private int age;
    private Address address;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    public User(String username, String password, String email, int age) {
        super();
        this.username = username;
        this.password = password;
        this.email = email;
        this.age = age;
    }
    public User(Integer id, String username, String password, String email, int age) {
        super();
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + ", age="
                + age + "]";
    }
    public User() {
    }
}

 

  

  還有一個(gè)Address類

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.jackie.springmvc.entities;
public class Address {
    private String province;
    private String city;
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    @Override
    public String toString() {
        return "Address [province=" + province + ", city=" + city + "]";
    }
}

 

  同時(shí)我們還需要在SpringMVCTest中寫一個(gè)testPojo的測試方法

1
2
3
4
5
@RequestMapping(value="/testPojo")
public String testPojo(User user){
    System.out.println("testPojo: " + user);
    return SUCCESS;
}

  

  好了,這樣,我們就可以在前臺jsp頁面上構(gòu)造這樣的表單數(shù)據(jù)了

1
2
3
4
5
6
7
8
9
<form action="springmvc/testPojo" method="post">
    username: <input type="text" name="username"><br>
    password: <input type="password" name="password"><br>
    email: <input type="text" name="email"><br>
    age: <input type="text" name="age"><br>
    city: <input type="text" name="address.city"><br>
    province: <input type="text" name="address.province"><br>
    <input type="submit" value="submit">
</form><br/><br/>

 

  至此,我們啟動tomcat服務(wù)器,就可以發(fā)送一個(gè)POJO類型的參數(shù)了,并且我們成功了讀取了這個(gè)請求參數(shù)

 

  3. ModelAndView

  ModelAndView是什么鬼?其實(shí)它是我們經(jīng)常寫在SpringMVCTest里測試方法的返回值類型,在方法體內(nèi)我們可以通過ModelAndView對象來是像請求域中添加模型數(shù)據(jù)的,抽象?那就看例子吧~~~

  SpringMVCTest中添加方法

1
2
3
4
5
6
7
@RequestMapping(value="/testModelAndView")
public ModelAndView testModelAndView(){
    String viewname = SUCCESS;
    ModelAndView modelAndView = new ModelAndView(viewname);
    modelAndView.addObject("time", new Date());
    return modelAndView;
}

 

  index.jsp中還是添加一個(gè)超鏈接

1
<a href="springmvc/testModelAndView">testModelAndView</a><br/><br/>

 

  注意我們需要在結(jié)果頁面中拿到這個(gè)放入請求域中的鍵值對,所以在success.jsp頁面中添加

1
time: ${requestScope.time}<br><br>

  

  最終的效果圖是這樣的

  沒錯(cuò),我們將當(dāng)前時(shí)間信息寫進(jìn)了請求域,并通過視圖展示出來。

 

  有了前面的小鋪墊,現(xiàn)在我們來嘮嘮這視圖解析器的事兒

  視圖解析器

  這里主要通過調(diào)試源代碼看看spring mvc的handler是如何利用視圖解析器找到并返回實(shí)際的物理視圖的,別眨眼

  1. 如何看源碼

  說到調(diào)試源碼,我們就要有源碼才行,那么如何看源碼,相信這個(gè)頁面大家已經(jīng)看膩了吧

 

  沒錯(cuò),這是因?yàn)槟銢]有導(dǎo)入源碼的jar包,程序沒辦法給你呈現(xiàn)源代碼,還好,這個(gè)問題難不倒我們,在第一篇中我們有關(guān)于springframework所需要的功能jar包,javadoc以及源碼包,那么來導(dǎo)入一波

 

  選中前面提示的spring-context的source jar包,我們就可以一睹這個(gè)java文件的廬山真面目了

                                            484很開心~~~

 

  2. 代碼調(diào)試

  為此我們寫一個(gè)測試方法

1
2
3
4
5
@RequestMapping("/testViewAndViewResolver")
public String testViewAndViewResolver(){
    System.out.println("testViewAndViewResolver");
    return SUCCESS;
}

 

  index.jsp加個(gè)鏈接

1
<a href="springmvc/testViewAndViewResolver">testViewAndViewResolver</a><br/><br/>

 

  給testViewAndView方法體一個(gè)斷點(diǎn),我們進(jìn)入調(diào)試狀態(tài),

 

  程序停在斷點(diǎn)處,在調(diào)試的上下文中,我們找到DispatcherServlet.doDispaatch方法,以此為入口,來看看視圖解析器

 ?。?) 進(jìn)入DispatcherServlet.doDispaatch

  定位到

1
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 

  可以看到這里有個(gè)mv對象,實(shí)際上就是ModelAndView,通過調(diào)試我們發(fā)現(xiàn)這里的mv中包括了model和view,view的指向就是success,而model這里之所以有值是因?yàn)樵赟pringMVCTest中有一個(gè)getUser方法,且加上了@ModelAttribute注解,從而初始化了model。

 

  (2)執(zhí)行processDispatchResult方法

  在doDispatch中繼續(xù)執(zhí)行,直到

1
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

 

  進(jìn)入該方法進(jìn)行視圖渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }
        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                        "': assuming HandlerAdapter completed request handling");
            }
        }
        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

 

  這里我們著重看下render方法,然后得到視圖的名字,即運(yùn)行到view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);進(jìn)入到該方法后,我們可以看到整個(gè)方法如下:

1
2
3
4
5
6
7
8
9
10
11
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
            HttpServletRequest request) throws Exception {
        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
        return null;
    }

  

  這里用到了視圖解析器即this.viewResolvers。而真正的渲染視圖在DispatcherServlet的view.render(mv.getModelInternal(), request, response);點(diǎn)擊進(jìn)入這里的render方法,我們選擇AbstractView這個(gè)抽象類中的該方法

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
     * Prepares the view given the specified model, merging it with static
     * attributes and a RequestContext attribute, if necessary.
     * Delegates to renderMergedOutputModel for the actual rendering.
     * @see #renderMergedOutputModel
     */
    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (logger.isTraceEnabled()) {
            logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
                " and static attributes " + this.staticAttributes);
        }
        Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
        prepareResponse(request, response);
        renderMergedOutputModel(mergedModel, request, response);
    }

 

 

  該方法負(fù)責(zé)針對具體的Model呈現(xiàn)具體的view,這時(shí)候再進(jìn)入到renderMergedOutputMode的具體實(shí)現(xiàn)類

 

  點(diǎn)擊后,我們發(fā)現(xiàn)對此方法多個(gè)類都有實(shí)現(xiàn),那么到底是哪個(gè)呢,實(shí)際上是InternalResourceView這個(gè)類,為什么定位到這個(gè)類,筆者是根據(jù)之前在springmvc.xml中配置的視圖解析器的線索找到的,當(dāng)時(shí)我們配的是InternalResourceViewResolver這個(gè)解析器,所以相應(yīng)的,這里應(yīng)該是InternalResourceView類,同時(shí)通過加斷點(diǎn),更加驗(yàn)證了這一想法~~~

  此外在調(diào)試DispatcherServlet的resolveViewName方法時(shí),發(fā)現(xiàn),這里的viewResolver正是我們配置的視圖解析器InternalResourceViewResolver

 

  同時(shí)發(fā)現(xiàn)這里返回的view就是/WEB-INF/views/success.jsp

 

 

  至此,我們就完成了ModelAndView的邏輯路徑向這里"/WEB-INF/views/success.jsp"的物理路徑的轉(zhuǎn)化,大致了了解了視圖解析器的工作機(jī)制(感覺還是沒有說清楚--?。?。

 

  好了,本篇我們主要學(xué)習(xí)了

  1. @Request的用法
  2. 請求參數(shù)為POJO的用法
  3. ModelAndView的用法
  4. 如何看源代碼
  5. spring mvc如何通過視圖解析器得到真正的物理視圖頁面

 

  如果您覺得閱讀本文對您有幫助,請點(diǎn)一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續(xù)關(guān)注我的文章,請掃描二維碼,關(guān)注JackieZheng的微信公眾號,我會將我的文章推送給您,并和您一起分享我日常閱讀過的優(yōu)質(zhì)文章。(點(diǎn)贊不迷路,博主帶你上高速~~~)

 

友情贊助

如果你覺得博主的文章對你那么一點(diǎn)小幫助,恰巧你又有想打賞博主的小沖動,那么事不宜遲,趕緊掃一掃,小額地贊助下,攢個(gè)奶粉錢,也是讓博主有動力繼續(xù)努力,寫出更好的文章^^。

    1. 支付寶                              2. 微信

                            

 

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多