springMVC
springMVC概述
三层架构
- 表现层:负责数据展示
- 业务层:负责业务处理
- 数据层:负责数据操作
概念
- Spring MVC 是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架。
- MVC(Model View Controller),一种用于设计创建Web应用程序表现层的模式
- Model(模型):数据模型,用于封装数据
- View(视图):页面视图,用于展示数据
- Controller(Handle 处理器):处理用户交互的调度器,用于根据用户需求处理程序逻辑
spring MVC步骤
新建javaEE项目
导入maven依赖
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xinzhi</groupId>
<artifactId>springMVC</artifactId>
<version>1.0-SNAPSHOT</version>
<name>springMVC</name>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<junit.version>5.9.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- servlet3.1规范的坐标 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--jsp坐标-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!--spring的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--spring web的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--springmvc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
创建controller包
创建UserController类
@Controller
public class UserController {
@RequestMapping("/save")
public String say(){
System.out.println("你好");
return "a.jsp";
}
}
在resources下创建spring-mvc.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="com.xinzhi"/>
</beans>
编写web.xml文件,配置前端控制器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<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>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
在webapp下创建a.jsp和b.jsp文件
配置tomcat(选择9.0.80版本)
启动测试
工作流程分析
- 服务器启动
- 加载web.xml中DispatcherServlet
- 读取spring-mvc.xml中的配置,加载所有com.xinzhi包中所有标记为bean的类
- 读取bean中方法上方标注@RequestMapping的内容
- 处理请求
- DispatcherServlet配置拦截所有请求 /
- 使用请求路径与所有加载的@RequestMapping的内容进行比对
- 执行对应的方法
- 根据方法的返回值在webapp目录中查找对应的页面并展示
- web三大组件有 处理器映射,处理器适配器, 视图解析器
- dispatcherServlet 前置控制器,负责接收并处理所有的web请求,根据handlerMapping(处理器映射)找到具体的Controller(处理器),由controller完成具体的处理逻辑。
- HandlerMapping(处理器映射器):负责处理web请求和具体的Controller之间的映射关系匹配。
- HandlerAdapter(处理器适配器) 通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 主要处理方法参数、相关注解、数据绑定、消息转换、返回值、调用视图解析器等等。
- Controller(处理器):DispatherServlet的次级控制器,web请求的具体处理者。DispatherServlet获得handlerMapping的返回结果后,调用controller的处理方法处理当前的业务请求,处理完成后返回ModelAndView对象。
- ViewResolver( 视图解析器):用来处理视图名与具体的view实例之间的映射对应关系。根据ModelAndView中的视图名查找相应的View实现类,然后将查找的结果返回给DispatcherServlet,DispatcherServlet最终会将ModelAndView中的模型数据交给返回的View处理最终的视图渲染工作。
Springmvc架构原理解析
- 发起请求到前端控制器(DispatcherServlet)
- 前端控制器请求HandlerMapping查找 Handler,可以根据xml配置、注解进行查找
- 处理器映射器HandlerMapping向前端控制器返回Handler
- 前端控制器调用处理器适配器去执行Handler
- 处理器适配器去执行Handler
- Handler执行完成给适配器返回ModelAndView
- 处理器适配器向前端控制器返回ModelAndView
ModelAndView是springmvc框架的一个底层对象,包括 Model和view - 前端控制器请求视图解析器去进行视图解析
根据逻辑视图名解析成真正的视图(jsp) - 视图解析器向前端控制器返回View
- 前端控制器进行视图渲染
视图渲染将模型数据(在ModelAndView对象中)填充到request域 - 前端控制器向用户响应结果
请求参数的绑定
- 默认类型:
直接放在参数上就可以使用的数据,HttpServletRequest - 简单类型:
直接将简单类型的数据放在方法里,如果前端参数和后端参数名字一样,自动匹配;
名字不一样:@RequsetParam(“前端的值”) 就可以将前端的值和后端参数映射 - 对象类型:
前端的参数要和对象的属性名称必须一致,会自动封装。 - 对象嵌套:
参数和对象的属性名称一致,前端参数对象子属性必须(子对象.属性) - 自定义数据的绑定
- 编写转换器类,作用是将前端的数据类型转换成后端的数据类型,继承converter
- 配置文件中,添加转化器驱动
- 数组类型:
前端数组中是简单类型的数据,那么前端数组中的name要和后端数组名称一致 - 集合类型:
后端接受的对象是含有List<对象>属性的,那么前端的name值格式要和后端list属性名称一致,而且用索引的格式 list[0].属性(list集合里对象的属性名称)
拦截器
创建interceptor包,创建MyInterceptor类并实现HandlerInterceptor接口
/**
* 三个方法的运行顺序为 preHandle -> postHandle -> afterCompletion
* 如果preHandle返回值为false,三个方法仅运行preHandle
*/
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("前置运行");
//返回值为false将拦截原始处理器的运行
//如果配置多拦截器,返回值为false将终止当前拦截器后面配置的拦截器的运行
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("后置运行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("完成运行");
}
}
在spring-mvc.xml配置文件中配置拦截器
<beans
xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login"/>
<bean class="com.xinzhi.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
拦截器配置项
<mvc:interceptors>
<!--开启具体的拦截器的使用,可以配置多个-->
<mvc:interceptor>
<!--设置拦截器的拦截路径,支持*通配-->
<!--/** 表示拦截所有映射-->
<!--/* 表示拦截所有/开头的映射-->
<!--/user/* 表示拦截所有/user/开头的映射-->
<!--/user/add* 表示拦截所有/user/开头,且具体映射名称以add开头的映射-->
<!--/user/*All 表示拦截所有/user/开头,且具体映射名称以All结尾的映射-->
<mvc:mapping path="/*"/>
<mvc:mapping path="/**"/>
<mvc:mapping path="/handleRun*"/>
<!--设置拦截排除的路径,配置/**或/*,达到快速配置的目的-->
<mvc:exclude-mapping path="/b*"/>
<!--指定具体的拦截器类-->
<bean class="MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
拦截器的使用场景
- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
- 权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;
- 性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
拦截器登录案例
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 检查用户是否已登录
if (!isUserLoggedIn(request)) {
// 未登录,重定向到登录页面
response.sendRedirect("/login");
return false;
}
// 已登录,继续请求处理
return true;
}
private boolean isUserLoggedIn(HttpServletRequest request) {
// 在这里实现用户登录状态的检查逻辑
// 如果用户已登录,返回true;否则返回false
// 可以根据具体业务需求来实现用户登录状态的判断
// 例如,可以检查Session中是否包含登录用户的信息
// 或者检查请求头中是否包含有效的身份验证令牌等
// 这里只是一个示例,具体实现根据实际情况进行调整
return request.getSession().getAttribute("user") != null;
}
}
在spring-mvc.xml配置文件中,我们可以指定拦截器的拦截路径和排除路径。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/> <!-- 拦截所有请求 -->
<mvc:exclude-mapping path="/login"/> <!-- 排除登录请求 -->
<bean class="com.example.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
文件上传
导入maven依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
前端页面
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上传</title>
<script type="text/javascript" src="./js/jquery-1.11.3.min.js"></script>
</head>
<body>
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
上传文件: <input type="button" value="添加文件" onclick="add()"/>
<div id="file" style="margin-top: 10px;" value="文件上传区域">
</div>
<input id="submit" type="submit" value="上传" style="display: none;margin-top: 10px;"/>
</form>
</body>
<script>
function add(){
var innerdiv = "<div>";
innerdiv += "<input type='file' name='uploadfile'>" +
"<input type='button' value='删除' οnclick='remove(this)'>";
innerdiv +="</div>";
$("#file").append(innerdiv);
$("#submit").css("display","block");
}
function remove(obj) {
$(obj).parent().remove();
if($("#file div").length ==0){
$("#submit").css("display","none");
}
}
</script>
</html>
在spring-mvc.xml配置文件中,配置多媒体解析器
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
在controller包下,创建处理文件上传下载请求的FileUploadController类
@Controller
/**
* 文件上传控制器类
*/
public class FileUploadController{
/**
* 处理文件上传请求
* @param uploadfile 上传的文件数组
* @return 返回上传结果
*/
@RequestMapping(value = "/upload",method = RequestMethod.POST)
@ResponseBody
private String uploadFile(MultipartFile[] uploadfile){
for (MultipartFile file : uploadfile) {
String filename = file.getOriginalFilename();
filename= UUID.randomUUID()+"_"+filename;
String dirPath="D:/file/";
File filePath=new File(dirPath);
if (!filePath.exists()){
filePath.mkdir();
}
try {
file.transferTo(new File(dirPath+filename));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return "success";
}
/**
* 处理文件下载请求
* @param request 请求对象
* @param filename 文件名
* @return 返回文件下载的响应实体
*/
@RequestMapping("/download")
public ResponseEntity<byte[]> download(HttpServletRequest request,String filename){
String path="D:/file/";
File file=new File(path+filename);
HttpHeaders headers=new HttpHeaders();
headers.setContentDispositionFormData("attachment",filename);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
try {
return new ResponseEntity<>(FileUtils.readFileToByteArray(file),headers, HttpStatus.OK);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 不同浏览器的版本兼容
* @param request
* @param filename
* @return
*/
private String getFilename(HttpServletRequest request,String filename) {
//判断是不是IE内核的关键字
String[] IEBrowerKeyWords = {"MSIE","Trident","Edge"};
String keywords = request.getHeader("User-Agent");
for (String keyWord : IEBrowerKeyWords) {
if(keywords.contains(keyWord)){ //判断是否为IE浏览器
try {
return URLEncoder.encode(filename,"UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
// 其他浏览器编码格式ISO-8859-1
try {
return new String(filename.getBytes("UTF-8"),"ISO-8859-1");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}