1. Web 开发的发展
1.1 model1 的开发方式
统一把显示层、控制层、数据层的操作全部交给 JSP 或者 JavaBean 来进行处理
![image-20210308143854691](https://ysy-java.oss-cn-hangzhou.aliyuncs.com/image-20210308143854691.png)
出现的弊端:
JSP 和 Java Bean 之间严重耦合,Java 代码和 HTML 代码也耦合在了一起
要求开发者不仅要掌握 Java ,还要有高超的前端水平
前端和后端相互依赖,前端需要等待后端完成,后端也依赖前端完成,才能进行有效的测试
代码难以复用
1.2 model2 的开发方式
Servlet + JSP + Java Bean
![image-20210308144038275](https://ysy-java.oss-cn-hangzhou.aliyuncs.com/image-20210308144038275.png)
首先用户的请求会到达 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的执行流程
传统的 JavaWeb 开发
一个请求资源路径(url) 对应一个控制器(Controller),每次请求之后都会自动根据所请求的资源找到对应的 Servlet 类,执行相应的业务。
SpringMVC 开发
前后端不分离
SpringMVC 底层有一个核心对象:DispatcherServlet 前端控制器(分发器),使用了 SpringMVC 框架之后,所有的请求都会执行 DispatcherServlet 这个对象,不再去直接执行对应的 Controller,而是先通过 DispatcherServlet 前端控制器找到该请求路径(URL) 对应的控制器,前端控制器再去调用该控制器执行具体业务。
- 一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml 中指定),WEB 容器将该请求转交给 DispatcherServlet 处理
- DispatcherServlet 接收到请求后,解析URL,将根据请求信息交给处理器映射器 (HandlerMapping)
- HandlerMapping 根据用户的url请求 查找匹配该 url 的 Handler,并返回一个执行链
- DispatcherServlet 再请求处理器适配器(HandlerAdapter) 调用相应的 Handler 进行处理并返回 ModelAndView 给 DispatcherServlet
- DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View
- DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet 将页面响应给用户
注意:
- 在前端浏览器上第一次请求我们的 DispatchServlet 前端控制器(核心组件),会创建该 DispatchServlet 对象的实例,再执行 DispatchServlet 中的 init() 方法 , 从 spring 容器中按照类型注入来获取 DispatchServlet 中的属性对应的组件来进行依赖注入 !!!
- 如果不是第一次请求的话,各大组件依赖注入完毕,直接执行 doService() 方法来完成后续操作!!!
- SpringMVC 底层也有自己的一个容器:WebXmlApplicationContext ,和 spring 的 ApplicationContext 容器是父子关系,SpringMVC 的容器是继承了 Spring 容器的,spring 容器是父容器,springmvc 容器是子容器!!!
- springmvc 在需要使用到某个功能组件的时候,先去自己的 WebXmlApplicationContext 容器中去找,如果没有则去 spring 容器中去找
- springmvc 可以获取 spring 容器中的 bean,而 spring 则无法获取 springmvc 容器中的 bean !!!!
- 一般的话像 Controller 层对象一般都是存放在 springmvc 的容器中来共 springmvc 中的处理器适配器去调用!!!
- Service 层 和 Dao 层对象,一般则是放在 spring 容器中,因为像一些事务的处理和 mybatis 核心对象的生成不是加上注解之后就会立即生效,而是先生成代理对象,一般这些代理对象一般都在 spring 容器中注册!!!
前后端分离
使用 @ResponseBody 来把数据写入到响应体中。所以不需要进行页面的跳转。
用户发起请求被 DispatchServlet 所处理
DispatchServlet 通过 HandlerMapping 根据具体的请求查找能处理这个请求的 Handler。(HandlerMapping 主要是处理请求和Handler方法的映射关系的)
HandlerMapping 返回一个能够处理请求的执行链给 DispatchServlet,这个链中除了包含 Handler 方法也包含拦截器。
DispatchServlet 拿着执行链去找 HandlerAdapter 执行链中的方法。
HandlerAdater 会去执行对应的 Handler 方法,把数据处理转换成合适的类型,然后作为方法参数传入
Handler 方法执行完后的返回值会被 HandlerAdapter 转换成 ModelAndView 类型。由于使用了 @ResponseBody 注解,返回的ModelAndView 会为 null,并且 HandlerAdapter 会把方法返回值放到响应体中。(HandlerAdater 主要进行 Handler 方法参数和返回值的处理。)
返回 ModelAndView 给 DispatchServlet。
因为返回的 ModelAndView 为null,所以不用去解析视图解析和其后面的操作
4. SpringMvc 应用程序的编写
引入依赖
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
| <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
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
| <web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<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> <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
配置文件
1 2 3 4 5 6 7 8 9 10 11
| <?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
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
| <?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">
<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"/> <context:component-scan base-package="com.xzy.spring.controller"/> </beans>
|
编写处理器(Controller)
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
| 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; } }
|
编写视图
1 2 3 4 5 6 7 8 9 10 11
| <%@ 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>
|
5. 浏览器访问
http://localhost:8080/user/login.do
DispatcherServlet会接收到这个请求,并且去掉.do
去到相应的处理器
6. SpringMvc接收客户端的请求参数
http://localhost:8080/user/login.do?username=admin&password=admin123
6.1 通过servlet原始的api接收参数(了解)
1 2 3 4
| @RequestMapping("/login") public ModelAndView login(HttpServletRequest request, HttpServletResponse response) { String username = request.getParameter("username"); }
|
6.2 通过springmvc自带的功能完成参数的接收(重要)
1 2 3 4 5 6 7
| @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:
1 2 3 4 5 6 7
| @RequestMapping("/login") public ModelAndView login(String username,String password) { System.out.println(username); System.out.println(password); ModelAndView modelAndView = new ModelAndView(); return modelAndView; }
|
6.3 接收参数使用实体类来进行接收
注意: 实体类必须是一个标准的JavaBean(setter方法);
1 2 3 4 5
| @PostMapping(value = "/login.do") public ModelAndView login(User user) { System.out.println(user.getName()); System.out.println(user.getAge()); }
|
6.4 获取Rest请求风格的参数(路径参数) ==讲到这了==
什么是RestFul?
RESTFUL是一种网络应用程序的设计风格和开发方式;
Post
Put
Get
Delete
1 2 3 4 5 6 7 8 9 10 11 12 13
| 查询: 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@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
注解不能省略;
6.5 获取请求实体的内容
![image-20210311203740472](https://ysy-java.oss-cn-hangzhou.aliyuncs.com/image-20210311203740472.png)
1 2 3 4 5 6 7 8
| @PostMapping(value = "/login") public ModelAndView login(@RequestBody User user) { System.out.println(user); ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("index.jsp"); return modelAndView; }
|
注意
7. SpringMvc的响应处理
7.1 方法的返回值为void
1 2 3 4 5
| @PostMapping(value = "/login") public void login(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { request.setAttribute("username", "admin678"); request.getRequestDispatcher("/index01.jsp").forward(request, response); }
|
7.2 方法的返回值为String类型
1 2 3 4 5 6
| @PostMapping(value = "/login") public String login(HttpServletRequest request, HttpServletResponse response, Model model) throws IOException { model.addAttribute("username", "admin234"); return "/index01.jsp"; }
|
7.3 方法的返回值为ModelAndView
1 2 3 4 5 6 7
| @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; }
|
8. mvc:annotation-driven
我们如果使用springmvc的基础功能比如:@Controller
@RequestMapping
@RequestParam
,是不需要添加 mvc:annotation-driven的支持,但是我们如果要使用高级功能,例如: @RequestBody
@ResponseBody
这一些高级功能注解就需要添加mvc:annotation-driven的支持了;
我们要在 spring-mvc.xml
的配置文件中配置高级功能注解的支持
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
| <?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>
<context:component-scan base-package="com.xzy.goods.controller"></context:component-scan> </beans>
|
mvc:annotation-driven一定要放在其他注解之前,要不然不会生效;
9. @ReponseBody 注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <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 对象给客户端
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
| 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; } }
|
但是我们如果在每个方法上面都添加 ReponseBody 注解实在太繁琐了,所以 Spring 给我们提供了一种非常好的解决方案: @RestController
通过源码可知:
![image-20210311220719635](https://ysy-java.oss-cn-hangzhou.aliyuncs.com/image-20210311220719635.png)
@RestController
=@Controller
+ @ResponseBody
10. SpringMvc的请求转发与请求重定向
10.1 传统的Servlet的方式
请求转发:
1 2 3
| request.setAttribute("orderNum", "7823432"); request.getRequestDispatcher("/orderList.jsp").forward(request, response);
|
请求重定向:
1 2
| response.sendRedirect("/orderList.jsp");
|
10.2 SpringMvc提供的方式
请求转发:
1 2 3 4 5 6 7 8
| @GetMapping("/findById")
public String findById(Model model) throws ServletException, IOException { model.addAttribute("orderNum", 220); return "/orderList.jsp"; }
|
请求重定向:
1 2 3 4 5
| public String findById(Model model) throws ServletException, IOException { model.addAttribute("orderNum", 220); return "redirect:/orderList.jsp"; }
|
11. Springmvc中的拦截器
SpringMVC 中的Interceptor 拦截器是有相当重要的,它的主要作用是拦截用户的请求并进行相应的处理;
定义拦截器可以通过两种方式:
- 通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类来定义;
- 通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义。
编写拦截器
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.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 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
注册拦截器
1 2 3 4 5 6 7 8 9 10
| <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中是直接可以使用过滤器的
12. SpringMvc 请求参数限定
1 2 3 4 5
| @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
来进行结合使用;
13. SpringMvc接收数组参数
1 2 3 4 5 6 7 8 9 10
|
@GetMapping("/u3") public String u3(String[] loves) { System.out.println(Arrays.toString(loves)); return "/login.jsp"; }
|
14. 跨域问题
一个域的三要素:
默认浏览器中不支持后台的ajax跨域访问的;处于安全因素考虑的
浏览器对于 javascript 的同源策略的限制,例如 http://a.cn 下面的 js 不能调用 http://b.cn 中的 js 对象或数据(因为 http://a.cn 和 http://b.cn 是不同域)
同源策略会阻止一个域的 JavaScript 脚本和另一个域的内容进行交互
跨域问题的解决方案:
前端需要做什么?
无需做任何事情,正常发送 Ajax 请求即可。
后端需要做什么?
需要加响应头 。或者使用第三方模块 cors 。
WebConfig 配置类解决跨域问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT") .allowedHeaders("*") .maxAge(3600); } }
|