学习SpringMVC——说说视图解析器

 

  各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~)

  相信大家在昨天那篇如何获取请求参数篇中都已经领略到了spring mvc注解的魅力和套路了。搭上@RequestMapping的便车,我们可以去到我们想去的地方(方法)去,借助@RequestParam、@PathVariable等我们可以得到请求中想要的参数值,最终还能够通过神奇的“return SUCCESS”到达我们的目的地。今天主要就来说说在达到目的地的路上,我们都经历了些什么!

在此之前

  我们顺便说说@RequestHeader、请求参数类型为POJO(也就是Java对象类型)的情况以及ModelAndView

  1. @RequestHeader

  这个无需多说,还是原来的配方,还是一样的套路,只要举个例子,你就都明白了。

  在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;
}

  我们知道一个请求如get请求或post都有请求头和响应头,这里我们想获取的是请求头中“Accept-Language”的具体信息,所以就用上了@RequestHeader注解来获取。

  index.jsp中

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

  启动服务器,点击超链接,我们得到了

1
testRequestHeader Accept-Languge:zh-CN

 

  2. 请求参数为POJO

  前面两篇,我们看到的请求类型都是一些字符串也就是某一个字段。那么如果现在有一个form表单,说夸张点,表单中有10个字段需要提交,行吧,还用原来的匹配的方式,你要用10个参数来接收,累不累?累!有没有办法?有!我们可以把这些要提交的字段封装在一个对象中,从而请求类型就是一个POJO。

  这里我们新建一个类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() {
 
    }
}

  

  还有一个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 + "]";
    }
}

 

  同时我们还需要在SpringMVCTest中写一个testPojo的测试方法

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

  

  好了,这样,我们就可以在前台jsp页面上构造这样的表单数据了

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服务器,就可以发送一个POJO类型的参数了,并且我们成功了读取了这个请求参数

  3. ModelAndView

  ModelAndView是什么鬼?其实它是我们经常写在SpringMVCTest里测试方法的返回值类型,在方法体内我们可以通过ModelAndView对象来是像请求域中添加模型数据的,抽象?那就看例子吧~~~

  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中还是添加一个超链接

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

  注意我们需要在结果页面中拿到这个放入请求域中的键值对,所以在success.jsp页面中添加

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

  

  最终的效果图是这样的

  没错,我们将当前时间信息写进了请求域,并通过视图展示出来。

  有了前面的小铺垫,现在我们来唠唠这视图解析器的事儿

  视图解析器

  这里主要通过调试源代码看看spring mvc的handler是如何利用视图解析器找到并返回实际的物理视图的,别眨眼

  1. 如何看源码

  说到调试源码,我们就要有源码才行,那么如何看源码,相信这个页面大家已经看腻了吧

  没错,这是因为你没有导入源码的jar包,程序没办法给你呈现源代码,还好,这个问题难不倒我们,在第一篇中我们有关于springframework所需要的功能jar包,javadoc以及源码包,那么来导入一波

  选中前面提示的spring-context的source jar包,我们就可以一睹这个java文件的庐山真面目了

                                            484很开心~~~

  2. 代码调试

  为此我们写一个测试方法

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

  index.jsp加个链接

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

  给testViewAndView方法体一个断点,我们进入调试状态,

  程序停在断点处,在调试的上下文中,我们找到DispatcherServlet.doDispaatch方法,以此为入口,来看看视图解析器

  (1) 进入DispatcherServlet.doDispaatch

  定位到

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

  可以看到这里有个mv对象,实际上就是ModelAndView,通过调试我们发现这里的mv中包括了model和view,view的指向就是success,而model这里之所以有值是因为在SpringMVCTest中有一个getUser方法,且加上了@ModelAttribute注解,从而初始化了model。

  (2)执行processDispatchResult方法

  在doDispatch中继续执行,直到

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

  进入该方法进行视图渲染

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方法,然后得到视图的名字,即运行到view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);进入到该方法后,我们可以看到整个方法如下:

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);点击进入这里的render方法,我们选择AbstractView这个抽象类中的该方法

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);
    }

  该方法负责针对具体的Model呈现具体的view,这时候再进入到renderMergedOutputMode的具体实现类

  点击后,我们发现对此方法多个类都有实现,那么到底是哪个呢,实际上是InternalResourceView这个类,为什么定位到这个类,笔者是根据之前在springmvc.xml中配置的视图解析器的线索找到的,当时我们配的是InternalResourceViewResolver这个解析器,所以相应的,这里应该是InternalResourceView类,同时通过加断点,更加验证了这一想法~~~

  此外在调试DispatcherServlet的resolveViewName方法时,发现,这里的viewResolver正是我们配置的视图解析器InternalResourceViewResolver

  同时发现这里返回的view就是/WEB-INF/views/success.jsp

  至此,我们就完成了ModelAndView的逻辑路径向这里"/WEB-INF/views/success.jsp"的物理路径的转化,大致了了解了视图解析器的工作机制(感觉还是没有说清楚--!)。

  好了,本篇我们主要学习了

  1. @Request的用法
  2. 请求参数为POJO的用法
  3. ModelAndView的用法
  4. 如何看源代码
  5. spring mvc如何通过视图解析器得到真正的物理视图页面

