69 KiB
title | author | top | cover | toc | mathjax | summary | tags | categories | reprintPolicy | abbrlink | date | coverImg | img | password |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
SpringMVC | TianZD | true | true | true | false | SpringMVC框架学习笔记,粗略学了一下,没有参考价值 | [SpringMVC Java 学习笔记] | [java] | cc_by | ae0f95e0 | 2022-04-29 11:09:28 | <nil> | <nil> | <nil> |
[toc]
SpringMVC
ssm:Spring+SpringMVC+Mybatis
1、MVC
1.1、MVC概述
MVC是一种设计模式,用于应用程序的分层开发
MVC为:
- Model模型:提供要展示的数据,可以带有逻辑,包含数据和行为,通常将数据和行为分离
- 数据:DAO层
- 行为:Service层,服务层
- View视图:jsp,负责模型包含的数据的可视化,即模型的展示,代表用户界面
- Controller控制器:servlet,接收视图层的用户请求,委托给模型进行处理,将处理完毕返回的数据返回给视图,相当于一个调度员
最典型的MVC模式:JSP+servlet+javaBean
1.2、Model1时代
早期Web开发中,采用Model1模型,分为视图层和模型层。
- 视图层:负责展示模型、接收请求并调用业务逻辑方法;
- 模型层:提供要展示的数据;
- **优点:**架构简单;
- 缺点:模型层职责不单一,既展示数据,又接收请求并处理。不利于维护!
1.3、Model2时代
将项目分为 M-V-C 三层,即目前使用的 MVC 模式。
- 用户输入,发起请求;
- 控制层(Servlet)接收请求数据;
- 控制层(Servlet)调用模型层(Service)中相应的业务逻辑方法;
- 模型层(Service)处理业务,与数据库交互,将数据返回到控制层(Servlet);
- 控制层(Servlet)将数据返回给视图层,由视图层渲染页面;
- 将页面响应到前端。
优点:职责分明,利于维护
- Model
- 处理业务逻辑(Service 层);
- 保存数据的状态(DAO 层);
- Controller
- 获取请求;
- 调用业务逻辑;
- 响应页面(跳转指定页面);
- View
- 显示页面
2、Servlet
新建Maven无模板父工程
删除src,作为父工程
添加依赖
- SpringMVC
- JUnit
- Servlet、JSP
<!--导入依赖-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
新建Module,添加webapp支持
新建Module时选择普通Maven项目
新建完成后右键-添加框架支持,勾选web
编写代码
- 登陆页面
<html>
<head>
<title>Title</title>
</head>
<body>
<div id="app">
<form action="login" method="post">
<label>
用户名:<input type="text" name="username">
</label>
<input type="submit">
</form>
</div>
</body>
</html>
- servlet类
package com.tian.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取前端参数
String username = req.getParameter("username");
if ("admin".equals(username)) {
req.setAttribute("msg", "管理员");
} else {
req.setAttribute("msg", "普通用户");
}
// 2.调用业务层
// 3.视图转发或者重定向
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- servlet注册
<servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>com.tian.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
- 成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登陆成功:${msg}</h1>
</body>
</html>
3、SpringMVC入门
3.1、简介
Spring Web MVC,简称 SpringMVC。
- 是 Spring Framework 的一部分,是基于 Java Servlet API 实现 MVC 的轻量级 Web 框架。
- 围绕 DispatcherServlet 设计,将请求分发到不同的处理器。
特点:
- 轻量级,简洁灵活,简单易学;
- 高效 , 基于请求响应的MVC框架;
- 与 Spring 兼容性好,无缝结合;
- 约定大于配置;
- 功能强大:RESTful、数据验证、格式化、本地化、主题等;
3.2、中心控制器
- SpringMVC 围绕 DispatcherServlet 设计,DispatcherServlet 的作用是将请求分发到不同的处理器。
- Spring 2.5 + Java 5 以上版本,可以采用基于注解的 Controller 声明。
- 从继承图可以看出,DispatcherServlet 本质上就是 Servlet:
3.3、SpringMVC执行原理
- 前端控制器:即 DispatcherServlet
- 实际控制器:即实际处理请求的 Controller
- 模型层:即 Model 层,包括 Service 层和 DAO 层;
- 视图层:即 View 层。
原理图
- 用户:发起请求;
- 前端控制器:拦截请求,根据请求参数生成代理请求,将请求调度给对应的实际控制器;
- 控制器:处理请求,调用模型层中相应的业务逻辑方法;
- 模型层:Service 层处理业务,调用 DAO 层与数据库交互,创建并返回数据模型;
- 控制器:将 ModelAndView 返回给前端控制器;
- 前端控制器:传递 Model 到视图层;
- 视图层:渲染视图并返回前端控制器;
- 前端控制器:响应给用户。
底层源码关系
- 实线部分不用程序员做,程序员只需要做虚线部分
3.4、HelloSpringMVC-基于xml配置
- 新建Module,添加web支持
- 导入SpringMVC依赖
- 在web.xml中配置DispatchServlet(前端控制器,请求分发器)
这个是SpringMVC的核心
-
插入dispatcherServlet:alt+ins键,选择servlet,搜索dispatchservlet
-
关联配置文件
-
启动优先级
-
<url-pattern>
/
:匹配所有请求,不包括 JSP;/*
:匹配所有请求及 JSP- 如果填写了
/*
,Controller 处理业务后返回的 JSP页面会再次被拦截请求,会无限嵌套。
<!--配置DispatchServlet:请求分发器、前端控制器-->
<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:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动优先级:越小越优先启动 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--只能用/,不能用/*-->
<url-pattern>/</url-pattern>
</servlet-mapping>
注意:
- /:只匹配所有的请求,不会匹配jsp页面
- /*:匹配所有的请求,包括jsp页面
- 编写springmvc-servlet.xml
- 处理器映射
- 处理器适配器
- 内部资源视图解析器
- 后续:将 Controller 注册到 IOC 容器中:相当于原来在 web.xml 中注册 Servlet
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 处理器映射 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 内部资源视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀,必须有斜杠 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 相当于原来在 web.xml 中注册 Servlet -->
<bean id="/hello" class="com.tian.controller.HelloController"/>
</beans>
- Controller
HelloController.class
最后返回给视图解析器,进行hello名字拼接,拼接成/WEB-INF/jsp/hello.jsp
然后跳转得到hello.jsp页面
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
// 业务代码
String result = "HelloSpringMVC";
modelAndView.addObject("msg", result);
//视图跳转
modelAndView.setViewName("hello");
return modelAndView;
}
}
- 跳转页面hello.jsp
<%--
Created by IntelliJ IDEA.
User: 12038
Date: 2021/8/15
Time: 16:17
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>
<h1>${msg}</h1>
</body>
</html>
过程分析
- 用户:发起请求;
- web.xml中:
- 前端控制器:拦截请求,根据请求参数生成代理请求,根据关联的配置文件springmvc-servlet.xml,将请求调度给对应的实际控制器
- 处理器映射器映射到hello
- 处理器适配器找到com.tian.controller.HelloController进行处理
- 控制器:处理请求,调用模型层中相应的业务逻辑方法HelloController.class;
- 模型层:Service 层处理业务,调用 DAO 层与数据库交互,创建并返回数据模型ModeAndView对象;
- 控制器:将 ModelAndView 返回给前端控制器;
- 前端控制器:传递 Model 到视图层;
- 视图层:渲染视图并返回前端控制器;
- 前端控制器:响应给用户。
易错
- 注册 *DispatcherServlet* 的 *url-pattern* 不能是
/*
; - springmvc-servlet.xml 中:
- 视图解析器的前缀的结尾必须有
/
:如/WEB-INF/jsp/
; - Controller 的注册 Bean 必须以
/
开头,如/hello
;
- 视图解析器的前缀的结尾必须有
- 以上操作均无误,仍报 404 或 500:
- 创建的 web 模板的Maven项目,理论上不会报错;
- 创建不带模板的 Maven 项目 + 手动添加 web 框架支持,可能会报错;
- 需要手动在项目结构中添加 lib 目录:
4、基于注解开发SpringMVC
在 SpringMVC 实际开发中,我们通常都会采用注解开发。
- 新建Module,添加web支持
- 导入SpringMVC依赖:Spring框架核心库、SpringMVC、servlet、JSTL等
- 在web.xml中配置DispatchServlet(前端控制器,请求分发器)
这个是SpringMVC的核心
-
关联配置文件
-
启动优先级
-
<url-pattern>
/
:匹配所有请求,不包括 JSP;/*
:匹配所有请求及 JSP- 如果填写了
/*
,Controller 处理业务后返回的 JSP页面会再次被拦截请求,会无限嵌套。
alt+ins键,选择servlet,搜索dispatchservlet
<!--配置DispatchServlet:请求分发器、前端控制器-->
<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:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动优先级:越小越优先启动 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--只能用/,不能用/*-->
<url-pattern>/</url-pattern>
</servlet-mapping>
注意:
- /:只匹配所有的请求,不会匹配jsp页面
- /*:匹配所有的请求,包括jsp页面
- springmvc-servlet.xml
与基于配置的SpringMVC
的配置相比:
- 添加注解支持,不再需要手动注册 Controller
- 内部资源视图解析器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 扫描包 -->
<context:component-scan base-package="com.tian.controller"/>
<!-- 过滤静态资源 -->
<mvc:default-servlet-handler/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- Controller
HelloController.class
最后返回给视图解析器,进行hello名字拼接,拼接成/WEB-INF/jsp/hello.jsp
然后跳转得到hello.jsp页面
@Controller
@RequestMapping("/hello") //配置总地址,可以有可无
public class HelloController {
/**
* 使用 @RequestMapping注解来处理映射关系,后接请求路径(如"/hello")
* 方式一:使用 ModelAndView来封装模型和视图,返回 ModelAndView
*/
@RequestMapping("/hello")
public ModelAndView hello(ModelAndView mv) {
// 可以调用Service层方法,获取模型对象
// 添加数据模型
mv.addObject("msg", "Hello Annotation:ModelAndView");
// 封装要渲染、跳转的视图
mv.setViewName("hello");
return mv;
}
/**
* 使用 @RequestMapping注解来处理映射关系,后接请求路径(如"/hello")
* 方式二:使用 Model来封装模型,返回 ViewName
*/
@RequestMapping("/hello1") //真实访问地址:localhost:8080/项目名/hello/hello1
public String hello(Model model) {
// 可以调用Service层方法,获取模型对象
// 添加数据模型
model.addAttribute("msg", "Hello Annotation:Model");
// 返回视图
return "hello"; //会被视图解析器处理
}
}
- 跳转页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>
总结
- 新建项目
- 导入jar包
- 编写web.xml,注册DispatchServlet
- 编写Springmvc配置文件
- 创建对应的控制类controller
- 完善前端视图和controller之间的对应
- 测试运行
后续开发只需要进行5和6
注解和配置对比
基于配置 VS 基于注解
-
web.xml 完全相同;
-
springmvc-servlet.xml
-
基于配置:处理器映射、处理器适配器、内部资源视图解析器;
- 处理器映射:默认配置,不需要显式配置;
- 处理器适配器:默认配置,不需要显式配置;
- 内部资源视图解析器:必须显式配置;
- 将 Controller 注册到 IOC 容器中:相当于原来在 web.xml 中注册 Servlet。
-
基于注解:Spring 注解开发
- 扫描包:扫描指定包下的 Bean ,被扫描的 Bean 中包含的类级别的注解才会生效,Bean 才会被注册到容器中;
- 过滤静态资源:如 CSS、JS、HTML、MP3、MP4...
- 注解驱动:在 Spring 中一般使用 @RequestMapping 注解来处理映射关系,使用该注解需要注册
处理器映射
和处理器适配器
,annotation-driven 自动注入以上两个实例; - 内部资源视图解析器。
-
-
Controller
- 基于配置:
- 继承 Controller 接口,重写 handleRequest 方法;
- 通过 ModelAndView 封装模型和视图,返回 ModelAndView
- 需要在 springmvc-servlet.xml 中注册 Bean 并映射请求路径;
- 如果有多个请求,通过获取参数判断方法名实现 Controller 复用,或编写多个 Controller。
- 基于注解
- 使用 @Controller 注解来注册 Bean,自定义方法;
- 通过 ModelAndView 封装模型和视图,返回 ModelAndView。或通过 Model 封装模型,返回视图名;
- 在方法上使用
@RequestMapping("/xxx")
来映射请求路径 - 如果有多个请求,自定义编写多个方法。
- 基于配置:
5、控制器Controller
- 控制器提供访问应用程序的行为,通过接口定义或注解两种方式实现
- 负责解析用户的请求并将其转换为一个模型
5.1、实现Controller接口
缺点是一个控制器中只有一个方法,如果要有多个方法需要定义多个controller,比较麻烦
//实现接口的类能获得控制器功能
public interface Controller {
@Nullable
//处理请求并返回一个模型与视图对象
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
编写实现类
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
// 业务代码
String result = "HelloSpringMVC";
modelAndView.addObject("msg", result);
//视图跳转
modelAndView.setViewName("hello");
return modelAndView;
}
}
配置文件
- 视图解析器
- 注册servlet
配置文件中处理器映射和处理器适配器部分可以去掉,spring会自动加
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 处理器映射 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 内部资源视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀,必须有斜杠 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 相当于原来在 web.xml 中注册 Servlet -->
<bean id="/hello" class="com.tian.controller.HelloController"/>
</beans>
5.2、使用注解@Controller
配置文件
- 添加包扫描
- 视图解析器
<!-- 扫描包 -->
<context:component-scan base-package="com.tian.controller"/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
增加一个controller类,使用注解i实现
@Controller
@RequestMapping("/hello") //配置总地址,可以有可无
public class HelloController {
/**
* 使用 @RequestMapping注解来处理映射关系,后接请求路径(如"/hello")
* 方式一:使用 ModelAndView来封装模型和视图,返回 ModelAndView
*/
@RequestMapping("/hello")
public ModelAndView hello(ModelAndView mv) {
// 可以调用Service层方法,获取模型对象
// 添加数据模型
mv.addObject("msg", "Hello Annotation:ModelAndView");
// 封装要渲染、跳转的视图
mv.setViewName("hello");
return mv;
}
/**
* 使用 @RequestMapping注解来处理映射关系,后接请求路径(如"/hello")
* 方式二:使用 Model来封装模型,返回 ViewName
*/
@RequestMapping("/hello1") //真实访问地址:localhost:8080/项目名/hello/hello1
public String hello(Model model) {
// 可以调用Service层方法,获取模型对象
// 添加数据模型
model.addAttribute("msg", "Hello Annotation:Model");
// 返回视图
return "hello"; //会被视图解析器处理
}
}
6、RestFul风格
6.1、概念
RESTful 是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。
基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
6.2、功能
- 资源:互联网所有的事物都可以被抽象为资源
- 资源操作:使用不同方法操作资源,POST(添加)、DELETE(删除)、PUT(更新)、GET(查询);
传统方法操作资源:通过不同的请求参数来实现不同的功能,请求地址不同。
- 添加:
http://127.0.0.1/item/insertItem.action
- 删除:
http://127.0.0.1/item/deleteItem.action?id=1
- 更新:
http://127.0.0.1/item/updateItem.action
- 查询:
http://127.0.0.1/item/getItem.action?id=1
RESTful风格操作资源:通过不同的请求方式来实现不同的功能,请求地址相同。
- 添加:
http://127.0.0.1/item
- 删除:
http://127.0.0.1/item/1
- 更新:
http://127.0.0.1/item
- 查询:
http://127.0.0.1/item/1
6.3、实现
使用 @PathVariable 注解,将参数值绑定到 URI 模板变量上。
@Controller
public class RestfulController {
@RequestMapping("/addition/{a}/{b}")
public String addition(@PathVariable("a") int a, @PathVariable("b") int b, Model model) {
int result = a + b;
model.addAttribute("result", "结果:" + result);
return "test";
}
}
使用 method 属性约束请求的类型,指定类型的请求才可访问。
@RequestMapping(value = "/multiplication/{a}/{b}", method = RequestMethod.POST)
public String multiplication(@PathVariable("a") int a, @PathVariable("b") int b, Model model) {
int result = a * b;
model.addAttribute("result", "结果:" + result);
return "test";
}
也可以使用 @RequestMapping 的衍生注解:@PostMapping
@PostMapping("/multiplication/{a}/{b}")
public String multiplication(@PathVariable("a") int a, @PathVariable("b") int b, Model model) {
int result = a * b;
model.addAttribute("result", "结果:" + result);
return "test";
}
6.4、请求类型
@RequestMapping 注解能够处理 HTTP 请求的方法,包括 GET, PUT, POST, DELETE , PATCH 等等。
- 所有的地址栏请求默认都会是 HTTP GET 类型的。
- *@RequestMapping* 注解有以下几个变体,是组合注解
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
- 以上注解相当于:@RequestMapping(method =RequestMethod.XXX)
6.5、优点
- 使路径变得更加简洁:
- 传统方式:
http://localhost:8080/springmvc_03/multiplication?a=7&b=3
; - RESTful:
http://localhost:8080/springmvc_03/multiplication/7/3
;
- 传统方式:
- 获得参数更加方便,框架会自动进行类型转换;
- 使用 @PathVariable 注解来绑定 URI 模板变量,路径变量的类型可以约束请求参数。如果类型不一致,则访问不到对应的请求方法;
7、转发和重定向
7.1、SpringMVC使用视图解析器
通过 ModelAndView 对象,根据 view 名称和视图解析器,映射到指定页面。
- 使用视图解析器的方式属于请求转发,地址栏 URL 不会发生改变。
- 实际页面 URL :(视图解析器前缀) + viewName +(视图解析器后缀)
视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
Controller
z@RequestMapping("/h1")
public String hello(Model model) {
model.addAttribute("msg", "Hello Annotation:Model");
// viewName
return "hello";
}
**结果:**访问http://localhost:8080/springmvc_03/h1
,地址栏 URL 不变。
请求转发到http://localhost:8080/springmvc_03/WEB-INF/jsp/hello.jsp
。
7.2、SpringMVC不使用视图解析器
请求转发
forward:
表示请求转发,后接转发路径。
可以省略,即直接返回 /index.jsp
,默认为请求转发。
@RequestMapping("/mvc/t1")
public String test1(Model model){
model.addAttribute("msg","SpringMvcController请求转发");
return "forward:/index.jsp";
}
@RequestMapping("/mvc/t2")
public String test2(Model model){
model.addAttribute("msg","SpringMvcController请求转发");
return "/index.jsp";
}
**结果:**访问http://localhost:8080/springmvc_03/mvc/t1
,地址栏 URL 不变。
请求转发到http://localhost:8080/springmvc_03/index.jsp
。
请求重定向
forward:
表示请求重定向。
@RequestMapping("/mvc/t3")
public String test3() {
return "redirect:/index.jsp";
}
**结果:**访问http://localhost:8080/springmvc_03/mvc/t3
,地址栏 URL 改变。
重定向到http://localhost:8080/springmvc_03/index.jsp
。
7.3、使用ServletAPI
通过HttpServletResponse进行输出、重定向、转发
原生 Servlet API 实现,不需要视图解析器。
- HttpServletRequest:请求转发
- HttpServletResponse:请求重定向、页面打印、。
请求转发
Controller
@RequestMapping("/api/t1")
public void test1(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 携带参数
req.setAttribute("result", "ServletApiController:请求转发");
// 请求转发
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
}
**结果:**访问http://localhost:8080/springmvc_03/api/t1
,地址栏 URL 不变。
请求转发到http://localhost:8080/springmvc_03/WEB-INF/jsp/test.jsp
。
请求重定向
Controller
@RequestMapping("/api/t2")
public void test2(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 请求重定向
resp.sendRedirect(req.getContextPath() + "/index.jsp");
}
**结果:**访问http://localhost:8080/springmvc_03/api/t2
,地址栏 URL 改变。
请求重定向到http://localhost:8080/springmvc_03/index.jsp
。
页面打印
Controller
@RequestMapping("/api/t1")
public void test1(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().println("ServletApiController被访问了");
}
**结果:**页面打印内容。
8、数据接收和回显
8.1、处理提交数据
获取的请求参数,可能是一个字段(域),也可能是一个对象。
- 提交的域名称和处理方法的参数名是否一致,决定了参数的获取方式;
- 提交的域名称和实体类的属性名是否一致,决定了参数的获取方式。
提交的域名称和处理方法的参数名一致
提交的域名称和处理方法的参数名一致,会将域名称自动映射到处理方法的参数。
@RequestMapping("/user/t1")
public String test1(Model model, String name) {
// 1、获取请求参数
System.out.println("请求参数:" + name);
// 2、数据回显
model.addAttribute("msg", name);
// 3、设置视图
return "test";
}
提交数据:
http://localhost:8080/springmvc_03/user/t1?name=tian
提交的域名称和处理方法的参数名不一致
使用 @RequestParam 注解,设置提交的域名称。
@RequestMapping("/user/t2")
public String test2(Model model, @RequestParam("username") String name) {
// 1、获取请求参数
System.out.println("请求参数:" + name);
// 2、数据回显
model.addAttribute("msg", name);
// 3、设置视图
return "test";
}
提交数据:
http://localhost:8080/springmvc_03/user/t1?username=tian
提交对象
处理方法使用实体类接收请求参数,提交的域名称和实体类的属性名必须一致,才能自动映射。
- 实体类:User
private int id;
private String name;
private int age;
- Controller
@PostMapping("/user/t3")
public String test3(Model model, User user) {
// 获取请求参数
System.out.println("请求参数:" + user);
// 设置视图
return "test";
}
- 表单:用于提交参数
域名称和与实体类的属性名必须一致!
<form action="user/t3" method="post">
<label> ID:<input type="text" name="id"> </label><br>
<label> 姓名:<input type="text" name="name"> </label><br>
<label> 年龄:<input type="text" name="age"> </label><br>
<input type="submit">
</form>
8.2、数据显示到前端
将数据显示到前端,有三种方式:
- ModelAndView:既可以存储数据模型,又可以设置返回视图;
- Model:用于存储数据模型;
- ModelMap:继承 LinkedMap,具有 LinkedMap 的方法和特性。
@RequestMapping("/data/t1")
public ModelAndView test1() {
ModelAndView mv = new ModelAndView();
// 数据模型
mv.addObject("msg", "DataController ModelAndView");
// 设置视图
mv.setViewName("test");
return mv;
}
@RequestMapping("/data/t2")
public String test2(Model model){
// 数据模型
model.addAttribute("msg","DataController Model");
// 返回视图
return "test";
}
@RequestMapping("/data/t3")
public String test3(ModelMap modelMap){
// 数据模型
modelMap.addAttribute("msg","DataController ModelMap");
// 返回视图
return "test";
}
9、乱码问题
在 web 开发中经常会遇到页面乱码问题,编写一个 Filter 过滤器解决乱码问题。
CharacterEncodingFilter
public class CharacterEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
chain.doFilter(req, resp);
}
@Override
public void destroy() {}
}
web.xml:注册Filter
注意:
/
:匹配所有的请求,不包括 JSP/*
:匹配所有的请求和 JSP
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>indi.tian.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
10、JSON
JSON(JavaScript Object Notation):JavaScript 对象标记。
前后端分离时代:
- 后端部署后端,提供接口,提供数据
- 前端独立部署,负责渲染后端的数据
json用于数据交换
10.1、简介
什么是 JSON
- 是轻量级的文本数据交换格式;
- 独立于编程语言;
- 使用 JavaScript 语法来描述数据对象(使用文本表示 JS 对象);
- 具有自我描述性,易于理解。
10.2、JSON vs XML
JSON 和 XML 都用于接收 web 服务端的数据,通常使用字符串。
对比
相同点
- 具有自我描述性;
- 具有层级结构;
- 可以被大多数编程语言使用;
- 可通过 JavaScript 解析;
- 可使用 AJAX 传输;
不同点
- JSON 不需要结束标签;
- JSON 更加简短;
- JSON 读写速度更快;
- JSON 可以使用数组;
JSON优于XML
- JSON 比 XML 更容易解析;
- JSON 可以使用现有的 JavaScript 对象解析;
- 对于AJAX来说,JSON 比XML数据加载更快、更简单:
- 使用 XML
- 获取 XML 文档;
- 使用 XML DOM 来循环遍历文档;
- 将数据解析并存储在变量中;
- 使用 JSON
- 获取 JSON 字符串;
- 解析 JSON 字符串;
- 使用 XML
10.3、语法
JSON 语法是 JavaScript 语法的子集。
JSON语法规则
- 数据用 K-V 键值对表示;
- 数据之间用逗号分隔;
- 大括号
{}
保存对象; - 中括号
[]
保存数组。
{
"name" : "tian",
"age" : "17",
"hobby": ["java","music","basketball"]
}
JSON数据类型
JSON 值可以是:
- 数字:整数或浮点数;
- 字符串:写在双引号中;
- 逻辑值:true 或 false;
- 对象:写在大括号
{}
中; - 数组:写在中括号
[]
中; - 空值:null
JSON对象
用大括号{}
表示 JSON 对象,JSON 对象可以包含多个 K-V 键值对。
- Key 必须是字符串,Value 可以是合法的 JSON 数据类型(数字, 字符串, 布尔值, 对象, 数组或 null);
- Key 和 Value 中使用冒号
:
分割; - 多个 K-V 键值对之间使用逗号
,
分割。
访问 JSON 对象属性
通过点号.
或中括号[]
访问 JSON 对象,可以进行以下操作;
- 获取、修改属性值;
- 使用 delete 关键字删除属性。
var person = {
"name" : "tian",
"age" : "17",
"hobby": ["java","music","basketball"]
}
var obj1 = person.name;
var obj2 = person["name"];
person.age = 10;
person["age"] = 10;
delete person.hobby;
delete person[hobby];
JSON数组
用中括号[]
表示 JSON 数组。
- 数组值必须是合法的 JSON 数据类型(数字, 字符串, 布尔值, 对象, 数组或 null);
- 多个数组值之间使用逗号
,
分割。
访问 JSON 数组值
通过索引值(下标从0开始)访问数组,可以进行以下操作;
- 获取、修改属性值;
- 使用 delete 关键字删除属性。
var person = {
"name" : "tian",
"age" : "17",
"hobby": ["java","music","basketball"]
}
var obj = person.hobby[0];
person.hobby[0] = "spring";
delete person.hobby[0];
JSON文件
- JSON 文件的 文件 类型:
.json
- JSON 文本的 MIME 类型:
application/json
10.4、JSON和JS对象转换
JSON 通常用于与服务端交换数据。
- 从服务器接收数据:将 JSON 字符串解析为 JavaScript 对象;
- 向服务器发送数据:将 JavaScript 对象 转换为 JSON 字符串。
parse
使用 JSON.parse() 方法将 JSON 字符串转换为 JavaScript 对象:即字符串 → 对象。
JSON解析实例
-
假设从服务器接收到以下数据
{ "name":"tian", "age":"17" }
-
使用 JSON.parse() 方法解析数据,将其转化为 JavaScript 对象
var obj = JSON.parse('{ "name":"tian", "age":"17" }');
stringify
使用 JSON.stringify() 方法将 JavaScript 对象转换为 JSON 字符串:即对象→ 字符串。
JSON对象转换实例
-
假设要向服务器发送以下数据
var obj = { "name":"tian", "age":"17" }
-
使用 JSON.stringify() 方法将 JavaScript 对象转换为 JSON 字符串
var myJson = JSON.stringify(obj);
10.5、测试
-
编写一个 JS 对象
let person = { "name": "tian", "age": 17, "hobby": ["java", "music", "basketball"] } console.log(person)
-
将 JS 对象转换为 JSON 字符串
let myJson = JSON.stringify(person) console.log(myJson)
-
将 JSON 字符串转换为 JS 对象
let obj = JSON.parse(myJson) console.log(obj)
11、Java中使用JSON
Java 中常用的 JSON 类库: jackson、fastjson、Gson、 JSON-lib 等。
11.1、Jackson
环境搭建
-
新建 module,添加 web 框架支持:在项目结构中手动添加 lib 目录;
-
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.4</version> </dependency>
-
web.xml:注册 DispatcherServlet
- 关联配置文件;
- 启动优先级;
-
springmvc-servlet.xml:添加注解支持、内部资源视图解析器。
-
实体类:User
private int id; private String name; private int age;
@ResponseBody
@ResponseBody 注解的作用:将处理方法返回的对象写到页面中。
- 将 Controller 方法返回的对象,转换为指定的格式(通常是 JSON 或 XML);
- 写入到 response 对象的 body 区。
举例
@Controller
public class UserController {
@ResponseBody
@RequestMapping("/user/t1")
public String test1() {
// 创建User
User user = new User(7, "tian", 17);
// 返回User字符串
return user.toString();
}
}
结果
@RestController
@RestController 相当于 @ResponseBody 和 @Controller 合在一起的作用。
- Controller 中的所有方法都会返回 return 中的内容;
- 视图解析器 InternalResourceViewResolver 不起作用,无法返回视图( JSP 或 HTML)。
举例
@RestController
public class UserRestController {
@RequestMapping("/ur/t1")
public String test1() {
// 创建User
User user = new User(7, "tian", 17);
// 返回User字符串
return user.toString();
}
}
结果
ObjectMapper
要使用 Jackson 处理 JSON 数据,需要一个 ObjectMapper 对象。
ObjectMapper mapper = new ObjectMapper();
Object obj = new Object();
// 转换为字符串
String objJson = mapper.writeValueAsString(obj);
// 转换为字节流
byte[] objBytes = mapper.writeValueAsBytes(obj);
// 转换为文件
mapper.writeValue(new File("obj.json"), user);
// 读取字符串解析对象
User obj1 = mapper.readValue(objJson, Object.class);
// 读取字节流解析对象
User obj2 = mapper.readValue(objBytes, Object.class);
// 读取文件解析对象
User obj3 = mapper.readValue(new File("obj.json"), Object.class);
- 除了对 Java 类进行转换,还可以对集合和日期类进行转换。
解决乱码问题
乱码问题有两种解决方案。
- produces属性
@RequestMapping(value = "/user/j1", produces = "application/json;charset=utf-8")
- 在处理方法的 @RequestMapping 注解使用 produces 属性;
- 如果多个方法要使用,则需要在每个处理方法上写。
-
配置文件(推荐)
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="failOnEmptyBeans" value="false"/> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
- 在配置文件中统一配置;
- 统一配置后,就可以在全局使用。
测试
使用 @ResponseBody 和 @RestController 可以达到一样的效果,以下测试采用第一种注解。
Java类
- j1:将 User 对象转换为 JSON 字符串。
@ResponseBody
@RequestMapping("/user/j1")
public String json1() throws JsonProcessingException {
// 创建ObjectMapper
ObjectMapper mapper = new ObjectMapper();
// 创建User
User user = new User(7,"路人甲",10);
// 返回JSON字符串
return mapper.writeValueAsString(user);
}
集合
- j2:将 List 集合转换为 JSON 字符串;
- j3:将 Map 集合转换为 JSON 字符串。
@ResponseBody
@RequestMapping("/user/j2")
public String json2() throws JsonProcessingException {
// 创建ObjectMapper
ObjectMapper mapper = new ObjectMapper();
// 创建List集合
List<User> list = new ArrayList<>();
User user1 = new User(3, "张三", 20);
User user2 = new User(4, "李四", 17);
User user3 = new User(5, "王五", 30);
list.add(user1);
list.add(user2);
list.add(user3);
return mapper.writeValueAsString(list);
}
@ResponseBody
@RequestMapping("/user/j3")
public String json3() throws JsonProcessingException {
// 创建ObjectMapper
ObjectMapper mapper = new ObjectMapper();
// 创建List集合
HashMap<String, Object> map = new HashMap<>();
map.put("name", "tian");
map.put("age", 17);
return mapper.writeValueAsString(map);
}
日期
- j4:将日期对象转换为时间戳;
- j5:可以自定义日期格式。
@ResponseBody
@RequestMapping("/user/j4")
public String json4() throws JsonProcessingException {
// 创建ObjectMapper
ObjectMapper mapper = new ObjectMapper();
// 创建Date
Date date = new Date();
// 返回JSON
return mapper.writeValueAsString(date);
}
@ResponseBody
@RequestMapping("/user/j5")
public String json5() throws JsonProcessingException {
// 创建ObjectMapper
ObjectMapper mapper = new ObjectMapper();
// 自定义日期格式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(sdf);
// 创建Date
Date date = new Date();
// 返回JSON
return mapper.writeValueAsString(date);
}
j1-j5测试结果
工具类
Jackson 的使用步骤如下:
- 创建 ObjectMapper 对象;
- 创建对象
- 对象类型:pojo、日期类、集合;
- 如果是日期类,可以自定义日期格式;
- 将对象转换为 JSON 字符串。
将以上重复的代码抽取,封装成工具类。
package com.tian.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JsonUtils {
/**
* ObjectMapper
*/
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 对象转换后的JSON字符串
*/
private static String OBJ_JSON;
/**
* 获取pojo、集合类的JSON字符串
*/
public static String getJson(Object obj) {
// 转换为JSON字符串
try {
OBJ_JSON = MAPPER.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return OBJ_JSON;
}
/**
* 获取默认日期格式的JSON字符串
*/
public static String getDateJson(Date date) {
return getDateJson(date, "yyyy-MM-dd hh-mm-ss");
}
/**
* 获取指定日期格式的JSON字符串
*/
public static String getDateJson(Date date, String dateFormat) {
// 自定义日期格式
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
MAPPER.setDateFormat(sdf);
// 转换为JSON字符串
try {
OBJ_JSON = MAPPER.writeValueAsString(date);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return OBJ_JSON;
}
}
11.2、fastjson
fastjson 是阿里开发的 Java 类库,可以方便地实现 Java 对象和 JSON 格式之间的转换。
环境搭建
-
新建 module,添加 web 框架支持:在项目结构中手动添加 lib 目录;
-
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.78</version> </dependency>
-
web.xml:注册 DispatcherServlet
- 关联配置文件;
- 启动优先级;
-
springmvc-servlet.xml:添加注解支持、内部资源视图解析器。
-
实体类:User
private int id; private String name; private int age;
JSON
JSON 类似 Jackson 中的 ObjectMapper,用于处理 JSON 数据。
不同的是,fastJson 无需 new 对象,提供静态方法,类似 Jackson 中的工具类。
Object obj = new Object();
// 转换为字符串
String objJson = JSON.toJSONString(obj);
// 转换为字节流
byte[] objBytes = JSON.toJSONBytes(obj);
// 读取字符串解析对象
Object obj1 = JSON.parse(objJson);
// 读取字节流解析对象
Object obj2 = JSON.parse(objBytes);
测试
Java类
- j1:将 User 对象转换为 JSON 字符串。
@ResponseBody
@RequestMapping("/uf/j1")
public String json1() {
User user = new User(7, "路人甲", 10);
return JSON.toJSONString(user);
}
集合
- j2:将 List 集合转换为 JSON 字符串;
- j3:将 Map 集合转换为 JSON 字符串。
@ResponseBody
@RequestMapping("/uf/j2")
public String json2() {
// 创建List集合
List<User> list = new ArrayList<>();
User user1 = new User(3, "张三", 20);
User user2 = new User(4, "李四", 17);
User user3 = new User(5, "王五", 30);
list.add(user1);
list.add(user2);
list.add(user3);
return JSON.toJSONString(list);
}
@ResponseBody
@RequestMapping("/uf/j3")
public String json3() {
// 创建List集合
HashMap<String, Object> map = new HashMap<>();
map.put("name", "tian");
map.put("age", 17);
return JSON.toJSONString(map);
}
日期
- j4:将日期对象转换为时间戳;
- j5:可以自定义日期格式,fastjson 提供了默认日期格式。
@ResponseBody
@RequestMapping("/uf/j4")
public String json4() {
Date date = new Date();
return JSON.toJSONString(date);
}
@ResponseBody
@RequestMapping("/uf/j5")
public String json5() {
Date date = new Date();
return JSON.toJSONStringWithDateFormat(date, JSON.DEFFAULT_DATE_FORMAT);
}
j1-j5测试结果
如果一切正常,但是报 500。检查项目结构的 lib 目录中是否有相关 jar 包。
12、SSM整合SpringMVC
见SSM整合笔记
13、Ajax
13.1、概述
-
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
-
AJAX 不是新的编程语言,而是一种使用现有标准的新方法
-
AJAX 是一种用于创建快速动态网页的技术
-
AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术
AJAX一个前后台配合的技术,它可以让javascript发送http请求,与后台通信,获取数据和信息。ajax技术的原理是实例化xmlhttp对象,使用此对象与后台通信。
Jquery将它封装成了一个函数**$.ajax()**,我们可以直接用这个函数来执行ajax请求。
13.2、$.ajax()
$.ajax()
重要:
- url
- data
- success
$.ajax({
url:"发送请求(提交或读取数据)的地址",
dataType:"预期服务器返回数据的类型",
type:"请求方式",
async:"true/false",
data:{发送到/读取后台(服务器)的数据},
success:function(data){请求成功时执行},
error:function(){请求失败时执行}
});
$(function(){
//请求参数
var list = {};
//ajax
$.ajax({
//请求方式
type : "POST",
//请求的媒体类型
contentType: "application/json;charset=UTF-8",
//请求地址
url : "http://127.0.0.1/admin/list/",
//数据,json字符串
data : JSON.stringify(list),
//请求成功
success : function(result) {
console.log(result);
},
//请求失败,包含具体的错误信息
error : function(e){
console.log(e.status);
console.log(e.responseText);
}
});
});
实例
登陆页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%-- <script src="jquery-3.5.1.js"></script>--%>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$(function () {
$("#button1").click(function (){
$.get({
url: "${pageContext.request.contextPath}/a1",
data: {"name":$("#username").val()},
success: function (data) {
if ((data.toString() === 'ok')) {
$("#span1").css("color","green");
}
$("#span1").html(data);
}
})
})
});
</script>
</head>
<body>
用户名:
<input type="text" id="username">
<button id="button1">校验</button>
<span id="span1"></span>
</body>
</html>
Controller
@RestController
public class AjaxController {
@RequestMapping("/a1")
public String login(String name) {
String msg = "";
if ("admin".equals(name)) {
msg = "ok";
} else {
msg = "用户名错误";
}
return msg;
}
}
14、拦截器
概述
在开发一个网站时可能有这样的需求:某些页面只希望几个特定的用户浏览。对于这样的访问权限控制,需要用到拦截器
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理
过滤器、拦截器
过滤器是 servlet 规范中的一部分,任何 Java Web 工程都可以使用,就算在SpringMVC中也可以使用不受限制。在 web.xml 中使用 url-pattern 配置了拦截地址之后,会对所有的访问所有改地址的资源进行拦截。
拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。并且拦截器只会拦截访问的控制器方法,不会拦截其他资源,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的。
使用拦截器
实现HandlerInterceptor 接口
/**
* springmvc中的拦截器对象
* 1.编写代码:实现拦截器接口 HandlerInterceptor
* 2.配置拦截器
*/
public class Demo1Interceptor implements HandlerInterceptor {
/**
* preHandle方法,预处理
* 作用:在请求进入指定Controller方法之前,执行的方法(对请求进行验证)
* @param request 请求对象
* @param response 响应对象
* @param handler 请求将要执行的Controller对象
* @return 布尔类型:true表示请求放行,继续向后执行(有可能进入下一个拦截器,也有可能进入Controller)
* false表示拦截请求,请求不能继续向后执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Demo1Interceptor拦截器中的preHandle方法执行了...");
return true;
}
/**
* postHandle 后处理
* @param request 请求对象
* @param response 响应对象
* @param handler 正在执行的Controller对象
* @param modelAndView 模型和视图数据,Controller方法执行完成之后的返回值
*
* 注意:此方法必须在请求进入Controller之后才会执行,如果没有进入Controller是不会执行的
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Demo1Interceptor拦截器中的postHandle方法执行了...");
}
/**
* afterCompletion 请求处理完成之后
* @param request 请求对象
* @param response 响应对象
* @param handler 已经执行过的Controller对象
* @param ex Controller方法抛出的异常对象
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Demo1Interceptor拦截器中的afterCompletion方法执行了...");
}
}
方法解析:
-
preHandle方法:在请求进入Controller方法之前调用此方法,起到拦截的作用
- 如果preHandle方法返回值为true,表示放行(允许进入Controller方法)
- 如果preHandle方法返回值为false,表示拦截(不允许进入Controller方法)
-
postHandle方法
请求进入Controller方法之后,但未结束之前,调用此方法
可在返回的模型数据进行处理加工,比如加入公用信息以便页面显示
-
afterCompletion方法
- 请求离开Controller方法之后,调用此方法
- 可获取异常信息,记录日志,资源清理等
拦截器配置
web.xml
<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 一个拦截器 -->
<mvc:interceptor>
<!-- 配置此拦截器所拦截的路径
拦截所有请求:/** 表示所有进入springmvc的请求
-->
<mvc:mapping path="/**"/>
<!-- 注册拦截器对象 -->
<bean class="com.newcapec.interceptor.Demo1Interceptor"/>
</mvc:interceptor>
</mvc:interceptors>
应用:用户登陆
- controller
@Controller
@RequestMapping("/auth")
public class LoginController {
@RequestMapping("/login")
public String login(String username, String password, HttpSession session, Model model){
//模拟查询:根据用户名和密码查询数据
if("admin".equals(username) && "123".equals(password)) {
//比对成功,用户登录
//将用户信息放入session域对象
session.setAttribute("loginUser", username);
return "index";
}
model.addAttribute("message", "username or password is invalid");
return "login";
}
@RequestMapping("/logout")
public String logout(HttpSession session){
session.removeAttribute("loginUser");
session.invalidate();
return "login";
}
}
- 拦截器
public class LoginInterceptor implements HandlerInterceptor {
/**
* 身份认证:
* 过滤器:
* 1.从session域中获取用户的登录信息
* 2.判断用户信息是否为空
* 3.不为空(表示已登录)放行(doFilter方法)
* 4.为空(表示未登录)
* 5.判断当前请求是否为登录请求或无需登录可执行请求(身份认证白名单)
* 6.如果为白名单请求,放行
* 7.如果不是,拦截,跳转login,信息提示...
*
* 拦截器:
* 1.从session域中获取用户的登录信息
* 2.判断用户信息是否为空
* 3.不为空(表示已登录)放行(返回true)
* 4.为空(表示未登录),拦截,跳转login,信息提示...
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("身份认证拦截器执行.....");
HttpSession session = request.getSession();
Object userInfo = session.getAttribute("loginUser");
if(userInfo == null){
request.setAttribute("message", "you are not login.");
request.getRequestDispatcher("/views/login.jsp").forward(request, response);
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
- 配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 配置拦截器不拦截路径,可使用通配符 -->
<mvc:exclude-mapping path="/auth/login"/>
<!--<mvc:exclude-mapping path="/demo/**"/>-->
<bean class="com.newcapec.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
</head>
<body>
<div style="text-align: center">
<h1>用户登录</h1>
<div style="color: red;">${message}</div>
<form action="auth/login" method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密 码:<input type="text" name="password"></p>
<p><button>登录</button></p>
</form>
</div>
</body>
</html>
15、文件上传和下载
15.1、文件上传
在 Spring MVC 中MultipartResolver接口用于处理上传请求,将上传请求包装成可以直接获取文件的数据,从而方便操作。
MultpartiResolver 接口有以下两个实现类:
- StandardServletMultipartResolver:使用了 Servlet 3.0 标准的上传方式。
- CommonsMultipartResolver:使用了 Apache 的 commons-fileupload 来完成具体的上传操作。
MultpartiResolver 接口具有以下方法
名称 | 作用 |
---|---|
byte[] getBytes() | 以字节数组的形式返回文件的内容 |
String getContentType() | 返回文件的内容类型 |
InputStream getInputStream() | 返回一个InputStream,从中读取文件的内容 |
String getName() | 返回请求参数的名称 |
String getOriginalFillename() | 返回客户端提交的原始文件名称 |
long getSize() | 返回文件的大小,单位为字节 |
boolean isEmpty() | 判断被上传文件是否为空 |
void transferTo(File destination) | 将上传文件保存到目标目录下 |
单文件上传
导入jar包
Maven 项目在 pom.xml 文件中添加以下依赖。
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
配置 MultipartResolver
使用 CommonsMultipartReslover 配置 MultipartResolver 解析器,在 springmvc-servlet.xml 中添加代码如下。
<!-- 配置MultipartResolver,用于上传文件,使用spring的CommonsMultipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5000000" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
- defaultEncoding:请求的编码格式,默认为 ISO-8859-1,此处设置为 UTF-8(注:defaultEncoding 必须和 JSP 中的 pageEncoding 一致,以便正确读取表单的内容)。
- maxUploadSize:上传文件大小上限,单位为字节。
编写文件上传表单页面
负责文件上传表单的编码类型必须是“multipart/form-data”类型。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/fileupload"
method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="myfile"><br>
文件描述:<input type="text" name="description"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
表单的 enctype 属性指定的是表单数据的编码方式,该属性有以下 3 个值。
- application/x-www-form-urlencoded:这是默认的编码方式,它只处理表单域里的 value 属性值。
- multipart/form-data:该编码方式以二进制流的方式来处理表单数据,并将文件域指定文件的内容封装到请求参数里。
- text/plain:该编码方式只有当表单的 action 属性为“mailto:”URL 的形式时才使用,主要适用于直接通过表单发送邮件的方式。
编写控制器
@Controller
public class FileUploadController {
// 得到一个用来记录日志的对象,这样在打印信息时能够标记打印的是哪个类的信息
private static final Log logger = LogFactory.getLog(FileUploadController.class);
@RequestMapping("getFileUpload")
public String getFileUpload() {
return "fileUpload";
}
/**
* 单文件上传
*/
@RequestMapping("/fileupload")
public String oneFileUpload(@ModelAttribute FileDomain fileDomain, HttpServletRequest request) {
/*
* 文件上传到服务器的位置“/uploadfiles”,该位置是指 workspace\.metadata\.plugins\org.eclipse
* .wst.server.core\tmp0\wtpwebapps, 发布后使用
*/
String realpath = request.getServletContext().getRealPath("uploadfiles");
String fileName = fileDomain.getMyfile().getOriginalFilename();
File targetFile = new File(realpath, fileName);
if (!targetFile.exists()) {
targetFile.mkdirs();
}
// 上传
try {
fileDomain.getMyfile().transferTo(targetFile);
logger.info("成功");
} catch (Exception e) {
e.printStackTrace();
}
return "showFile";
}
}
多文件上传
前端
<body>
<h2>同一类别多个文件上传</h2>
<form name="onfile" action="onfiles2" method="post" enctype="multipart/form-data">
图片:
<input type="file" name="img"><br>
<input type="file" name="img"><br>
<input type="file" name="img"><br>
<input type="file" name="img"><br>
<input type="submit" value="提交">
</form>
</body>
服务器
@PostMapping("onfiles2")
@ResponseBody
public String onfiles2(MultipartFile img[]) throws IOException {
for(int i=0;i<img.length;i++)
{
if(!img[i].isEmpty())//文件不空
{
File imgfile =new File("F:/fileupload/"+img[i].getOriginalFilename());
imgfile.createNewFile();
img[i].transferTo(imgfile);
logger.info(img[i].getOriginalFilename());
}
}
return "sucucess";
}
15.2、文件下载
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Springmvc文件下载</title>
</head>
<body>
<h2>Springmvc文件下载</h2>
个人照片<a href="/download/个人照片.png">个人照片.png</a><br>
个人简历<a href="/download/个人简历.pdf">个人简历.pdf</a>
</body>
</html>
其中href是下载的超链接,download是下载的接口名,而链接最后面部分则是下载资源名称。
服务端
文件下载的原理就是服务端向客户端返回二进制流和信息,而Springmvc通过ResponseEntity完成。我们在controller中编写以下接口实现下载的功能:
@GetMapping("download/{filename}")
public ResponseEntity<byte[]>download(@PathVariable String filename) throws IOException {
//下载文件的路径(这里绝对路径)
String filepath= "F:/download/"+filename;
File file =new File(filepath);
//创建字节输入流,这里不实用Buffer类
InputStream in = new FileInputStream(file);
//available:获取输入流所读取的文件的最大字节数
byte[] body = new byte[in.available()];
//把字节读取到数组中
in.read(body);
//设置请求头
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("Content-Disposition", "attchement;filename=" + file.getName());
//设置响应状态
HttpStatus statusCode = HttpStatus.OK;
in.close();
ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(body, headers, statusCode);
return entity;//返回
}
文件下载非常常见的问题:中文文件名错误显示。这个解决方案也很容易解决,只需将Content-Disposition内容后面的文件名进行url编码即可,具体代码为(替换上面对于部分):
headers.add("Content-Disposition", "attchement;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
15.3、总结
前面所讲文件上传,前端就是form表单用<input type="file">
表示客户端要上传文件,而服务端主要使用MultipartFile
或者MultipartFile[]
分别接收单个文件和多个文件。而在存储到本地也仅仅需要在本地磁盘创建对应文件然后MultipartFile
调用transferTo()
方法即可将上传的文件储存。
而文件下载的前端需要一个请求的url链接,服务端需要编写这个链接对应的接口。通过一些名称找到文件在本地真实的位置通过ResponseEntity
即可将二进制文件返回给客户达到文件下载的功能。而ResponseEntity
使用也很简单在创建时候只需要传入二进制主体、头和状态码即可成功返回,而这些Springmvc已进行了很好封装你可以直接使用。
而无论是文件上传、多文件上传还是文件下载,一个完整的案例大致都需要这样一个过程:
- 构思需求和页面大体样式
- 编写前端html页面
- 编写服务端响应的请求
- 启动程序运行测试
在其中过程如果有问题可以根据编译器的错误提示、运行时的错误日志找到根源进行修正,这样完整的案例就可以成功完成啦!