0%

【Spring】Spring-MVC

Spring Mvc 的原理 理解 使用 笔记

Spring Mvc

  它是属于Spring基本架构里面的一个组成部分,属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面,所以我们在后期和 Spring 进行整合的时候,几乎不需要别的什么配置。

什么是Spring Mvc

  SpringMVC 是类似于 Struts2 的一个 MVC 框架,在实际开发中,接收浏览器的请求响应,对数据进行处理,然后返回页面进行显示,但是上手难度却比 Struts2 简单多了。而且由于 Struts2 所暴露出来的安全问题,SpringMVC 已经成为了大多数企业优先选择的框架。

为什么要学

  1. 轻量级
  2. 高效,基于请求相应的Mvc框架
  3. 与Spring无缝结合
  4. 约定优于配置
  5. 功能强大,restful、数据验证、格式化、本地化、主题…..
  6. 简洁灵活
  7. 用的人多
    …..

原理

中心控制器介绍

  SpringWebMVC框架与许多其他WebMVC框架一样,是请求驱动的,围绕一个中央Servlet设计,该Servlet将请求分派给控制器并提供其他功能以促进web应用程序的开发。
  然而,Spring 的 DispatcherServlet 作用不止于此。它与 Spring IoC 容器完全集成,因此允许您使用 Spring 具有的所有其他功能。(比如ioc、aop等等)
  SpringWebMVC的请求处理工作流程DispatcherServlet如下图所示。精通模式的读者会认识到这 DispatcherServlet是 “前端控制器” 设计模式的一种表达
  (这是 Spring Web MVC 与许多其他领先的 Web 框架共享的模式)。

流程

  1. 客户端(浏览器)发送请求,直接请求到 DispatcherServlet。
  2. DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。
  3. 解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。
  4. HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑。
  5. 处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。
  6. ViewResolver 会根据逻辑 View 查找实际的 View。
  7. DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  8. 把 View 返回给请求者(浏览器)

DispatcherServlet?

如图所示:
DispatcherServlet 是一个实际的 Servlet继承自 HttpServlet

因此在您需要在您的 Web 应用程序中声明,DispatcherServlet使用URL映射来映射要处理的请求。

示例

在下面的示例中,所有以/example开头的请求都将由名为DispatcherServlet的实例处理。