SpringMVC——说说视图解析器的更多相关文章

  1. SpringMVC 多视图解析器配置以及问题

    在SpringMVC模式当中可以通过如下配置来支持多视图解析 <!-- jsp jstl --> <bean id="JSPViewResolver" class ...

  2. 学习SpringMVC——说说视图解析器

    各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~) 相信大家在昨天那篇如何获取请求参数篇中都已经领略到了spring mvc注 ...

  3. SpringMVC的视图解析器

    ViewResolver和View介绍 SpringMVC用于处理视图最重要的两个接口是ViewResolver和View.ViewResolver的主要作用是把一个逻辑上的视图名称解析为一个真正的视 ...

  4. springMVC初探视图解析器——InternalResourceViewResolver

    springmvc在处理器方法中通常返回的是逻辑视图,如何定位到真正的页面,就需要通过视图解析器. springmvc里提供了多个视图解析器,InternalResourceViewResolver就 ...

  5. SpringMVC中视图解析器

    视图解析器:固定写法直接coppy就行 1.dispatcherServlet-servlet.xml中添加 <!-- 视图解析器InternalResourceViewResolver --& ...

  6. springMVC初探视图解析器——ResourceBundleViewResolver

    视图解析器ResourceBundleViewResolver是根据proterties文件来找对应的视图来解析”逻辑视图“的, 该properties文件默认是放在classpath路径下的view ...

  7. springMVC初探视图解析器——XmlViewResolver

    XmlViewResolver解析器 XmlViewResolver基于XML文件中的视图bean来解析“逻辑视图”.XmlViewResolver默认会从/WEB-INF/views.xml中加载视 ...

  8. SpringMVC 多视图解析器 跳转问题

    在SpringMVC的配置文件中加入以下配置: <!--  下面红色的配置必须要在--> <mvc:default-servlet-handler /> <bean id ...

  9. SpringMvc:视图和视图解析器

    请求处理方法执行完成后,最终返回一个ModelAndView对象,对于返回String,View或ModelMap等类型的处理方法,SpringMvc也会在内部将它们装配成一个ModelAndView ...

随机推荐

  1. poj 2393 奶牛场生产成本问题 贪心算法

    题意:有一个奶牛场,第i周的生产成本为c,需要数量为 y,每周的存储成本为s.问怎么安排使得成本最低? 思路: 成本最低是吧?求出每周的最低成本*该周需要的数量就是成本最低 每周的成本有两个:自己本周 ...

  2. SELECT(データ取得)

    WHERE 句は.満たすべき条件を指定することにより選択される行数を制限します. WHERE 句は.SELECT 命令と同様に OPEN CURSOR.UPDATE.および DELETE 命令でも使用 ...

  3. C# 面试题 (一)

    一.C# 理论 1.1.简述 private. protected. public. internal.protected internal 访问修饰符和访问权限 private : 私有成员, 在类 ...

  4. 利尔达NB-IOT的PSM和eDRX低功耗模式笔记

    1. NB-IOT的技术优势,广覆盖,NB-IOT与GPRS和LTE相比较,最大链路预算提升了20dB,相当于提升了100倍,即使在地车车库.地下室.地下管道等普通无线网络信号难以到达的地方也容易覆盖 ...

  5. howto:在构建基于debian的docker基础镜像时,更换国内包源

    debian经常被用作构建应用镜像的基础镜像,如微软在构建linux下的dotnetcore基础镜像时,提供了基于debian 8(jessie)和debian 9(stretch)的镜像. 由于这些 ...

  6. What to do when Enterprise Manager is not able to connect to the database instance (ORA-28001)

    摘自:http://dbtricks.com/?p=34 If you are trying to connect to the Oracle enterprise Manger and you ge ...

  7. jmeter3.0生成html格式的dashboard性能测试结果

    jmeter3.0以上支持生成dashboard的html报告,官网介绍:https://jmeter.apache.org/usermanual/generating-dashboard.html ...

  8. 【转】ASP.NET Core 快速入门(环境篇)

    原文链接:http://www.cnblogs.com/zhaopei/p/netcore.html [申明]:本人.NET Core小白.Linux小白.MySql小白.nginx小白.而今天要说是 ...

  9. CF 55D

    Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer numb ...

  10. 软工实践 - 第二十七次作业 Beta 冲刺(5/7)

    队名:起床一起肝活队 组长博客:https://www.cnblogs.com/dawnduck/p/10134471.html 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过 ...