文章目录
1. Web开发的发展
1.1 model1的开发方式
统一把显示层、控制层、数据层的操作全部交给 JSP 或者 JavaBean 来进行处理
出现的弊端:
- JSP 和 Java Bean 之间严重耦合,Java 代码和 HTML 代码也耦合在了一起
- 要求开发者不仅要掌握 Java ,还要有高超的前端水平
- 前端和后端相互依赖,前端需要等待后端完成,后端也依赖前端完成,才能进行有效的测试
- 代码难以复用
1.2 model2的开发方式
Servlet + JSP + Java Bean
首先用户的请求会到达 Servlet,然后根据请求调用相应的 Java Bean,并把所有的显示结果交给 JSP 去完成,这样的模式我们就称为 MVC 模式。
- M 代表 模型(Model)
模型是什么呢? 模型就是数据,就是 dao,bean - V 代表 视图(View)
视图是什么呢? 就是网页, JSP,用来展示模型中的数据 - C 代表 控制器(controller)
控制器是什么? 控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上,Servlet 扮演的就是这样的角色。
2. Spring MVC 框架介绍
SpringMVC 是类似于 Struts2 的一个 MVC 框架,在实际开发中,接收浏览器的请求响应,对数据进行处理,然后返回页面进行显示,但是上手难度却比 Struts2 简单多了。而且由于 Struts2 所暴露出来的安全问题,SpringMVC 已经成为了大多数企业优先选择的框架。
- 特点:
- 结构松散,几乎可以在 Spring MVC 中使用各类视图
- 松耦合,各个模块分离
- 与 Spring 无缝集成
3. SpringMvc的执行流程
- 一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理
- DispatcherServlet 接收到请求后, 将根据 请求信息 交给 处理器映射器 (HandlerMapping)
- HandlerMapping 根据用户的url请求 查找匹配该url的 Handler,并返回一个执行链
- DispatcherServlet 再 请求 处理器适配器(HandlerAdapter) 调用相应的 Handler 进行处理并返回 ModelAndView 给 DispatcherServlet
- DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View
- DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet 将页面响应给用户
3. SpringMvc应用程序的编写
引入依赖
<!--只需要引入这一个依赖即可,因为这个依赖包含 beans core context web ..-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
创建Spring与SpringMvc配置文件
applicationContext.xml
spring-mvc.xml
修改web.xml
<web-app>
<!--Spring的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 监听tomcat的启动,在启动时初始化容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置springmvc的前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--大于0:代表tomcat启动时加载这个Servlet,小于等于0:代表第一次访问时加载这个Servlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
编写spring的applicationContext.xml
配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置注解扫描器-->
<context:component-scan base-package="com.xzy.spring"/>
</beans>
编写springmvc配置文件 spring-mvc.xml
<?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:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置前端控制器(在web.xml中配置)-->
<!--配置处理器映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
<!--必须配置处理器的包扫描路径,如果想要去掉applicationContext中的这个注解,就要把这个扫描路径配置成父类的-->
<context:component-scan base-package="com.xzy.spring.controller"/>
</beans>
编写处理器(Controller)
package com.xzy.spring.controller;
import com.xzy.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@GetMapping("/login")
public ModelAndView login() {
userService.login();
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/login.jsp");
Map map = new HashMap();
map.put("name", "admin");
map.put("age", 20);
modelAndView.addObject(map);
return modelAndView;
}
}
编写视图
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Title</title>
</head>
<body>
登录视图${requestScope.user}
<%= request.getAttribute("user")%>
</body>
</html>
4. 浏览器访问
http://localhost:8080/user/login.do
DispatcherServlet会接收到这个请求,并且去掉.do
去到相应的处理器
5. SpringMvc接收客户端的请求参数
http://localhost:8080/user/login.do?username=admin&password=admin123
5.1 通过servlet原始的api接收参数(了解)
@RequestMapping("/login")
public ModelAndView login(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
}
5.2 通过springmvc自带的功能完成参数的接收(重要)
@RequestMapping("/login")
public ModelAndView login(@RequestParam("username") String uname, @RequestParam("password") String upassword,@RequestParam("loves") List<String> loves) {
System.out.println(uname);
System.out.println(upassword);
ModelAndView modelAndView = new ModelAndView();
return modelAndView;
}
但是我们如果接收很多的参数每个参数都需要添加@RequestParam
注解,这样就显得很繁琐,我们可以省略@RequestParam
注解,那么就需要让我们形参的名称和请求的参数的名称一致;
eg:
@RequestMapping("/login")
public ModelAndView login(String username,String password) {
System.out.println(username);
System.out.println(password);
ModelAndView modelAndView = new ModelAndView();
return modelAndView;
}
5.3 获取Rest请求风格的参数(路径参数)
什么是RestFul?
RESTFUL是一种网络应用程序的设计风格和开发方式;
Post
Put
Get
Delete
查询:
GET /user/selectUser?id=10 #传统方式
GET /user/10 #RestFul的方式
删除:
POST /user/deleteUser?id=10 #传统方式
DELETE /user/10 #RestFul的方式
修改:
POST /user/updateUser?id=10 #传统方式
xxxxxx
PUT /user/10 #RestFul的方式
xxxx
请求url:
POST: http://localhost:8080/user
DELET:http://localhost:8080/user/10
/**
* 添加用户
*/
@PostMapping()
public void add(String name) {
System.out.println("添加用户:name=" + name);
}
/**
* 添加用户
*/
@DeleteMapping("/{id}")
public void del(@PathVariable("id") String id) {
System.out.println("删除:id=" + id);
}
@PathVariable
注解不能省略;
5.4 获取请求实体的内容
//需要添加 <mvc:annotation-driven/> 高级功能支持
@PostMapping(value = "/login")
public ModelAndView login(@RequestBody User user) {
System.out.println(user);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("index.jsp");
return modelAndView;
}
注意
-
导入依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.0</version> </dependency>
-
在
spring-mvc.xml
配置高级注解功能的支持<!--@RequestBody注解必须得配置这一项--> <mvc:annotation-driven/>
此注解需要配置在之前
6. SpringMvc的响应处理
6.1 方法的返回值为void
@PostMapping(value = "/login")
public void login(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
request.setAttribute("username", "admin678");
request.getRequestDispatcher("/index01.jsp").forward(request, response);
}
6.2 方法的返回值为String类型
@PostMapping(value = "/login")
public String login(HttpServletRequest request, HttpServletResponse response, Model model) throws IOException {
//这个model里面的数据会被携带到视图中去
model.addAttribute("username", "admin234");
return "/index01.jsp";
}
6.3 方法的返回值为ModelAndView
@PostMapping(value = "/login")
public ModelAndView login(HttpServletRequest request, HttpServletResponse response) throws IOException {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index01.jsp");
modelAndView.addObject("username", "admin");
return modelAndView;
}
7. mvc:annotation-driven
我们如果使用springmvc的基础功能比如:@Controller
@RequestMapping
@RequestParam
,是不需要添加 mvc:annotation-driven的支持,但是我们如果要使用高级功能,例如: @RequestBody
@ResponseBody
这一些高级功能注解就需要添加mvc:annotation-driven的支持了;
我们要在 spring-mvc.xml
的配置文件中配置高级功能注解的支持
<?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:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--高级功能注解的扩展支持(一定要放在其他配置的最前面,要不然不生效)-->
<mvc:annotation-driven/>
<!--注册处理器映射器-->
<bean name="requestMappingHandlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--注册处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
<!--开启Springmvc的注解扫描,主要扫描处理器的注解 @Controller @RequestMapping @GetMapping @PostMapping-->
<context:component-scan base-package="com.xzy.goods.controller"></context:component-scan>
</beans>
mvc:annotation-driven一定要放在其他注解之前,要不然不会生效;
8. @ReponseBody注解
<!--返回的对象转换成json字符串必须导入jackson的依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
@ReponseBody
和@Controller
进行结合使用,可以直接返回json对象给客户端
package com.xzy.goods.controller;
import com.xzy.goods.entity.User;
import com.xzy.goods.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@PostMapping(value = "/login")
@ResponseBody
public User login(@RequestBody User user) {
System.out.println(user);
user.setId(1001);
return user;// 返回一个对象,在springmvc的底层会把对象自动转换为json字符串进行返回,还自动添加了 Content-Type: application/json
}
}
但是我们如果在每个方法上面都添加ReponseBody注解实在太繁琐了,所以Spring给我们提供了一种非常好的解决方案: @RestController
通过源码可知:
@RestController=
@Controller+
@ResponseBody`
9. SpringMvc的请求转发与请求重定向
9.1 传统的Servlet的方式
请求转发:
//请求转发
request.setAttribute("orderNum", "7823432");
request.getRequestDispatcher("/orderList.jsp").forward(request, response);
请求重定向:
//传统方式的请求重定向
response.sendRedirect("/orderList.jsp");
9.2 SpringMvc提供的方式
请求转发:
@GetMapping("/findById")
/**
* 方法的返回值为String类型,返回的是视图逻辑名称
*/
public String findById(Model model) throws ServletException, IOException {
model.addAttribute("orderNum", 220);
return "/orderList.jsp";
}
请求重定向:
public String findById(Model model) throws ServletException, IOException {
//重定向时会把orderNum以参数的形式携带在url中
model.addAttribute("orderNum", 220);
return "redirect:/orderList.jsp";
}
10. Springmvc中的拦截器
SpringMVC 中的Interceptor 拦截器是有相当重要的,它的主要作用是拦截用户的请求并进行相应的处理;
定义拦截器可以通过两种方式:
- 通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类来定义;
- 通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义。
编写拦截器
package com.xzy.goods.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyAllRequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyAllRequestInterceptor...请求处理之前....");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyAllRequestInterceptor...请求处理之后......");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyAllRequestInterceptor...视图渲染结束之后");
}
}
拦截器的定义中实现了 HandlerInterceptor 接口,并实现了接口中的 3 个方法。
- preHandle 方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
- postHandle 方法:该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
- afterCompletion 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
注册拦截器
<mvc:interceptors>
<!--配置全局拦截器,拦截所有请求-->
<bean class="com.xzy.goods.interceptor.MyAllRequestInterceptor"></bean>
<!--配置某个请求的拦截器,可以使用/**通配符-->
<mvc:interceptor>
<mvc:mapping path="/user/findById.do"/>
<bean class="com.xzy.goods.interceptor.Interceptor01"></bean>
</mvc:interceptor>
</mvc:interceptors>
拦截器与过滤器的区别
过滤器是原生的servlet中提供的过滤请求的一种机制
拦截器是SpringMvc中基于Aop的一种过滤handler的一种机制
在纯Servlet开发中不能直接使用拦截器,拦截器只能在SpringMvc中使用,
在SpringMvc中是直接可以使用过滤器的
11. SpringMvc请求参数限定
@RequestMapping("/findById")
public User findById(@RequestParam(name = "id",required = false,defaultValue = "110") Integer id) {
User user = new User(id, "admin", "admin123");
return user;
}
required: 默认为true,也就是说发送的请求必须要传递这个参数,不传递就会报错,我们想要不传递也不报错就要设置为false
defaultValue: 不传递时的默认值,一般与 required = false
来进行结合使用;
12. SpringMvc接收数组参数
/**
* 接收数组参数,前端访问的格式: /user/u3.do?loves=java&loves=python
* @param loves
* @return
*/
@GetMapping("/u3")
public String u3(String[] loves) {
System.out.println(Arrays.toString(loves));
return "/login.jsp";
}