1
2
3
4
5
6
7
8
9
public class MyWebApplicationInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext container) {
ServletRegistration.Dynamic registration = container.addServlet("example", new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/example/*");
}
}

  WebApplicationInitializer 是 SpringMVC 提供的接口,通过简单地指定它的 servlet 映射和列出配置类,可确保检测到基于代码的配置并自动用于初始化任何 Servlet 3 容器。
  这个接口的抽象基类实现 AbstractAnnotationConfigDispatcherServletInitializer 使得DispatcherServlet注册变得更加容易 。
下面是web.xml上述基于代码的示例的等价物:

1
2
3
4
5
6
7
8
9
10
11
12
13
<web-app>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/example/*</url-pattern>
</servlet-mapping>

</web-app>

组件

WebApplicationContext 中的特殊 bean 类型

Bean type 名称 解释
DispatcherServlet 前端控制器 Spring MVC 的入口函数。
接收请求,响应结果,相当于转发器,中央处理器。
有了 DispatcherServlet 减少了其它组件之间的耦合度。
用户请求到达前端控制器,它就相当于mvc模式中的c,
DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,
DispatcherServlet的存在降低了组件之间的耦合性。
HandlerMapping 处理器映射器 根据请求的url查找Handler。
HandlerMapping负责根据用户请求找到Handler即处理器(Controller),
SpringMVC提供了不同的映射器实现不同的映射方式。
例如:配置文件方式,实现接口方式,注解方式等。
HandlerAdapter 处理器适配器 按照特定规则(HandlerAdapter要求的规则)去执行Handler
通过HandlerAdapter对处理器进行执行,
这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
Handler 处理器 写Handler时按照HandlerAdapter的要求去做
这样适配器才可以去正确执行Handler Handler 是继DispatcherServlet前端控制器的后端控制器,
在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler
HandlerExceptionResolver 处理异常解析器 将异常映射到视图还允许更复杂的异常处理代码。
ViewResolver 视图解析器 进行视图解析,根据逻辑视图名解析成真正的视图(view)
首先根据逻辑视图名解析成物理视图名(页面地址),
再生成View视图对象,
最后对View进行渲染将结果页面展示给用户。
springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,
需要由工程师根据业务需求开发具体的页面。
LocaleResolver
LocaleContextResolver
语言环境解析器 解析客户端正在使用的区域设置以及可能的时区,以便能够提供国际化的视图
ThemeResolver 主题解析器 解决您的 Web 应用程序可以使用的主题,例如,提供个性化布局
MultipartResolver 多部分分解器 例如,解析多部分请求以支持处理从HTML表单上载的文件。
FlashMapManager Flash地图管理器 存储并检索可用于将一个请求属性传递到另一个请求的input和output的FlashMap,<br/通常用于重定向

使用

写一个复杂的小demo来理解SpringMVC工作流

引入Maven依赖

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
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>

在web.xmi中配置 DispatcherServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

新建springmvc-servlet.xml并配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--处理器映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB_NIF/jsp"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>

写一个HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
String result = "hello Spring mvc";
modelAndView.addObject("msg",result);
modelAndView.setViewName("hello");
return modelAndView;
}
}

在 springmvc-servlet.xml 中注配置 HelloController

1
<bean id="/hello" class="com.wx.HelloController"/>

写一个View

1
2
3
4
5
6
7
8
<html>
<head>
<title>Hello</title>
</head>
<body>
${msg}
</body>
</html>

访问链接
http://localhost:8080/hello
结果

使用注解开发

引入依赖: 与上述示例一致
在web.xmi中配置DispatcherServlet: 与上述示例一致
新建springmvc-servlet.xml并配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.wx.controller"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>

写一个HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "hello,spring mvc annotation");
return "hello";
}
}

写一个View

1
2
3
4
5
6
7
8
<html>
<head>
<title>Hello</title>
</head>
<body>
${msg}
</body>
</html>

访问链接
http://localhost:8080/hello
结果

总结 Controller

控制器复杂提供访问应用程序的行为,通常通过接、定义或注解定义两种方法实现
控制器负责解析用户的请求并将其转换为一个模型
在Spring MVC中一个控制器类可以包含多个方法
在Spring MVC中,对于Controller的配置方式有很多种:

实现接口

实现Controller接口

1
2
3
4
5
6
7
public class HelloController1  implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "hello,spring mvc");
return mv;
}
}

在 springmvc-servlet.xml 中注配置 HelloController

1
<bean id="/hello" class="com.wx.HelloController"/>

注解实现

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!--配置ioc扫描注解-->
<context:component-scan base-package="com.wx.controller"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>

-

1
2
3
4
5
6
7
8
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "hello,spring mvc annotation");
return "hello";
}
}

重定向和转发

转发

1
2
3
4
5
6
7
8
9
10
11
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "/WEB-INF/jsp/test.jsp";
}
@RequestMapping("/hello2")
public String hello() {
return "forward:/WEB-INF/jsp/test.jsp";
}
}

重定向

1
2
3
4
5
6
7
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "redirect:/WEB-INF/jsp/test.jsp";
}
}

数据处理

接收数据

  1. 链接中名称与方法参数名称一致

    http://localhost:8080/hello?name=test

    1
    2
    3
    4
    5
    6
    7
    8
    @Controller
    public class HelloController {
    @RequestMapping("/hello")
    public String hello(String name) {
    System.out.println(name);
    return "hello";
    }
    }
  2. 链接中名称与方法参数名称不一致

    http://localhost:8080/hello?username=test

    1
    2
    3
    4
    5
    6
    7
    8
    @Controller
    public class HelloController {
    @RequestMapping("/hello")
    public String hello(@RequestParam("username") String name) {
    System.out.println(name);
    return "hello";
    }
    }
  3. 提交的是一个对象

    http://localhost:8080/hello?id=1&username=test

    1
    2
    3
    4
    5
    public class User{
    private int id;
    private String username;
    //省略 构造 / set/get
    }
    1
    2
    3
    4
    5
    6
    7
    8
    @Controller
    public class HelloController {
    @RequestMapping("/hello")
    public String hello(User user) {
    System.out.println(user);
    return "hello";
    }
    }

    对象会匹配对象中的字段名称,匹配到了就会自动装配对象

返回数据

  1. ModelAndView
    1
    2
    3
    4
    5
    6
    7
    public class HelloController1  implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ModelAndView mv = new ModelAndView();
    mv.addObject("msg", "hello,spring mvc");
    return mv;
    }
    }
  2. Model
    1
    2
    3
    4
    5
    6
    7
    8
    @Controller
    public class HelloController {
    @RequestMapping("/hello")
    public String hello(@RequestParam("username") String name,Model model) {
    model.addAttribute("msg",name);
    return "hello";
    }
    }
  3. ModelMap
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Controller
    public class HelloController {
    @RequestMapping("/hello")
    public String hello(ModelMap modelMap) {
    modelMap.
    mv.addAttribute("msg",name);
    return "hello";
    }
    }
    如图我们可以看出:
    ModelAndView 则是统合了 View 和 ModeMapl 个人认为是 ModelMap 扩展,基本不用。
    ModelMap 继承自 LinkedHashMap 所以 ModelMap 可以使用 LinkedHashMap的方法。

乱码过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置解决中文乱码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--初始化参数-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

拦截器

拦截器是Aop思想的具体应用
类似于Servlet开发中的过滤器filter,用于对处理器进行预处理和后处理,卡发着可以定义一些拦截器来实现特定功能。

过滤器

Servlet 规范中的一部分,任何javaweb工程都可以使用
在url-pattern中配置了/*之后可以对所有要访问的资源进行拦截

拦截器

拦截器是Spring《Mvc框架自己的,只有使用了SpringMVC框架的工程才能使用。
拦截器只访问的控制器方法,如果访问的是jsp/html/css/image/js是不会拦截的

自定义拦截器

继承接口 org.springframework.web.servlet.HandlerInterceptor;

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
package com.wx.interceotor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class DiyInterceptor implements HandlerInterceptor {

//true 放行
//false 不放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle======");
return false;
}

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle======");

}

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion======");
}
}

配置拦截器

1
2
3
4
5
6
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.wx.interceotor.DiyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

SSM

springmvc + mybatis + mysql 框架整合实战
请移步此文章《SSM框架整合实战》


感谢查阅