文章目录
SpringMVC模型
一、概念
MVC模型:M(模型)、V(视图)、C(控制器),是一种架构模式
模型:领域对象 - 具体业务,区分:实体类不跟业务
模型包含:实体类、业务逻辑sevice
后台操作都是在模型中
视图:把信息显示给用户
控制器:是模型和视图的桥梁,从视图接收数据,封装成对象,传给后台(模型)
二、配置
第一:添加MVC模型需要的依赖
<dependencies>
<!-- 添加servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- 添加webmvc依赖,会将Spring的核心包一并依赖进来 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
第二:添加war插件
注意:在有web包的文件设置<packaging>war</packaging>
属性
<build>
<plugins>
<!-- war插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<warSourceDirectory>wab</warSourceDirectory>
<webXml>web/WEB-INF/web.xml</webXml>
</configuration>
</plugin>
</plugins>
</build>
第三:添加web文件
第四:配置web.xml文件
添加核心请求总控器
<?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">
<!-- 核心请求总控器,负责接收所有的请求,并根据映射的url地址将请求分发给具体控制器的方法来处理 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc默认会从WEB-INF目录下查找名为[servletName]-servlet.xml的配置文件,
这是spring官方默认的约定命名。如果想要自定义文件名并且想存放在其他目录下,
则需要通过contextConfigLocation初始化参数来配置,
例如:自定义一个springmvc.xml配置文件放在resources目录下
-->
<!-- <init-param>-->
<!-- <param-name>contextConfigLocation</param-name>-->
<!-- <param-value>classpath:springmvc.xml</param-value>-->
<!-- </init-param>-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
第五:配置dispatcher-servlet.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: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="edu.nf.ch01"/>
<!-- 启用mvc注解处理器
这个注解驱动注册了RequestMappingHandlerMapping(请求映射处理器)
和一个RequestMappingHandlerAdapter(请求处理适配器),同时
提供了@ReqeustBody、@ResponseBady注解支持、数据绑定等支持 -->
<mvc:annotation-driven/>
<!--配置视图解析器,springmvc支持多种视图,不同的视图由不同的视图解析器来解析
例如:想要使用JSP作为视图,那么就需要配置InternalResourceViewResolver这个视图解析器,用于解析内部的JSP资源 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 设置JSP资源的前缀,用于指定jsp存放的目录 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 设置jsp资源的后缀名,以'.jsp'结尾的文件-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
三、springmvc的请求注解及配置方法
1. @RequestMapping
- 声明在方法上,设置请求地址和请求类型
- 声明在类上,用作命名空间,可以区分多个相同子请求
package edu.nf.ch02.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
* @Date 2023-10-19
* @Author hy
*/
@Controller
/**
* @RequestMapping注解可以声明在类上,用作命名空间
* 可以区分多个相同子请求
*/
@RequestMapping("/admin")
public class RequestMapController {
// 设置请求地址和请求类型
@RequestMapping(value = "/getUser", method = RequestMethod.GET)
public ModelAndView getUser(){
// 响应视图,JSP视图解析器基于转发的机制
return new ModelAndView("index");
}
@RequestMapping(value = "/login", method = {RequestMethod.POST,RequestMethod.GET})
public ModelAndView login(){
return new ModelAndView("index");
}
}
从springmvc4.0开始,提供了新的子注解来替换@RequestMapping
2. @GetMapping
与@RequestMapping作用一样,只是@GetMapping只能用于get请求
package edu.nf.ch02.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @Date 2023-10-19
* @Author hy
*/
@Controller
public class RequestMapController {
/**
* @GetMapping只能用于get请求
* @return
*/
@GetMapping("/getUser2")
public ModelAndView getUser2(){
return new ModelAndView("index");
}
}
3. @PostMapping
与@RequestMapping作用一样,只是@PostMapping只能用于post请求
package edu.nf.ch02.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @Date 2023-10-19
* @Author hy
*/
@Controller
public class RequestMapController {
/**
* @PostMapping只能用于post请求
* @return
*/
@PostMapping("/login2")
public ModelAndView login2(){
return new ModelAndView("index");
}
}
四、SpringMVC原理解析
1. DispatcherServlet (核心请求总控器)
DispatcherServlet是SpringMVC的核心控制器,负责截获所有的请求,并根据映射的url地址将请求分发给具体控制器的方法来处理
springmvc执行流程:
浏览器发起请求,会被中央调度器(DispatcherServlet)所捕获,但中央调度器不会直接去处理请求,而是把请求交给 处理器映射器(HandlerMapping)处理,处理器映射器根据请求的路径(url地址)去Controller控制层中进行匹配[即对应的执行器:找到哪一个Controller的方法可以处理该请求],然后将匹配结果(整个Controller)返回给中央调度器;中央调度器再调用处理器适配器(HandlerAdaptor)来执行匹配结果(控制层执行器)的方法并返回执行结果(即ModelAndView对象)给中央调度器,
中央调度器会将返回的ModelAndView对象交给视图解析器(ViewResolver)解析成对应的视图(View)对象,渲染视图,最后将结果响应给浏览器。
2. HandlerMapping和HandlerAdapter
当xml配置了 注解处理器<mvc:annotation-driven/>
或者 配置类中配置了@EnableWebMvc
注解时,spring会自动装配 RequestMappingHandlerMapping(请求映射处理器)、RequestMappingHandlerAdapter(请求处理适配器)这两个类。
-
RequestMappingHandlerMapping: 负责解析带有 @ReqeustMapping 注解的方法以及类信息,并在请求到达时找到相应的 HandlerMethod(一个 JavaBean,封装了请求处理方法、参数信息、类信息以及 IOC 容器等重要的内容)。
当找到相应的 HandlerMethod 后,如果程序中有定义拦截器,那么就会将这个 HandlerMethod 封装到 HandlerExecutionChain 的类中,这个类包含了一个 拦截器的集合 和一个 HandlerMethod 的对象。最后将这个 chain 返回给 DispatcherServlet 。
DispatcherServlet 从这个 HandlerExecutionChain 中取出 HandlerMethod 来匹配相应的 HandlerAdapter,找到合适的可以调用 HandlerMathod 的请求处理适配器。
接着 DispatcherServlet 负责调用 HandlerExecutionChain 中的所有拦截器中的预处理方法,如果预处理方法没有任何问题,那么就将 HandlerMethod 交给 HandlerAdapter 去调用。 -
RequestMappingHandlerAdapter: DispatcherServlet 将 HandlerMethod 传递给 HandlerAdapter,由它负责调用 HandlerMethod(也就是目标控制器的方法)。
调用时还会使用具体的 MethodArgumentResolver (方法参数解析器, RequestMappingHandlerAdapter 内部会初始化一系列默认的 HandlerMethodArgumentResolver)将请求中的参数解析为请求处理方法所需要的具体类型参数。
最后将 Controller 方法返回的 ModelAndView 一并返回到 DispatcherServlet 中。
接着 DispatcherServlet 会继续执行所有拦截器中的后置处理方法。
3. ViewResolver
springmvc内部提供了许多视图解析器用于解析不同的视图对象,最长见的有InternalResourceViewResolver(内部资源视图解析器)、FreeMarkerViewResolver(模板引擎视图解析器)等。
InternalResourceViewResolver
在 DispatcherServlet 接收到 HandlerAdapter 返回的 ModelAndView 之后,DispatcherServlet 将这个 ModelAndView 交给指定InternalResourceViewResolver 来进行视图解析,
InternalResourceViewResolver 会根据ModelAndView 的视图名称来创建一个 InternalResourceView 的视图对象返回到 DispatcherServlet。由 DispatcherServlet 去调用视图对象的渲染方法来响应视图。
在渲染完视图之后,DispatcherServlet会执行所有拦截器中的after方法。
4. View
视图对象是由相应的视图解析器解析出来的,Spring也提供了不同的视图对象来完成不同的视图响应工作,常见的有的InternalResourceView(内部资源转发视图)等。
InternalResourceView: 这个视图对象会将 ModeAndView 中而外带的数据放入请求作用域,以及获取到拼接好的转发地址。并提供一个 renderMergedOutputModel 渲染方法由 DispatcherServlet 调用,这个方法就是负责具体的url转发工作。
五、springmvc处理静态资源的方式
1. 使用容器的默认Servlet来处理
<!-- 方式一 将静态资源交给容器的默认Servlet来处理,springmvc不参与解析。
常见的servlet容器如Tomcat、Jetty等都会有一个自带的DefaultServlet来处理这些静态资源 -->
<mvc:default-servlet-handler/>
2. 由springmvc自己来处理
<!-- 方式二 静态资源由springmvc自己来处理
mapping:用于映射静态资源的虚拟url;
location:用于指定静态资源的本地相对路径;
例如:下面的配置中,当以page为开头的所有请求,都会映射到static这目录中去查找相应的静态资源文件 -->
<mvc:resources mapping="/page/**" location="/static/"/>
六、创建web文件
快速在webapp文件夹下创建web文件
第一:添加依赖
<packaging>war</packaging>
第二:创建webapp文件夹
在项目的.\src\main\路径下,创建一个webapp文件夹
第三:手动添加web文件
注意:用此方法创建web文件,不能再添加web插件依赖(二选一)
七、SpringMVC中请求参数绑定
第一:创建web文件
第二:添加依赖
<packaging>war</packaging>
第三:配置dispatcher-servlet.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: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="edu.nf.ch04"/>
<!-- 启用mvc注解驱动 -->
<mvc:annotation-driven/>
<!-- 默认servlet处理静态资源处理 -->
<mvc:resources mapping="/page/**" location="/static/"/>
<!-- 内部资源视图解析器
一般添加了hibernate-validator依赖,就不需要再配置内部资源视图解析器,
只有在自定义路径、重定向时才需要配置
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 设置JSP资源的前缀,用于指定jsp存放的目录 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 设置jsp资源的后缀名,以'.jsp'结尾的文件-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
第四:配置web.xml
<?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">
<!-- 配置核心请求总控器,负责接收所有的请求,并根据映射的url地址将请求分发给具体控制器的方法来处理 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
第五:创建一个静态页面,添加用户
路径:webapp/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>添加用户</h2>
<form id="f1" method="post" action="../add3">
Name:<input type="text" name="username" value="simi"/><br>
Age:<input type="text" name="age" value="21"/><br>
Birth:<input type="text" name="birth" value="2021-10-22"/><br>
Tel1:<input type="text" name="tel" value="123235252532"/><br>
Tel2:<input type="text" name="tel" value="161242435332"/><br>
IdCard:<input type="text" name="card.cardNum" value="354636235623647425"/><br>
Address1:<input type="text" name="addresses[0].addr" value="广州"/><br>
Address2:<input type="text" name="addresses[1].addr" value="珠海"/><br>
<input type="submit" value="提交">
</form>
</body>
</html>
第六:创建一个jsp,接收信息
webapp/WEB-INF/jsp/index.jsp
<%--
Created by IntelliJ IDEA.
User: hy
Date: 2023/10/20
Time: 10:00
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>这是首页</h2>
用户名:${requestScope.username}<br>
年龄:${requestScope.age}<br>
生日:${requestScope.birth}<br>
电话1:${requestScope.tel1}<br>
电话2:${requestScope.tel2}<br>
住址1:${requestScope.addr1}<br>
住址2:${requestScope.addr2}<br>
身份证:${requestScope.idCard}<br>
路径参数:${requestScope.uid}<br>
</body>
</html>
第七:编写实体类
用户表
@Data
public class User {
private String username;
private Integer age;
private Date birth;
private List<String> tel;
// 一对一关联身份证
private IdCard card;
// 一对多关联地址
private List<Address> addresses;
}
身份证表
@Data
public class IdCard {
private String cardNum;
}
地址表
@Data
public class Address {
private String addr;
}
第八:编写controller层
1. 参数请求方法一
基本类型和String类型作为参数
package edu.nf.ch04.controller;
import edu.nf.ch04.entity.User;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @Date 2023-10-20
* @Author hy
*/
@Controller
@Slf4j
public class UserController {
@PostMapping("/add")
public ModelAndView add(HttpServletRequest request){
String username = request.getParameter("username");
String age = request.getParameter("age");
log.info(username,age);
// 将参数放入请求作用域
//request.setAttribute("username",username);
//request.setAttribute("age",age);
ModelAndView mav = new ModelAndView("index");
mav.addObject("username",username);
mav.addObject("age",age);
return mav;
}
}
2. 参数请求方法二
Date类型 和 数组类型 作为参数
package edu.nf.ch04.controller;
import edu.nf.ch04.entity.User;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @Date 2023-10-20
* @Author hy
*/
@Controller
@Slf4j
public class UserController {
/**
* 注册自定义转换器,@InitBinder注解标注的方法会在执行
* 任何Controller的方法之前先执行,springmvc会传入
* 一个WebBinder的参数,使用这个参数可以注册任意的Formatter(格式化器)
*
* @param binder 数据绑定器,用于注册各种格式化类
*/
@InitBinder
public void regFormatter(WebDataBinder binder){
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
};
/**
* 将请求数据直接绑定到参数上,
* 默认参数名与请求中的name保持一致即可映射,
* 否则使用@RequestParam注解来绑定参数名
* @param userName
* @param userAge
* @return
*/
@PostMapping("/add2")
public ModelAndView add2(@RequestParam("username") String userName,
@RequestParam("age") Integer userAge,
Date birth,
String[] tel){
ModelAndView mav = new ModelAndView("index");
// 将参数保存到请求作用域
mav.addObject("username",userName);
mav.addObject("age",userAge);
mav.addObject("birth",birth);
mav.addObject("tel1",tel[0]);
mav.addObject("tel2",tel[1]);
return mav;
}
}
3. 参数请求方法三
实体对象(包含集合类型) 作为参数
package edu.nf.ch04.controller;
import edu.nf.ch04.entity.User;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @Date 2023-10-20
* @Author hy
*/
@Controller
@Slf4j
public class UserController {
/**
* 注册自定义转换器,@InitBinder注解标注的方法会在执行
* 任何Controller的方法之前先执行,springmvc会传入
* 一个WebBinder的参数,使用这个参数可以注册任意的Formatter(格式化器)
*
* @param binder 数据绑定器,用于注册各种格式化类
*/
@InitBinder
public void regFormatter(WebDataBinder binder){
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
};
@PostMapping("/add3")
public ModelAndView add3(User user){
ModelAndView mav = new ModelAndView("index");
// 将参数保存到请求作用域
mav.addObject("username",user.getUsername());
mav.addObject("age",user.getAge());
mav.addObject("birth",user.getBirth());
mav.addObject("tel1",user.getTel().get(0));
mav.addObject("tel2",user.getTel().get(1));
mav.addObject("idCard",user.getCard().getCardNum());
mav.addObject("addr1",user.getAddresses().get(0).getAddr());
mav.addObject("addr2",user.getAddresses().get(1).getAddr());
return mav;
}
}
4. 路径参数请求
自定义路径参数
package edu.nf.ch04.controller;
import edu.nf.ch04.entity.User;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @Date 2023-10-20
* @Author hy
*/
@Controller
@Slf4j
public class UserController {
/**
* 路径参数绑定
* 请求格式:/url地址/{变量},
* 并且使用@PathVariable注解来映射
*
* @param uid
* @return
*/
@GetMapping("/user/{id}")
public ModelAndView getUser(@PathVariable("id") String uid){
ModelAndView mav = new ModelAndView("index");
mav.addObject("uid",uid);
return mav;
}
}
八、SpringMVC的注解
1、@InitBinder
- 标注的方法会在执行任何Controller的方法之前先执行
/**
* 注册自定义转换器,@InitBinder注解标注的方法会在执行任何Controller的方法之前先执行,
* springmvc会传入一个WebDataBinder的参数,使用这个参数可以注册任意的Formatter(格式化器)
*
* @param binder 数据绑定器,用于注册各种格式化类
*/
@InitBinder
public void regFormatter(WebDataBinder binder){
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
};
2、@RequestParam
- 当默认参数名与请求中的name不一致时,使用@RequestParam注解来绑定参数名
例如:下面示例中,请求的name是userName和userAge,而默认参数名是username和age
/**
* 将请求数据直接绑定到参数上,
* 默认参数名与请求中的name保持一致即可映射,
* 否则使用@RequestParam注解来绑定参数名
* @param userName
* @param userAge
* @return
*/
@PostMapping("/add2")
public ModelAndView add2(@RequestParam("username") String userName,
@RequestParam("age") Integer userAge,
Date birth,
String[] tel){
ModelAndView mav = new ModelAndView("index");
// 将参数保存到请求作用域
mav.addObject("username",userName);
mav.addObject("age",userAge);
mav.addObject("birth",birth);
mav.addObject("tel1",tel[0]);
mav.addObject("tel2",tel[1]);
return mav;
}
3、@PathVariable
- 映射路径参数
/**
* 路径参数绑定
* 请求格式:/url地址/{变量},
* 并且使用@PathVariable注解来映射
*
* @param uid
* @return
*/
@GetMapping("/user/{id}")
public ModelAndView getUser(@PathVariable("id") String uid){
ModelAndView mav = new ModelAndView("index");
mav.addObject("uid",uid);
return mav;
}
4、@ResponseBody
- 使用@ResponseBody注解,表示将方法返回值 以输出流的方式写回客户端
- 或者在类上直接使用@RestController,相当于当前类中所有的请求方法 都使用@ResponseBody注解
简单的说,就是将java对象(实体对象)转为json请求体格式的数据,也称之为序列化,响应给浏览器
如下案例:将User对象序列化为JSON数据{“username”:“simi”}
@Controller
@Slf4j
public class UserController {
@GetMapping("/user")
/**
* 使用@ResponsseBody注解,表示将方法返回值以输出流的方式写回客户端,
* 这样springmvc就会将序列化好的json数据放入响应体中并写回
*/
@ResponseBody
public User getUser(){
User user = new User();
user.setUsername("simi");
return user;
}
}
注意:添加依赖
<!-- 如果使用了JSON作为响应数据,需要依赖Jackson包,
它是spring官方默认集成的JSON序列化工具 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.1</version>
</dependency>
</dependencies>
九、视图响应注解
1、@RequestBody
- 当前端提交的是一个json字符串时,使用@RequestBody注解映射,springmvc就会将json字符串反序列化为实体对象。
简单的说,它是用来接收 前端传递给后端 的json字符串中的数据(请求体中的数据),
就是将前端的json格式的数据转为java对象(实体对象),也称之为反序列化,传入后台
请求体中的数据要传入后台,后台要用@RequestBody才能接收到
/**
* 当前端提交的是一个json字符串时,
* 此时要使用@RequestBody注解来映射,
* 这样springmvc就会将json字符串反序列化为实体对象
* @param user
* @return
*/
@PostMapping("/add")
public String add(@RequestBody User user){
log.info(user.getUsername());
return "success";
}
客户端(浏览器)向服务器发送请求(输入url地址),服务器接收请求并返回响应
2、转发
2.1 通过ModelAndView对象转发视图
package edu.nf.ch06.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestController {
/**
* 转发视图
* @return
*/
@GetMapping("/forward1")
public ModelAndView forward1(){
return new ModelAndView("index");
}
}
2.2 返回String类型,转发视图
package edu.nf.ch06.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
@Slf4j
public class TestController {
/**
* 转发视图2
* @return
*/
@GetMapping("/forward2")
public String forward2(){
log.info("转发视图");
return "index";
}
}
2.3 转发请求url,必须使用"forward:"前缀
package edu.nf.ch06.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
@Slf4j
public class TestController {
/**
* 转发视图2
* @return
*/
@GetMapping("/forward2")
public String forward2(){
log.info("转发视图");
return "index";
}
/**
* 转发请求url,必须使用"forward:"前缀
* 注意:转发视图是通过InternalResourceViewResolver解析器来完成的。
* 而转发请求的url是一个地址,并非视图,因此需要加上forward:前缀,并且url的路径必须写完整
* @return
*/
@GetMapping("/forward3")
public String forward3(){
return "forward:forward2";
}
}
3、重定向
重定向一个视图或者请求地址,结合“redirect:"这个前缀
package edu.nf.ch06.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
/**
* @Date 2023-10-23
* @Author hy
*/
@Controller
@Slf4j
public class TestController {
/**
* 如果需要重定向一个视图或者请求地址
* 可以结合“redirect:"这个前缀,
* 并且重定向的地址和视图名称必须是完整的url路径。
* 因为重定向是不会走内部资源视图解析器的
* @return
*/
@GetMapping("/redirect1")
public String redirect1(Model model){
model.addAttribute("name","simi");
// 重定向传递信息的方法
// 1.会话作用域
// 2.Url的重写
// 这里的参数是从地址栏里添加,不通过请求作用域,是额外的
// "redirect:redirect.jsp?name=simi"
return "redirect:redirect.jsp";
}
/**
* RedirectAttributes是springmvc3.1版本提供的一个类
* 主要用于在重定向时可以重写url,在重定向的url后面加入参数,
* 这样就可以变相在重定向的资源中获取相关的参数信息
* @param redirectAttributes
* @return
*/
@GetMapping("/redirect2")
public String redirect2(RedirectAttributes redirectAttributes){
redirectAttributes.addAttribute("name","simi");
// 相当于"redirect:redirect.jsp?name=simi"
return "redirect:redirect.jsp?";
}
}
十、表单验证
@NotEmpty注解
1. 添加依赖
<dependencies>
<!-- 如果使用了JSON作为响应的数据,需要依赖Jackson包
它是springmvc官方默认集成的JSON序列化工具 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.1</version>
</dependency>
<!-- 集成hibernate bean验证器 -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
2. 配置核心请求总控器
配置web.xml
<?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">
<!-- 配置核心请求总控器,负责接收所有的请求,并根据映射的url地址将请求分发给具体控制器的方法来处理 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3. 编写错误验证提示
在resources目录下,配置message.properties文件
user.userName.notEmpty = 请输入用户名
user.age.notNull = 请输入年龄
user.age.min = 年龄不得小于18岁
user.birth.notNull = 请输入出生年月
user.email.notEmpty = 请输入邮箱地址
user.email.legal = 请填写合法Email地址
4. 配置校验器
WEB-INF目录下,配置dispatcher-servlet.xml
查找到message.properties验证文件的路径,配置校验器(一起)
<?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="edu.nf.ch07"/>
<!-- 启用mvc注解驱动,注意:需要重新引用验证的id -->
<mvc:annotation-driven validator="validator"/>
<!-- 默认servlet处理静态资源处理 -->
<mvc:default-servlet-handler/>
<!--配置资源消息-->
<!-- 查找到message.properties验证文件的路径 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!--配置资源文件的路径-->
<property name="basenames">
<list>
<!--指定资源文件的位置,注意:不需要写文件的后缀-->
<value>classpath:message</value>
</list>
</property>
<!-- 指定编码格式 -->
<property name="defaultEncoding" value="utf-8"/>
</bean>
<!-- 配置校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!--注入校验器的提供方,-->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!-- 引入资源文件 -->
<property name="validationMessageSource" ref="messageSource"/>
</bean>
</beans>
5. 校验数据
编写Controller层方法
package edu.nf.ch07.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.nf.ch07.controller.vo.ResultVo;
import edu.nf.ch07.entity.User;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
/**
* @Date 2023-10-24
* @Author hy
*/
@RestController
public class UserController {
/**
* 注册日期格式化器
* @param binder
*/
@InitBinder
public void regFormatter(WebDataBinder binder){
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
/**
* @Valid注解标注的参数实体要参与Bean验证
*
* 从前端传递过来的data数据,进入User对象进行验证,
* 再通过BindingResult对象返回验证结果(在实体类验证时就自动生成该对象,这里只是回调使用)
*/
@PostMapping("/add")
public ResultVo add(@Valid User user, BindingResult result) throws JsonProcessingException {
ResultVo vo = new ResultVo();
// 先判断是否校验通过,如果存在错误则表示未通过
if(result.hasErrors()){
Map<String,String> errors = new HashMap<>();
result.getFieldErrors().forEach(fieldError ->{
// 以字段名作为key,错误信息作为value保存到map中
errors.put(fieldError.getField(),fieldError.getDefaultMessage());
});
// 设置响应状态码 - 500
vo.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
// 将map序列化为json字符串
String messages = new ObjectMapper().writeValueAsString(errors);
vo.setMessage(messages);
// 直接将errors保存到data中
//vo.setData(map);
return vo;
}
return vo;
}
}
6. 实体对象信息
package edu.nf.ch07.entity;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
* @Date 2023-10-24
* @Author hy
*/
@Data
public class User {
/**
* 验证空串
*/
// 方法一:直接输入验证错误信息
//@NotEmpty(message = "请输入用户名")
// 方法二:调用message.properties文件
@NotEmpty(message = "{user.userName.notEmpty}")
private String userName;
/**
* 验证空值
*/
@NotNull(message = "{user.age.notNull}")
/**
* 最小值范围
*/
@Min(value = 18,message = "{user.age.min}")
private Integer age;
@NotNull(message = "{user.birth.notNull}")
// 不推荐,只能定义一种,在实际开发中不可取
// @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
@NotEmpty(message = "{user.email.notEmpty}")
@Email(message = "{user.email.legal}")
private String email;
}
7. 注册页面-验证信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery-1.12.4.js"></script>
</head>
<body>
<h2>用户注册</h2>
<!-- 这个div用来显示验证的信息 -->
<div id="msg" style="color:red;"></div>
<form id="f1">
Name:<input type="text" name="userName"/><br>
Age:<input type="text" name="age"/><br>
Birth:<input type="text" name="birth"/><br>
Email:<input type="text" name="email"/><br>
<input type="button" value="注册"/>
</form>
<script>
$(function (){
$(':button').on('click',function (){
$.ajax({
url:'../add',
type:'post',
// 请求体格式:username=simi&age=12
// json字符串:{"username":"simi","age":"12"}
// json对象[object Object],可以打开
/**
* 序列化表单数据,这里数据是以请求体格式(username=zs&age=20...)传递的,
* 如果我们提交的不是规范的请求体格式而是一个json字符串{‘userName’:'zs','age':20...},
* 那么我们后端就需要使用@RequestBody注解来映射,这样springmvc就会将json字符串反序列化为实体对象。
*/
data:$('#f1').serialize(),
success:function (result){
if(result.code == 500){
$('#msg').empty();
let errors = result.message;
//将json字符串转换为json对象
errors = $.parseJSON(errors);
// 循环遍历错误信息放在div中
$.each(errors,function (key,val){
$('#msg').append(val + '<br>')
});
}else if(result.code == 200){
alert("注册成功")
}
}
})
})
})
</script>
</body>
</html>
十一、附件上传
上传过程:打开文件夹,上传到指定路径
1. 添加依赖
<!-- 上传组件 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
2. 配置核心请求总控器
编写web.xml文件
<?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">
<!-- 配置核心请求总控器,负责接收所有的请求,并根据映射的url地址将请求分发给具体控制器的方法来处理 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3. 装配上传附件解析器
编写dispatcher-servlet.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: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="edu.nf.ch08"/>
<!-- 启用mvc注解驱动 -->
<mvc:annotation-driven/>
<!-- 默认servlet处理静态资源处理 -->
<mvc:default-servlet-handler/>
<!-- 装配上传附件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 限制文件上传的总大小(单位:字节),不配置此属性默认不限制 -->
<property name="maxUploadSize" value="104857600"/>
<!-- 设置文件上传的默认编码-->
<property name="defaultEncoding" value="utf-8"/>
</bean>
</beans>
4. 保存页面提交的数据
编写ProductVO,相当于实体类
package edu.nf.ch08.controller.vo;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/**
* 商品vo对象,用于保存页面提交的数据
* 后续将这个vo拷贝到entity中
* @Date 2023-10-24
* @Author hy
*/
@Data
public class ProductVO {
/**
* 商品名称
*/
private String productName;
/**
* MultipartFile是springmvc封装的上传附件对象
* 商品图片
*/
private MultipartFile[] file;
}
5. 封装结果集
package edu.nf.ch08.controller.vo;
import lombok.Data;
import org.springframework.http.HttpStatus;
/**
* 返回结果集
* @Date 2023-10-24
* @Author hy
*/
@Data
public class ResultVO <T>{
// 设置默认值为200
private Integer code = HttpStatus.OK.value();
private String message;
private T data;
}
6. 上传附件
编写Controller层方法
package edu.nf.ch08.controller;
import ch.qos.logback.core.FileAppender;
import edu.nf.ch08.controller.vo.ProductVO;
import edu.nf.ch08.controller.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
/**
* @Date 2023-10-24
* @Author hy
*/
@Slf4j
@RestController
public class ProductController {
/**
* 添加商品,同时带有上传的附件
* @param productVO
* @return
*/
@PostMapping("/add")
public ResultVO add(ProductVO productVO) throws IOException {
ResultVO vo = new ResultVO();
// 获取上传的路径 (绝对路径)
String uploadPath = "D://springmvc/files/";
log.info(uploadPath);
// 根据路径构建上传的文件对象
File uploadFile = new File(uploadPath);
// 判断路径创建出的文件夹是否存在,不存在则创建出来
if(!uploadFile.exists()){
// 将文件夹创建出来
uploadFile.mkdirs();
}
// 获取所有上传的附件
// 上传单条
//MultipartFile file = productVO.getFile();
// 上传多条
MultipartFile[] files = productVO.getFile();
// 循环遍历
for(MultipartFile file :files){
// 获取文件名
String filName = file.getOriginalFilename();
// 执行上传
Path path = FileSystems.getDefault().getPath(uploadFile.getAbsolutePath(),filName);
// 执行IO的读写
file.transferTo(path);
}
return vo;
}
}
7. 添加附件的页面
前端提交商品信息,multiple表示多选
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery-1.12.4.js"></script>
</head>
<body>
<h1>添加商品</h1>
<form id="f1" enctype="multipart/form-data">
Name:<input type="text" name="productName"/><br>
Image:<input type="file" name="file" multiple/><br>
<input type="button" value="提交"/>
</form>
<script>
$(function (){
$(':button').on('click',function (){
// 构建formData对象
// formData对象是 Html5的内置对象,同样是处理from表单的数据
// 但formData对象可以处理文本和二进制数据,而序列化只能在文本有效
// 这里包含附件,所以使用formData对象
let formData = new FormData($('#f1')[0]);
$.ajax({
url: '../add',
type: 'post',
data: formData,
// 设置属性为false,就是使formData对象正常使用,而不会回到序列化的请求格式
processData: false, // 告诉jquery不要处理发送的数据类型
contentType: false, // 告诉jquery不要设置请求头的content-Type
success: function (result){
if(result.code == 200){
alert('上传成功');
}
}
})
})
})
</script>
</body>
</html>
十二、本地下载
下载过程:idea的控制层指定下载文件的路径,通过浏览器,下载到浏览器默认下载路径
本地下载
编写Controller层方法,这里使用的是路径参数
也可以在浏览器里输入http://localhost:8080/download?fileName=''指定文件名"
package edu.nf.ch08.controller;
import edu.nf.ch08.controller.vo.ProductVO;
import edu.nf.ch08.controller.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.FileSystems;
import java.nio.file.Path;
/**
* @Date 2023-10-24
* @Author hy
*/
@Slf4j
@RestController
public class ProductController {
/**
* 下载
* @param fileName
* @return
* @throws FileNotFoundException
* @throws UnsupportedEncodingException
*/
@GetMapping("/download/{fileName}")
public ResponseEntity<InputStreamResource> download(@PathVariable("fileName") String fileName) throws FileNotFoundException, UnsupportedEncodingException {
// 文件下载路径(也是上传路径), 注意:文件夹不存在文件,需先上传再下载
String downloadPath = "D://springmvc/files/" + fileName;
// 构建一个文件输入流读取服务器上的文件
FileInputStream fis = new FileInputStream(downloadPath);
// 设置响应头,告诉浏览器响应流数据
HttpHeaders headers = new HttpHeaders();
// 对文件名进行编码,防止在响应头中出现乱码
fileName = URLEncoder.encode(fileName,"UTF-8");
// 设置头信息,将响应内容处理的方式设置为附件下载
headers.setContentDispositionFormData("attachment", fileName);
// 设置响应类型为流类型
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 创建InputStreamResource对象封装输入流,用于读取服务器文件
InputStreamResource isr = new InputStreamResource(fis);
// 创建ResponseEntity对象(封装InputStreamResource、响应头、响应状态码)
ResponseEntity<InputStreamResource> entity = new ResponseEntity<>(isr,headers, HttpStatus.CREATED);
return entity;
}
}
十三、minio的应用
1. 远程桶
1.1 下载minio
1.2 cmd命令启动minio
打开含有minio.exe的文件夹,输入cmd,打开命令符
输入:minio.exe server D:\minio\data --console-address “:9001” (D:\minio\data:minio的下载路径)
1.3 浏览器打开远程minio
通过cmd命令启动后,才能在浏览器中打开
输入网址:http://127.0.0.1:9001/,密码跟账户一致
1.4 设置文件访问权限
1.5 访问文件
输入文件路径:http://127.0.0.1:9000/myapp/images/cb.jpg
2. idea创建桶
第一:添加依赖
<!-- minio客户端-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.4</version>
</dependency>
第二:创建桶
编写MinioUtils工具类
package edu.nf.minio.util;
import io.minio.*;
import java.io.IOException;
public class MinioUtils {
//minioClient:minio服务器的客户端
private static MinioClient minioClient;
static {
// 创建minioClient
minioClient = MinioClient.builder()
// 文件访问路径
.endpoint("http://127.0.0.1:9000")
// 账户密码
.credentials("minioadmin","minioadmin")
.build();
}
/**
* 创建桶
* 每个桶之间都是隔离的
* @param bucketName
*/
public static void createBucket(String bucketName) throws Exception{
// 先判断桶是否存在,不存在则创建
if(!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())){
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
}
第三:实现
package edu.nf;
import edu.nf.minio.util.MinioUtils;
import io.minio.MinioClient;
public class Main {
public static void main(String[] args) throws Exception {
// 创建桶
MinioUtils.createBucket("myapp");
}
}
3. 上传
上传前先启动远程桶
先将文件上传到本地,再将本地文件上传到远程minio,两次读写
package edu.nf.minio.util;
import io.minio.*;
public class MinioUtils {
private static MinioClient minioClient;
static {
// 创建minioClient
minioClient = MinioClient.builder()
.endpoint("http://127.0.0.1:9000")
.credentials("minioadmin","minioadmin")
.build();
}
/**
* 上传桶
* @param bucketName 桶的名称
* @param remotePath 远程要上传文件路径
* @param localPath 本地文件路径
*/
public static void upload(String bucketName,String remotePath,String localPath) throws Exception {
minioClient.uploadObject(UploadObjectArgs.builder().
// 指定桶的名字
bucket(bucketName)
// 上传的远程文件路径
.object(remotePath)
// 本地文件的路径
.filename(localPath)
.build());
}
}
实现
public class Main {
public static void main(String[] args) throws Exception {
// 上传
String bucket = "myapp";
String remotePath = "images/cb.jpg";
String localPath = "D://springmvc/files/cb.jpg";
MinioUtils.upload(bucket,remotePath,localPath);
}
}
4. 下载
下载前先启动远程桶
将远程minio文件,下载到本地
package edu.nf.minio.util;
import io.minio.*;
import java.io.IOException;
public class MinioUtils {
private static MinioClient minioClient;
static {
// 创建minioClient
minioClient = MinioClient.builder()
.endpoint("http://127.0.0.1:9000")
.credentials("minioadmin","minioadmin")
.build();
}
/**
* 下载文件
*
* @param bucketName 桶的名称
* @param remotePath 远程文件路径
* @param downloadPath 下载到本地保存的路径
*/
public static void download(String bucketName, String remotePath, String downloadPath) throws Exception {
minioClient.downloadObject(DownloadObjectArgs.builder()
// 指定桶的名字
.bucket(bucketName)
// 远程文件路径
.object(remotePath)
// 下载到本地保存的路径
.filename(downloadPath)
.build());
}
}
实现
将远程minio的文件,下载到桌面
public class Main {
public static void main(String[] args) throws Exception {
// 下载
String bucket = "myapp";
String remotePath = "images/cb.jpg";
String localPath = "C://Users/hy/Desktop/cb.jpg";
MinioUtils.download(bucket,remotePath,localPath);
}
}
十四、异常处理
1、配置 web.xml
<?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">
<!-- 配置核心请求总控器,负责接收所有的请求,并根据映射的url地址将请求分发给具体控制器的方法来处理 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2、配置 dispatcher-servlet.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: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="edu.nf.ch09"/>
<!-- 启用mvc注解驱动 -->
<mvc:annotation-driven/>
<!-- 默认servlet处理静态资源处理 -->
<mvc:default-servlet-handler/>
</beans>
3、封装结果集
package edu.nf.ch09.controller.vo;
import lombok.Data;
import org.springframework.http.HttpStatus;
/**
* @Date 2023-10-27
* @Author hy
*/
@Data
public class ResultVO<T> {
private Integer code = HttpStatus.OK.value();
private String message;
private T data;
}
4、自定义异常
package edu.nf.ch09.exception;
/**
* 自定义异常
* @Date 2023-10-27
* @Author hy
*/
public class AuthException extends RuntimeException{
/**
* 异常的状态码
*/
private Integer errorCode;
public AuthException(Integer errorCode,String message){
super(message);
this.errorCode = errorCode;
}
/**
* 提供一个获取错误码的方法
* @return
*/
public Integer getErrorCode(){
return errorCode;
}
}
5、dao实现
package edu.nf.ch09.dao.impl;
import edu.nf.ch09.dao.UserDao;
import edu.nf.ch09.entity.User;
import org.springframework.stereotype.Repository;
/**
* @Date 2023-10-27
* @Author hy
*/
@Repository
public class UserDaoImpl implements UserDao {
@Override
public User getUser(String username) {
User user = new User();
user.setUsername(username);
user.setPassword("123456");
return user;
}
}
6、service实现类
package edu.nf.ch09.service.impl;
import edu.nf.ch09.dao.UserDao;
import edu.nf.ch09.entity.User;
import edu.nf.ch09.exception.AuthException;
import edu.nf.ch09.service.LoginService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @Date 2023-10-27
* @Author hy
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {
private final UserDao userDao;
@Override
public User auth(String username, String password) {
User user = userDao.getUser(username);
// 用户不为null则校验密码
if(user != null){
if(password.equals(user.getPassword())){
return user;
}
}
// 抛出业务异常
throw new AuthException(1000,"账户或密码错误");
}
}
7、局部异常
package edu.nf.ch09.controller;
import edu.nf.ch09.controller.vo.ResultVO;
import edu.nf.ch09.entity.User;
import edu.nf.ch09.service.LoginService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
/**
* @Date 2023-10-27
* @Author hy
*/
@Slf4j
@RestController
@RequiredArgsConstructor
public class UserController {
private final LoginService service;
/**
* 局部异常处理(处理登录业务异常)
* @ExceptionHandler注解标注的方法专门用于处理请求方法产生的异常
* value属性指定要处理的异常类型
* 注意:这个局部异常只在当前的Controller中有效,也就是说
* 每个controller都有自己专门的handlerException的方法
* @param e
* @return
*/
// @ExceptionHandler({AuthException.class})
// public ResultVO handleAuthException(AuthException e){
// //验证未通过则创建提示信息
// ResultVO vo = new ResultVO();
// vo.setCode(e.getErrorCode());
// vo.setMessage(e.getMessage());
// return vo;
// }
/**
* 局部异常处理(处理非业务异常,如数据库异常等)
* @param
* @return
*/
// @ExceptionHandler({RuntimeException.class})
// public ResultVO handleRunTimeException(RuntimeException e){
// // 记录异常日志
// log.error(e.getMessage());
// // 再将异常转换为提示信息
// ResultVO vo = new ResultVO();
// vo.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
// vo.setMessage("服务器内部错误,请稍后尝试");
// return vo;
// }
@PostMapping("/login")
public ResultVO login(String username, String password, HttpSession session){
User user = service.auth(username, password);
log.info(user.getPassword(),user.getPassword());
// 将用户保存到会话
session.setAttribute("user", user);
return new ResultVO();
}
}
8、全局异常
package edu.nf.ch09.controller.advice;
import edu.nf.ch09.controller.vo.ResultVO;
import edu.nf.ch09.exception.AuthException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 定义一个全局异常处理类(类似一个切面)
* 这个类中定义所有的异常处理方法,也可以
* 理解为全局异常通知
* @Date 2023-10-27
* @Author hy
*/
// @RestControllerAdvice(对应@Controller注解的类)
// (对应@ResutController注解的类)
// value属性表示:controller包下的所有类都参与异常捕获
@RestControllerAdvice("edu.nf.ch09.controller")
@Slf4j
public class ExceptionAdvice {
/**
* 全局异常处理方法(处理登录业务异常)
* @param e
* @return
*/
@ExceptionHandler({AuthException.class})
public ResultVO handleAuthException(AuthException e){
//验证未通过则创建提示信息
ResultVO vo = new ResultVO();
vo.setCode(e.getErrorCode());
vo.setMessage(e.getMessage());
return vo;
}
/**
* 全局异常处理方法(处理非业务异常,如数据库异常等)
* @param e
* @return
*/
@ExceptionHandler({Exception.class})
public ResultVO handleRunTimeException(Exception e){
// 记录异常日志
log.error(e.getMessage());
// 再将异常转换为提示信息
ResultVO vo = new ResultVO();
vo.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
vo.setMessage("服务器内部错误,请稍后尝试");
return vo;
}
}
9、页面实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<script src="js/jquery-1.12.4.js"></script>
</head>
<body>
<h1>用户登录</h1>
<form id="f1">
账户:<input type="text" name="username"/><br>
密码:<input type="password" name="password"/><br>
<input type="button" value="登录"/>
</form>
<script>
$(function (){
$(':button').on('click',function (){
let formData = $('#f1').serialize();
$.ajax({
url:'../login',
type:'post',
data: formData,
success:function (result){
console.info(result)
if(result.code == 200){
// 跳转到首页
location.href = 'index.html';
}else if(result.code == 1000){
alert(result.message);
}
}
})
})
})
</script>
</body>
</html>
十五、拦截器
1、配置web.xml
2、封装结果集
3、配置拦截器栈
配置dispatcher-servlet.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: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="edu.nf.ch10"/>
<!-- 启用mvc注解驱动 -->
<mvc:annotation-driven/>
<!-- 默认servlet处理静态资源处理 -->
<mvc:default-servlet-handler/>
<!-- 配置拦截器栈 -->
<mvc:interceptors>
<!-- 配置具体的拦截器 -->
<mvc:interceptor>
<!-- 设置哪些请求会经过这个拦截器 -->
<mvc:mapping path="/**"/>
<!-- 排除哪些请求不经过这个拦截器 -->
<mvc:exclude-mapping path="/static/login.html"/>
<mvc:exclude-mapping path="/auth"/>
<mvc:exclude-mapping path="/static/js/**"/>
<!-- 装配具体的拦截器 -->
<bean class="edu.nf.ch10.interceptor.AuthInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
4、service实现
package edu.nf.ch10.service.impl;
import edu.nf.ch10.entity.User;
import edu.nf.ch10.service.LoginService;
import org.springframework.stereotype.Service;
/**
* @Date 2023-10-27
* @Author hy
*/
@Service
public class LoginServiceImpl implements LoginService {
/**
* 登录验证
* @param username
* @param password
* @return
*/
@Override
public User auth(String username, String password) {
if("simi".equals(username) && "123456".equals(password)){
User user = new User();
user.setUsername(username);
user.setPassword(password);
return user;
}
throw new RuntimeException("账户或密码错误");
}
}
5、controller实现
package edu.nf.ch10.controller;
import edu.nf.ch10.controller.vo.ResultVO;
import edu.nf.ch10.entity.User;
import edu.nf.ch10.service.LoginService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
/**
* @Date 2023-10-27
* @Author hy
*/
@RestController
@RequiredArgsConstructor
public class UserController {
private final LoginService service;
@PostMapping("/auth")
public ResultVO login(String username, String password, HttpSession session){
User user = service.auth(username,password);
session.setAttribute("user",user);
return new ResultVO();
}
}
6、拦截器
package edu.nf.ch10.interceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.nf.ch10.controller.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 认证拦截器
* 拦截所有的请求,如果为登录则返回401状态码
* 401:未认证,用户未登录
* 403:没有权限,用户没有相关权限
* @Date 2023-10-27
* @Author hy
*/
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
/**
* 在调用Controller请求方法之前执行,
* 如果此方法返回false,则请求就不会继续往下执行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("执行preHandle方法");
HttpSession session = request.getSession();
// 如果session为null表示用户未登录
if(session.getAttribute("user") == null){
ResultVO vo = new ResultVO();
// 设置401状态码
vo.setCode(HttpStatus.UNAUTHORIZED.value());
vo.setMessage("未登录");
response.setContentType("application/json;charset=utf-8");
String json = new ObjectMapper().writeValueAsString(vo);
response.getWriter().println(json);
return false;
}
return true;
}
/**
* 在调用Controller方法之后,返回之前执行(也就是说,开始了controller的方法,但是还没有执行return操作时,调用此方法)
* (注意:只有在preHandle方法返回true的情况下才会执行)
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("执行postHandle方法");
}
/**
* 调用Controller方法并返回后执行(也就是说,已经执行完return操作,才调用此方法)
* (注意:只有在preHandle方法返回true的情况下才会执行)
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
7、页面实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery-1.12.4.js"></script>
</head>
<body>
<h1>用户登录</h1>
<form id="f1">
账户:<input type="text" name="username"/><br>
密码:<input type="password" name="password"/><br>
<input type="button" value="登录"/>
</form>
<script>
$(function (){
$(':button').on('click',function (){
let formData = $('#f1').serialize();
$.ajax({
url:'../auth',
type:'post',
data: formData,
success:function (result){
if(result.code == 200){
location.href = 'index.html';
}
}
})
})
})
</script>
</body>
</html>
十六、零XML配置
用配置类取代xml配置
在使用Spring MVC时,每次都要去配置web.xml、spring-mvc.xml,甚至和Spring整合时候,还要配置spring.xml,用起来比较麻烦。而Spring Boot中使用Spring MVC时就不会去指定xml,这是因为Spring Boot提供了一种零XML配置的方式。
具体实现方式如下:
通过注解@EnableWebMvc启用Spring MVC;
通过@Configuration注解定义一个配置类,并实现WebMvcConfigurer接口;
在配置类中重写addResourceHandlers方法,添加静态资源路径;
在配置类中重写addViewControllers方法,添加视图控制器;
在配置类中重写configureDefaultServletHandling方法,开启默认Servlet处理;
在配置类中重写configureViewResolvers方法,配置视图解析器。
通过以上步骤,就可以实现Spring MVC的零XML配置。这种方式可以减少配置文件的数量,提高开发效率,同时也更加符合现代化的开发理念。
配置类一:JDBC
package edu.nf.ch12.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.io.InputStream;
import java.util.Properties;
/**
* JDBC配置
* @Date 2023-10-30
* @Author hy
*/
@Configuration
// 启用事务注解驱动,这样就可以在业务类中使用@Transaction注解
@EnableTransactionManagement
public class JdbcConfig {
/**
* 整合druid数据源连接池,装配DruidDataSource
* @return
*/
@Bean(initMethod = "init",destroyMethod = "close")
public DruidDataSource dataSource() throws Exception {
// 创建Properties对象
Properties prop = new Properties();
// 获取一个输入流来读取properties文件
InputStream input = SpringConfig.class.getClassLoader()
.getResourceAsStream("jdbc.properties");
// 将输入流加载到properties读写中
prop.load(input);
// 通过DruidDataSourceFactory来创建DruidDataSource实例
DruidDataSource ds = (DruidDataSource) DruidDataSourceFactory.createDataSource(prop);
return ds;
}
/**
* 装配事务管理器,并注入数据源,
* 这样事务管理器就可以基于AOP来管理Connection的事务操作
* @param dataSource
* @return
*/
@Bean
public PlatformTransactionManager txManager(DruidDataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
配置类二:springmvc
package edu.nf.ch12.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
/**
* springmvc配置类,
* 用于取代dispatcher-servlet.xml
* @Date 2023-10-30
* @Author hy
*/
// 声明配置类
@Configuration
// 启动mvc注解驱动
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
/**
* 静态资源处理
* @param configurer
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//enable()方法表示使用容器默认servlet处理
configurer.enable();
}
/**
* 配置内部资源视图解析器(内部资源视图的转发)
* @return
*/
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
配置类三:mybatis
package edu.nf.ch12.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageInterceptor;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.IOException;
import java.util.Properties;
/**
* @Date 2023-10-30
* @Author hy
*/
@Configuration
// 扫描dao接口的包,动态生成到接口的代理实现
@MapperScan(basePackages = "edu.nf.ch12.dao")
public class MybatisConfig {
/**
* 整合mybatis,装配SqlSessionFactory
* @param dataSource
* @return
* @throws IOException
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DruidDataSource dataSource) throws IOException {
// 创建SqlSessionFactoryBean
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// 注入数据源
factoryBean.setDataSource(dataSource);
// 注入mybatis其他设置
factoryBean.setTypeAliasesPackage("edu.nf.ch12.entity");
// 设置mapper映射文件的路径
// 先创建一个资源路径解析器来查找源文件
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factoryBean.setMapperLocations(resolver.getResources("classpath:mappers/*.xml"));
// 设置分页插件,创建分页拦截器
PageInterceptor interceptor = new PageInterceptor();
// 设置属性
Properties properties = new Properties();
properties.setProperty("helperDialect","mysql");
properties.setProperty("supportMethodsArguments","true");
properties.setProperty("reasonable","true");
// 将分页属性设置到拦截器中
interceptor.setProperties(properties);
// 最后将分页拦截器设置到SqlSessionFactoryBean的插件中
factoryBean.setPlugins(interceptor);
// 返回SqlSessionFactoryBean给容器
return factoryBean;
}
}
配置类四:spring主配置
package edu.nf.ch12.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 主配置类
* @Date 2023-10-30
* @Author hy
*/
@Configuration
// 扫描指定的包,装配相关的Bean
@ComponentScan(basePackages = "edu.nf.ch12")
@Import({JdbcConfig.class,MybatisConfig.class, MvcConfig.class})
public class SpringConfig {
}
配置类五:web
package edu.nf.ch12.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* web配置类,用于取代web.xml
* @Date 2023-10-30
* @Author hy
*/
@Configuration
public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 加载主配置
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
/**
* 加载mvc的配置类
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 给DispatcherServlet配置url-pattern
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
十七、过滤器/拦截器/监视器的区别
1、过滤器
过滤器(Filter):当你有一堆东西的时候,你只希望选择符合你要求的某一些东西。定义这些要求的工具,就是过滤器。
Servlet 中的过滤器 Filter 是实现了 javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断等。
其工作原理是,只要你在web.xml
文件配置好要拦截的客户端请求,它都会帮你拦截到请求,
此时你就可以对请求或响应(Request、Response)统一设置编码,简化操作;
同时还可以进行逻辑判断,如用户是否已经登录、有没有权限访问该页面等等工作,它是随你的web应用启动而启动的,
只初始化一次,以后就可以拦截相关的请求,只有当你的web应用停止或重新部署的时候才能销毁。
在javax.servlet.Filter接口中定义了3个方法:
- void init(FilterConfig filterConfig) 用于完成过滤器的初始化
- void destroy() 用于过滤器销毁前,完成某些资源的回收
- void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) 实现过滤功能,该方法对每个请求增加额外的处理
2、拦截器
拦截器(Interceptor):在一个流程正在进行的时候,你希望干预它的进展,甚至终止它进行,这是拦截器做的事情。
拦截器是在面向切面编程中应用的。也就是在你的service或者一个方法前/后调用一个方法。
比如 动态代理就是拦截器的简单实现,可以在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至可以在你抛出异常的时候做业务逻辑的操作。
拦截器不是在web.xml配置的。比如struts在struts.xml配置,springMVC在spring与springMVC整合的配置文件中配置。
在springmvc中,定义拦截器要实现 HandlerInterceptor接口,并实现该接口提供的三个方法:
- preHandle方法:进入Handler方法之前执行。可以用于身份认证、身份授权。比如如果认证没有通过表示用户没有登陆,需要此方法拦截不再往下执行(return false),否则就放行(return true)。
- postHandle方法:进入Handler方法之后,返回ModelAndView之前执行。可以看到该方法中有个modelAndView的形参。
应用场景:从modelAndView出发:将公用的模型数据(比如菜单导航之类的)在这里传到视图,也可以在这里同一指定视图。 - afterCompletion方法:执行Handler完成之后执行。应用场景:统一异常处理,统一日志处理等。
3、监听器
监听器(Listener):当一个事件发生的时候,你希望获得这个事件发生的详细信息,而并不想干预这个事件本身的进程,这就要用到监听器。
Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener接口的服务器端程序,它也是随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。主要作用是:做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。
在 javax.servlet.ServletContextListener接口 中定义了2种方法:
- void contextInitialized(ServletContextEvent sce) 监听器的初始化
- void contextDestroyed(ServletContextEvent sce) 监听器销毁
4、过滤器 和 拦截器的区别
1)拦截器是基于Java的反射机制的,而过滤器是基于函数回调
2)过滤器依赖与servlet容器,而拦截器不依赖与servlet容器
3)拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用
4)拦截器可以访问action上下文、值栈里的对象,而过滤器不能
5)在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
十八、自定义异常
1、什么是自定义异常类
在开发中根据自己业务的异常情况Q来定义异常类。
2、为什么需要自定义异常类
JAVA中不同的异常类分别表示某一种具体的异常情况,尽管内部有定义好的一些异常情况,但是在实际开发中总是有些异常情况是Sun没有定义过,此时我们根据自己业务的异常情况来定义异常类。