# springMVC-study
**Repository Path**: lyc12345899/spring-mvc-study
## Basic Information
- **Project Name**: springMVC-study
- **Description**: 专注于Spring MVC框架学习与实践的开源项目,包含基础示例和高级特性讲解,助您快速掌握Spring MVC核心知识。
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-04-14
- **Last Updated**: 2025-04-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# springMVC
ssm:mybatis+Spring+SpringMVC MVC三层架构
### 1.1、什么是MVC
- MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。
- 是将业务逻辑、数据、显示分离的方法来组织代码。
- MVC主要作用是**降低了视图与业务逻辑间的双向偶合**。
- MVC不是一种设计模式,**MVC是一种架构模式**。当然不同的MVC存在差异。
**Model(模型):**数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
**View(视图):**负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
**Controller(控制器):**接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。
**最典型的MVC就是JSP + servlet + javabean的模式。**

### 1.2、Model1时代
- 在web早期的开发中,通常采用的都是Model1。
- Model1中,主要分为两层,视图层和模型层。

Model1优点:架构简单,比较适合小型项目开发;
Model1缺点:JSP职责不单一,职责过重,不便于维护;
### 1.3、Model2时代
Model2把一个项目分成三部分,包括**视图、控制、模型。**

1. 用户发请求
2. Servlet接收请求数据,并调用对应的业务逻辑方法
3. 业务处理完毕,返回更新后的数据给servlet
4. servlet转向到JSP,由JSP来渲染页面
5. 响应给前端更新后的页面
**职责分析:**
**Controller:控制器**
1. 取得表单数据
2. 调用业务逻辑
3. 转向指定的页面
**Model:模型**
1. 业务逻辑
2. 保存数据的状态
**View:视图**
1. 显示页面
Model2这样不仅提高的代码的复用率与项目的扩展性,且大大降低了项目的维护成本。Model 1模式的实现比较简单,适用于快速开发小规模项目,Model1中JSP页面身兼View和Controller两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的扩展性和维护的难度。Model2消除了Model1的缺点。
Vo**还是实体类**,只是将他细分了
面试题:假设面试官问:你项目的架构,是设计好的,还是演进的?
所有的项目都不可能是一开始就设计好的,都是从一个单机项目发展,逐渐演进的。
### 1.4、回顾Servlet
1. 新建一个Maven工程当做父工程!pom依赖!
```xml
junit
junit
4.13.2
org.springframework
spring-webmvc
7.0.0-M2
javax.servlet
javax.servlet-api
3.1.0
provided
javax.servlet
jsp-api
2.0
javax.servlet
jstl
1.1.2
```
2. 建立一个Moudle:springmvc-01-servlet , 添加Web app的支持!
3. 导入servlet 和 jsp 的 jar 依赖
```xml
javax.servlet
javax.servlet-api
3.1.0
provided
javax.servlet
jsp-api
2.0
```
4. 编写一个Servlet类,用来处理用户的请求
```java
package com.kuang.servlet;
//实现Servlet接口
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//取得参数
String method = req.getParameter("method");
if (method.equals("add")){
req.getSession().setAttribute("msg","执行了add方法");
}
if (method.equals("delete")){
req.getSession().setAttribute("msg","执行了delete方法");
}
//业务逻辑
//视图跳转
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
```
5. 编写test.jsp,在WEB-INF目录下新建一个jsp的文件夹,新建test.jsp
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
test
${msg}
```
6. 在web.xml中注册Servlet
```xml
hello
com.lyc.servlet.HelloServlet
hello
/hello
index.jsp
```
7. 配置Tomcat,并启动测试
8. - localhost:8023/hello?method=add
- localhost:8023/hello?method=delete
9. 
10. 
**MVC框架要做哪些事情**
1. 将url映射到java类或java类的方法 .
2. 封装用户提交的数据 .
3. 处理请求--调用相关的业务处理--封装响应数据 .
4. 将响应的数据进行渲染 . jsp / html 等表示层数据 .
**说明:**
常见的服务器端MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF;常见前端MVC框架:**vue、angularjs、react**、backbone;由MVC演化出了另外一些模式如:MVP、**MVVM** 等等....
MVVM: M V VM:ViewModel:双向绑定
## 2、什么是SpringMVC
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
查看官方文档:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#spring-web
spring与springMVC的联系:我们可以将springMVC中所有要用到的bean,注册到spring中!直接去使用
**我们为什么要学习SpringMVC呢?**
Spring MVC的特点:
1. 轻量级,简单易学
2. 高效 , 基于请求响应的MVC框架
3. 与Spring兼容性好,无缝结合
4. 约定优于配置
5. 功能强大:RESTful、数据验证、格式化、本地化、主题等
6. 简洁灵活
7. **最重要的一点还是用的人多 , 使用的公司多 .**
Spring的web框架围绕**DispatcherServlet** [ 调度Servlet ] 设计。
DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁;
正因为SpringMVC好 , 简单 , 便捷 , 易学 , 天生和Spring无缝集成(使用SpringIoC和Aop) , 使用约定优于配置 . 能够进行简单的junit测试 . 支持Restful风格 .异常处理 , 本地化 , 国际化 , 数据验证 , 类型转换 , 拦截器 等等......所以我们要学习 .
SEO(搜索引擎优化)
### 2.2、中心控制器
Spring的web框架围绕**DispatcherServlet**设计。DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的**controller**声明方式。
Spring MVC框架像许多其他MVC框架一样, **以请求为驱动** , **围绕一个中心Servlet分派请求及提供其他功能**,**DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)**。

SpringMVC的原理如下图所示:
当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

### 2.3、SpringMVC执行原理

图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。
**简要分析执行流程**
1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
```xml
springmvc
/
```
所有的请求都被拦截了
我们假设请求的url为 : http://localhost:8080/SpringMVC/hello
**如上url拆分成三部分:**
http://localhost:8080服务器域名
SpringMVC部署在服务器上的web站点
hello表示控制器
通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。
2. HandlerMapping为处理器映射。**DispatcherServlet调用HandlerMapping**,HandlerMapping**根据请求url查找Handler**。
```xml
...
```
3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
6. Handler让具体的Controller执行。**实现controller接口的类,都会去找**
7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
```java
@Override
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();
//封装对象,放在ModelAndView中。Model
mv.addObject("msg","HelloSpringMVC!");
//封装要跳转的视图,放在ModelAndView中
mv.setViewName("hello");
//: /WEB-INF/jsp/hello.jsp
return mv;
}
```
8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。
11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
12. 最终视图呈现给用户。

1.用户发送请求,dispatcherServlet(前端控制器)接受请求并拦截请求
2.DispatcherServlet去找他的处理映射器 HanderMappering,在将映射器返回给dispactherServlet
3.dispatcherServlet根据这个映射器去适配这个映射器( HanderAdapter )
4.适配器其实就是controller,执行相应的controller
4.返回一个ModelAndView 到dispatcherServlet
5.通过ModelAndView去配置具体的视图解析器(ViewResolver)
6.视图解析器返回给前端调用
## 如何创建springMVC的第一个项目
第一步:确定我们项目中的maven依赖中有spring-webmvc;
```xml
org.springframework
spring-webmvc
5.1.9.RELEASE
```
第二步:为了防止报错404,确定我们项目中artifacts中lib包内有项目的全部依赖

第三步:在web.xml中配置dispatcherServlet
```xml
springmvc
org.springframework.web.servlet.DispatcherServlet
springmvc
/
```
注意点:
在springmvc中
/:只匹配所有的请求,不会去匹配jsp页面
/*:匹配所有的请求,包括jsp页面
我们去配视图解析器时会添加视图页面的前后缀,如果在servletMapper中使用/* 会将jsp页面也做一遍,就会变成a.jsp.jsp无限死循环,需要注意
第四步:为dispatcherServlet绑定spring的配置文件(需要在中配置)
```xml
contextConfigLocation
classpath:springmvc-servlet.xml
```
且需要在resource包中创建springmvc-servlet.xml文件,
第五步:设置启动级别:需要跟服务器一起启动,级别为1
```xml
1
```
代码展示:
```xml
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc-servlet.xml
1
springmvc
/
```
第六步:在springmvc-servlet.xml中配置处理器映射器与处理器适配器以及视图解析器(为了理解原理才配置,真实开发不需要配置,**这三个是springmvc的核心三要素**)
```xml
```
第七步:spring文件的配置完成了,现在去写Controller层的业务并完成视图跳转
HelloController.java
```Java
package com.lyc.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
//业务代码
String result = "HelloSpringMVC!";
mv.addObject("msg",result);
//视图跳转
mv.setViewName("test");
return mv;
}
}
```
第八步:因为这个处理器映射器BeanNameUrlHanderMapper是根据Bean的名字去找,因此我们还需要设置Bean
```Java
```
第九步:配置tomcat

运行效果

至此项目完成!
## 使用注解开发springMVC
注:由于Maven可能存在资源过滤的问题,在maven依赖中加入
```xml
src/main/resources
**/*.properties
**/*.xml
true
src/main/java
**/*.properties
**/*.xml
true
```
第一步:在pom.xml文件引入相关的依赖
主要有spring框架核心库。springmvc,servlet,JSTL(jsp标准标签库),已经在父依赖中了
第二步:配置web.xml
注:
- 注册DispatcherServlet
- 关联sprigMVC的配置文件
- 启动级别为1
- 映射路径为/(不能为/*)
第三步:配置springmvc-servlet.xml文件
```xml
```
这些在spring配置文件就不需要动了,不用改变,直接使用
注:**在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证试图安全,因为这个目录下的文件,客户端不能直接访问。**
第四步:创建Controller
```Java
package com.lyc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/HelloController")
public class HelloController {
//封装数据
//真是访问地:项目名/HelloController/hello
@RequestMapping("/hello")
public String Hello(Model model){
//向模型中添加属性msg与值,可以在JSP页面中取出并渲染
model.addAttribute("msg","HelloSpringMVC!");
//web-inf/jsp/hello.jsp
return "hello";
}
}
```
解析代码:@controller是为了让Spring IOC 容器初始化时自动扫描到
@RequestMapping是为了映射请求路径,这里因为类和方法上都有映射,所以访问时应该是/HelloController/hello
方法中声明Model类型的参数是为了把Action中的数据带到视图中
方法返回的是视图的名称hello,加上配置文件中得前后缀变成WEB-INF/jsp/hello.jsp
第五步:创建视图层 web/WEB-INF/jsp/hello.jsp
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
${msg}
```
运行时报错:

**Unsupported class file major version 61**
经过查询发现是jdk版本过高,我用的是jdk17,将版本改到jdk11重启,将maven配置文件中的版本改为11即可

运行成功!
总结步骤:
1. 新建maven项目
2. 导入相关依赖
3. 编写web.xml,注册DispatcherServlet
4. 编写springmvc配置文件
5. 创建对应的Controller类
6. 完善前端视图和Controller之间的对应
7. 测试运行测试
注:springMVC配置文件中的核心三要素:处理器映射器,处理器适配器,视图解析器,在注解开发的帮助下,我们只需要去手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可< mvc:annotation-driven/> 是代码更加简洁高效
## Controller配置总结
> 控制器Controller
- 控制器复杂提供访问应用程序的行为,通常通过接口定义或**注解定义**两种方式
- 控制器负责**解析客户的请求**并**转换成一个模型**
- 在springMVC中,一个控制器类可以包含多种方法
- 在springMVC中,对于controller的配置有多种
> 实现Controller接口
Controller是一个接口,在org.springframework.web.servlet.mvc包下
```Java
@FunctionalInterface//函数式接口
//实现该接口的类获得控制器功能
public interface Controller {
@Nullable//值可以为空
ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;//处理请求并返回一个MOdelAndView对象
}
```
### 测试1
1.新建一个模块,web.xml与springmvc-servlet.xml中的代码不变
2.编写一个Controller类,ControllerDemo01
```Java
//只要实现了该接口的类,说明这就是一个控制器
public class ControllerDemo01 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
//业务代码
mv.addObject("msg","ControllerDemo01");
//视图跳转
mv.setViewName("test");
return mv;
}
}
```
3.编写完毕后,去spring配置文件中注册请求的bean;name代表请求路径,class代表对应请求的类
```xml
```
4.编写前端页面,对应视图解析器
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
${msg}
```
配置tomcat运行测试

小结:
- 实现接口定义控制器有些过时了
- 缺点:一个控制器里只有一个方法,如果多个方法则需要定义多个Controller;定义的方法比较麻烦
### 测试2
> 使用注解@Controller
- @Controller注解类型用于声明spring类的实例是一个控制器(在spring笔记中还有另外三个注解,@Component,@Service,@Repository);
- spring可以通过扫描机制来找到应用程序中所有基于注解的控制类,为了保证spring能找到控制器,需要在配置文件中声明组件扫描
```xml
```
- 增加一个ControllerDemo02类,使用注解实现
```Java
package com.lyc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
//代表这个类被spring接管
//被解析的这个类中的所有的方法,如果返回值是string,并且有具体的页面可以跳转,就会被视图解析器解析
@RequestMapping("/demo2")
public class ControllerDemo02 {
@RequestMapping("/test2")
public String demo01(Model model){
model.addAttribute("msg","ControllerDemo02");
return "test";
}
```

小结:从这里两次测试可以发现,我们的两个请求是可以指向一个视图的,但是页面显示的结果是不同的,表明这里的视图是被**复用**了,而控制器与视图之间是**弱耦合关系**
### RequsetMapping说明
> RequestMapping
- @RequestMapping 注解用于**映射URL到控制器类**或**一个特定的处理程序方法**,可用于类或方法上,用于类上,表示类中的所有响应请求的方法都是以该地址作为父地址 如果要访问类中的方法,需要在网页中加上**/父地址/方法地址**
测试:
ConreollerDemo3
```Java
package com.lyc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/demo3")
public class ControllerDemo3 {
@RequestMapping("/test3")
public String test(Model model){
model.addAttribute("msg","ControllerDemo3");
return "test";
}
}
```

## RestFul风格
### 概念
RestFul就是一个资源定位及资源操作的风格,不是标准也不是协议,只是一种风格,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制
### 功能
- 资源:互联网所有的事物都可以抽象为资源
- 资源操作:使用POST,DELETE,PUT,GET,使用不同方法对资源进行操作
- 分别对应,添加,删除,修改,查询
**传统方式操作资源**:通过不同的参数来实现不同的效果!方法单一,post,get
- http://127.0.0.1/item/queryItem.action?id=1 查询,GET
- http://127.0.0.1/item/savetem.action 新增,POST
- http://127.0.0.1/item/updateItem.action更新,POST
- http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST
**使用RestFul操作资源**:可以通过**不同的请求方式**来实现不同的效果!如下:请求地址一样,但是功能可以不同!
- http://127.0.0.1/item/1 查询,GET
- http://127.0.0.1/item 新增,POST
- http://127.0.0.1/item 更新,PUT
- http://127.0.0.1/item/1 删除,DELETE
### 测试
1. 新建一个Controller类 RestFulController
2. 在springMVC中可以使用@PathVariable 注解,让方法参数的值对应绑定到一个URL模板变量上
```Java
@Controller
public class RestFulController {
//http://localhost:8023/springmvc_04_controller_Web_exploded/add?a=1&b=2 原来的写法
// http://localhost:8023/springmvc_04_controller_Web_exploded/add/1/2 RestFul风格
@RequestMapping("/add/{a}/{b}")
public String test(@PathVariable int a, @PathVariable int b, Model model){
int result = a+b;
model.addAttribute("msg","结果为"+result);
return "test";
}
}
```
3.运行效果

使用路径变量的好处:
- 是路径变的更加简洁高效且**安全**,**不会暴露参数**
- 获得参数更加方便,框架会自动进行类型转换
- 通过路径变量的类型可以约束访问参数,如果请求类型不一样,则访问不到对应的请求方法,会**报错400,请求错误**
这样是比之前的写法简便许多,但好像还是无法理解同样的URL却是不同的作用,让我们继续测试,我们先看下RequestMapping的源码
```Java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
```
发现其中还有个方法method,返回RequestMethod类型,而RequestMethod是一个枚举,其中就是不同的请求方式
```
public enum RequestMethod {
GET,
HEAD,
POST,
PUT,
PATCH,
DELETE,
OPTIONS,
TRACE;
```
这说明我们可以使用method属性来指定请求类型
用于约束请求的类型,可以收窄请求范围,指定请求谓词的类型如: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
让我们通过测试感受一下
```Java
public class RestFulController {
//http://localhost:8023/springmvc_04_controller_Web_exploded/add?a=1&b=2 原来的写法
// http://localhost:8023/springmvc_04_controller_Web_exploded/add/1/2 RestFul风格
@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
public String test(@PathVariable int a, @PathVariable int b, Model model){
int result = a+b;
model.addAttribute("msg","结果1为"+result);
return "test";
}
@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.POST)
public String test1(@PathVariable int a, @PathVariable int b, Model model){
int result = a+b;
model.addAttribute("msg","结果2为"+result);
return "test";
}
}
```
直接运行结果:

我们在写一个表单用post方法请求
a.jsp
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
```
测试:进入a.jsp提交表单


两次对比,我们发现两次测试的URL一致,但方法却不相同!!!
小结:SpringMVC的@RequestMapping注解能够处理HTTP请求的方法,如GET,POST,PUT,DELETE以及PATCH
所有的地址栏请求默认都会是**HTTP GET**类型的
**简便方法:**Spring中还有组合注解,比如 @GetMapping("/add/{a}/{b}")代替**@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)**
```Java
@GetMapping("/add/{a}/{b}") == @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)**
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
```
注:一般@GetMapping使用较多
## SpringMVC:结果跳转方式
### ModelAndView
设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 .
页面 : {视图解析器前缀} + viewName +{视图解析器后缀}
```xml
```
对应的controller类
```java
public class ControllerDemo1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
```
### ServletAPI
通过设置ServletAPI , 不需要视图解析器 .
1、通过HttpServletResponse进行输出
2、通过HttpServletResponse实现重定向
3、通过HttpServletResponse实现转发
```java
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
```
### SpringMVC
方式一:**通过SpringMVC来实现转发和重定向 - 无需视图解析器;**
测试前,需要将视图解析器注释掉
```java
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
// URL不变,转发
return "/WEB-INF/jsp/test.jsp";
}
@RequestMapping("/rsm/t2")
public String test2(){
//转发二
return "forward:/WEB-INF/jsp/test.jsp";
}
@RequestMapping("/rsm/t3")
public String test3(){
// URL变化 重定向
return "redirect:/index.jsp";
}
}
```
**通过SpringMVC来实现转发和重定向 - 有视图解析器;**
重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方 , 所以注意路径问题.
可以重定向到另外一个请求实现 .
```java
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test";
}
@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp";
//return "redirect:hello.do"; //hello.do为另一个请求/
}
}
```
**视图解析器就是一个URL识别并拼接工具**
### SpringMVC:数据处理
**1、提交的域名称和处理方法的参数名一致**
提交数据 : http://localhost:8023/springmvc_04_controller_Web_exploded/user/t1?name=lyc
处理方法 :
```java
@Controller
@RequestMapping("/user")
public class UserController {
//localhost:8080/user/t1?name=xxxx
@GetMapping("/t1")
public String test1(String name, Model model){
//1.接受前端参数
System.out.println("接受前端的参数:"+name);
//2.将返回的结果传递给前端,Model
model.addAttribute("msg",name);
//3.跳转到指定页面
return "test";
}
}
```
后台输出 : lyc
**2、提交的域名称和处理方法的参数名不一致**
提交数据 : http://localhost:8080/hello?username=lyc
处理方法 :
```java
//@@RequestParam("username") : username提交的域的名称 .
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
```
后台输出 : lyc
**这样可以明确表示这个参数是前端所需的参数,建议以后传前端参数时带上@RequestParam("username")**
**3、提交的是一个对象**
要求提交的表单域和对象的属性名一致 , 参数使用对象即可
1、实体类
```Java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String username;
private int age;
}
```
2、提交数据 : http://localhost:8080/mvc04/user?name=kuangshen&id=1&age=15
3、处理方法 :
```java
@RequestMapping("/user")
public String user(User user){
System.out.println(user);
return "hello";
}
```
后台输出 : User { id=1, username='lyc', age=15 }
说明:如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null。
### 数据显示到前端
**第一种 : 通过ModelAndView**
我们前面一直都是如此 . 就不过多解释
```java
public class ControllerTest1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}
```
**第二种 : 通过ModelMap**
ModelMap
Model--->ModelMap--->LinkedHashMap
ModelMap:继承了LinkHashMap。所以它拥有LinkHashMap的所有功能
Model:精简版(大部分情况直接使用Model)
```java
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("name",name);
System.out.println(name);
return "hello";
}
```
**第三种 : 通过Model**
Model
```java
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("msg",name);
System.out.println(name);
return "test";
}
```
### 对比
就对于新手而言简单来说使用区别就是:
```
Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;
ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;
ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
```
当然更多的以后开发考虑的更多的是性能和优化,就不能单单仅限于此的了解。
**请使用80%的时间打好扎实的基础,剩下18%的时间研究框架,2%的时间去学点英文,框架的官方文档永远是最好的教程。**
### 乱码问题
1、我们可以在首页编写一个提交的表单
```html
```
2、后台编写对应的处理类
```java
@Controller
public class Encoding {
@RequestMapping("/e/t")
public String test(Model model,String name){
model.addAttribute("msg",name); //获取表单提交的值
return "test"; //跳转到test页面显示输入的值
}
}
```
3、输入中文测试,发现乱码

不得不说,乱码问题是在我们开发中十分常见的问题,也是让我们程序猿比较头大的问题!
以前乱码问题通过过滤器解决 , 而SpringMVC给我们提供了一个过滤器 , 可以在web.xml中配置 .
修改了xml文件需要重启服务器!
```xml
encoding
org.springframework.web.filter.CharacterEncodingFilter
encoding
utf-8
encoding
/*
```
但是我们发现 , 有些极端情况下.这个过滤器对get的支持不好 .
处理方法 :
1、修改tomcat配置文件 :设置编码!
```xml
```
2、自定义过滤器
```java
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
```
这个也是我在网上找的一些大神写的,一般情况下,SpringMVC默认的乱码处理就已经能够很好的解决了!
**然后在web.xml中配置这个过滤器即可!**
乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!
## JSON交互处理
### 什么是JSON
前后端分离时代:后端部署后端,提供接口,提供数据:
json:数据格式
前端独立部署,负责渲染后端数据:
#### 概述
- JSON(JavaScript Object Notation, JS 对象标记) 是一种**轻量级**的数据交换格式,目前使用特别广泛。
- 采用完全独立于编程语言的**文本格式**来存储和表示数据。
- **简洁和清晰的层次结构**使得 JSON 成为理想的数据交换语言。
- 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
在 JavaScript 语言中,**一切都是对象**。因此,任何JavaScript 支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。看看他的要求和语法格式:
- 对象表示为键值对,数据由逗号分隔
- 花括号保存对象
- 方括号保存数组
**JSON 键值对**是用来保存 JavaScript 对象的一种方式,和 JavaScript 对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值:
```json
{"name": "lyc"}
{"age": "3"}
{"sex": "男"}
```
很多人搞不清楚 JSON 和 JavaScript 对象的关系,甚至连谁是谁都不清楚。其实,可以这么理解:
JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
```javascript
var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的
var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串
```
**JSON 和 JavaScript 对象互转**
要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法:
```javascript
// 将JSON字符串转换为JavaScript对象
var person = JSON.parse(myJSON);
console.log(person)
/*
{name: 'John', age: 30, city: 'New York'}
age: 30
city: "New York"
name: "John"
*/
```
要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法:
```javascript
var myJSON = JSON.stringify(person);
console.log(myJSON)
//结果是{"name":"John","age":30,"city":"New York"}
```
### Controller返回JSON数据
推荐工具:Jackson ,fastjson等等
使用前置条件,导入依赖
```
com.fasterxml.jackson.core
jackson-databind
2.18.3
```
配置SpringMVC需要的配置
web.xml
```xml
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc-servlet.xml
1
springmvc
/
encoding
org.springframework.web.filter.CharacterEncodingFilter
encoding
utf-8
encoding
/*
```
springmvc-servlet.xml
```xml
```
我们随便编写一个User的实体类,然后我们去编写我们的测试Controller;
```Java
package com.lyc.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
private int id;
private String username;
private String password;
}
```
这里我们需要两个新东西,一个是@ResponseBody,一个是ObjectMapper对象,我们看下具体的用法
还有**@RestController,直接配置在类上,所有方法都只会返回字符串**
**@Controller配置上去,回去走视图解析器**
**@ResponseBody配合@Controller 起到@RestController的作用**
```java
@Controller
public class UserController {
@RequestMapping(value = "/json1", produces = "application/json;charset=utf-8")//produces:指定响应体返回类型和编码
@ResponseBody //他就不会走视图解析器,会直接返回一个字符串
public String json1() throws JsonProcessingException {
//jackson可以解析java对象,通过@ResponseBody返回json格式的数据
//ObjectMapper对象
ObjectMapper objectMapper = new ObjectMapper();
User user = new User(1,"吴雁卿","123456");
String s = objectMapper.writeValueAsString(user);
return s;
}
}
```

【注意:使用json记得处理乱码问题】
> 代码优化
**乱码统一解决**
上一种方法比较麻烦,如果项目中有许多请求则每一个都要添加,可以通过Spring配置统一指定,这样就不用每次都去处理了!
我们可以在springmvc的配置文件上添加一段消息StringHttpMessageConverter转换配置!
```xml
```
#### 测试集合输出
```java
@RequestMapping("/json2")
public String json2() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List users = new ArrayList<>();
User user1 = new User(1,"吴雁卿","123456");
User user2 = new User(2,"陈思琪","123434");
User user3 = new User(3,"吴梦婷","123467");
users.add(user1);
users.add(user2);
users.add(user3);
String s = mapper.writeValueAsString(users);
return s;
//[{"id":1,"username":"吴雁卿","password":"123456"},
// {"id":2,"username":"陈思琪","password":"123434"},
// {"id":3,"username":"吴梦婷","password":"123467"}]
}
```

#### 输出时间对象
```Java
@RequestMapping("/json3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Date date = new Date();
return mapper.writeValueAsString(date);
```

默认日期格式会变成一个数字,是1970年1月1日到当前日期的毫秒数!
Jackson 默认是会把时间转成timestamps形式
**解决方案1: SimpleDateFormat自定义时间格式**
```java
@RequestMapping("/json3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
return mapper.writeValueAsString(simpleDateFormat.format(date));
```
运行结果 : 成功的输出了时间!

**解决方案2:取消timestamps形式 mapper自定义时间格式**
```Java
@RequestMapping("/json3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
//取消时间戳格式,自定义时间格式
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
Date date = new Date();
return mapper.writeValueAsString(date);
```

> 抽取为工具类
**如果要经常使用的话,这样是比较麻烦的,我们可以将这些代码封装到一个工具类中**
```Java
package com.lyc.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.text.SimpleDateFormat;
public static String GetJson(Object object) throws JsonProcessingException {
return GetJson(object,"yyyy-MM-dd HH:mm:ss");
}
public class JsonUtils {
public static String GetJson(Object object, String dateFormat) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
mapper.setDateFormat(sdf);
return mapper.writeValueAsString(object);
}
}
```
有工具类之后的方法:
```Java
@RequestMapping("/json3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Date date = new Date();
JsonUtils.GetJson(date);
return mapper.writeValueAsString(date);
}
```
测试完成
> FastJson
fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是一样的。
fastjson 的 pom依赖!
```xml
com.alibaba
fastjson
2.0.53
```
fastjson 三个主要的类:
**JSONObject 代表 json 对象**
- JSONObject实现了Map接口, 猜想 JSONObject底层操作是由Map实现的。
- JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取"键:值"对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。
**JSONArray 代表 json 对象数组**
- 内部是有List接口中的方法来完成操作的。
**JSON代表 JSONObject和JSONArray的转化**
- JSON类源码分析与使用
- 仔细观察这些方法,主要是实现json对象,json对象数组,javabean对象,json字符串之间的相互转化。
**代码测试,我们新建一个FastJsonDemo 类**
```java
package com.kuang.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kuang.pojo.User;
import java.util.ArrayList;
import java.util.List;
public class FastJsonDemo {
public static void main(String[] args) {
//创建一个对象
User user1 = new User(1,"吴雁卿","123456");
User user2 = new User(2,"陈思琪","123434");
User user3 = new User(3,"吴梦婷","123467");
List list = new ArrayList();
list.add(user1);
list.add(user2);
list.add(user3);
System.out.println("*******Java对象 转 JSON字符串*******");
String str1 = JSON.toJSONString(list);
System.out.println("JSON.toJSONString(list)==>"+str1);
String str2 = JSON.toJSONString(user1);
System.out.println("JSON.toJSONString(user1)==>"+str2);
System.out.println("\n****** JSON字符串 转 Java对象*******");
User jp_user1=JSON.parseObject(str2,User.class);
System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);
System.out.println("\n****** Java对象 转 JSON对象 ******");
JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));
System.out.println("\n****** JSON对象 转 Java对象 ******");
User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
}
}
```
这种工具类,我们只需要掌握使用就好了,在使用的时候在根据具体的业务去找对应的实现。和以前的commons-io那种工具包一样,拿来用就好了!
## 整合SSM项目
[第一个简易SSM框架项目-CSDN博客](https://blog.csdn.net/m0_73856804/article/details/147050521?spm=1001.2014.3001.5502)
## Ajax技术
> 简介
- **AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。**
- AJAX 是一种在无需重新加载整个网页的情况下**,能够更新部分网页的技术。**
- **Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术。**
- 在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。Google Suggest能够自动帮你完成搜索单词。
- Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。
- 就和国内百度的搜索框一样!
- 传统的网页(即不用ajax技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页。
- 使用ajax技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新。
- 使用Ajax,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的Web用户界面。
> 伪造Ajax
我们可以使用前端的一个标签来伪造一个ajax的样子。iframe标签
代码示例
```HTML
iframe测试体验页面无刷新
```
这个和Ajax的请求很像,但不是,他还是在页面中去请求网站,并不是异步请求,局部刷新

> 真正的Ajax
Ajax的核心是**XMLHttpRequest对象**(XHR)。XHR为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据。
jQuery 提供多个与 AJAX 有关的方法。
通过 jQuery AJAX 方法,您能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON – 同时您能够把这些外部数据直接载入网页的被选元素中。
jQuery 不是生产者,而是大自然搬运工。
jQuery Ajax本质就是 XMLHttpRequest,对他进行了封装,方便调用!
```
jQuery.ajax(...)
部分参数:
url:请求地址
type:请求方式,GET、POST(1.9.0之后用method)
headers:请求头
data:要发送的数据
contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
async:是否异步
timeout:设置请求超时时间(毫秒)
beforeSend:发送请求前执行的函数(全局)
complete:完成之后执行的回调函数(全局)
success:成功之后执行的回调函数(全局)
error:失败之后执行的回调函数(全局)
accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型
dataType:将服务器端返回的数据转换成指定类型
"xml": 将服务器端返回的内容转换成xml格式
"text": 将服务器端返回的内容转换成普通文本格式
"html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
"script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
"json": 将服务器端返回的内容转换成相应的JavaScript对象
"jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数
```
**我们来个简单的测试,使用最原始的HttpServletResponse处理 , 最简单 , 最通用**
AjaxController.java
```Java
@RequestMapping("/a1")
public void ajax1(String name, HttpServletResponse response) throws IOException {
if (name.equals("lyc")){
response.getWriter().write("true");
}else {
response.getWriter().write("false");
}
}
```
index.jap
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%-- 绝对路径
${pageContext.request.contextPath}
--%>
<%--失去焦点的时候,发送一个请求(携带信息)到后台--%>
用户名:
```

前后端分离示意图

Ajax把主动权交给前端
案例代码展示:
test2.jsp:
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
```
AjaxController.java
```java
@RequestMapping("/t2")
public List ajax2(){
List users = new ArrayList();
//添加数据
users.add(new User("lyc",5,"男"));
users.add(new User("java",5,"女"));
users.add(new User("ajax",5,"男"));
return users;
}
```
效果展示:

成功实现了数据回显!可以体会一下Ajax的好处!
### Ajax验证用户名体验
在使用Ajax之后,我们其实就可以实现登陆页面输入用户名 失去焦点后直接判断用户名是否正确
案例代码展示
页面展示:
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
用户名:
密码:
```
控制层代码:
```Java
@RequestMapping("/t3")
public String ajax3(String name, String password){
String msg = "";
if (name!=null) {
//这些数据已更改在数据库中查询
if ("admin".equals(name)) {
msg = "ok";
} else {
msg = "用户名错误";
}
}
if (password!=null){
//这些数据已更改在数据库中查询
if ("123456".equals(password)){
msg = "ok";
}else {
msg = "密码错误";
}
}
return msg;
}
```
**【记得处理json乱码问题】**
效果展示

## 拦截器
> 概述
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器**Filter**,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
**过滤器与拦截器的区别:**拦截器是**AOP思想**的具体应用。
**过滤器**
- servlet规范中的一部分,任何java web工程都可以使用
- 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
**拦截器**
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
- 拦截器只会拦截**访问的控制器方法**, 如果访问的是jsp/html/css/image/js是不会进行拦截的(自带静态资源过滤)
structs2也有拦截器,他们之间的区别
1. 从拦截级别上看,springMVC是方法级别的拦截,而structs2是类级别的拦截
2. 数据独立性:springMVC方法间独立,独享request和response
所以struct2的配置文件要大于spring MVC
> 自定义拦截器
那如何实现拦截器呢?
想要自定义拦截器,必须实现 HandlerInterceptor 接口。
1、新建一个Moudule , springmvc-07-Interceptor , 添加web支持
2、配置web.xml 和 springmvc-servlet.xml 文件
3、编写一个拦截器
```Java
package com.lyc.Interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
//return true表示放行,执行下一个拦截器,false表示拦截
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("================处理前===============");
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("================清理===============");
}
}
```
在springmvc的配置文件中配置拦截器
```xml
```
编写一个Controller,接收请求
```Java
package com.lyc.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class InterceptorController {
@GetMapping("/h1")
public String test(){
System.out.println("这个Controller执行了");
return "ok";
}
}
```
当返回值为true时

当返回值为false时

Controller请求被拦截了,无法发送到前端
### 实现需要先登录才可以进入首页
案例代码展示
先编写前端页面
首页:
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
首页
${username}
注销
```
登陆页面:
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%--在WEB-INF下的所有页面或者资源,只能通过controller,或者servlet进行访问--%>
登陆页面
```
index.jsp
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
```
在编写拦截器时,思考,要求没有登陆时点击首页会跳到登陆页面,登陆后跳转到首页,再刷新也可以进首页,注销后需要再进入登陆页面
由此得出
LoginInterceptor.java
```Java
package com.lyc.Interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
//在登陆页面上也需要放行
if (request.getRequestURI().contains("goLogin")){
return true;
}
// 判断用户是否登录
//第一次登陆,也是没有session的
if (request.getRequestURI().contains("login")){
return true;
}
if (session.getAttribute("loginInfo")!= null){
return true;
}
// 重定向到登录页面
response.sendRedirect("http://localhost:8023/springmvc_07_intercepter_Web_exploded/user/goLogin");
return false;
}
}
```
不要忘记在配置文件中注册拦截器的bean
```xml
```
最后写Controller类
LoginController.java
```java
package com.lyc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/goLogin")
public String login(){
return "login";
}
@RequestMapping("/login")
public String login(HttpSession session, String username, String password, Model model){
//把用户的信息存在session中
System.out.println("username===>"+username+"password===>"+password);
session.setAttribute("loginInfo",username);
model.addAttribute("username",username);
return "main";
}
@RequestMapping("/main")
public String main1(){
return "main";
}
@RequestMapping("/goOut")
public String goOut(HttpSession session){
session.removeAttribute("loginInfo");
return "main";
}
}
```
需要点击两次注销方法
第一次点击注销方法,先过的拦截器,还存在session,过了拦截器,再注销session,到达了main.jsp
第二次点击注销方法:session在第一次点击已经移除,方法被拦截,返回login页面
## 文件上传与下载
> 准备工作
文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配**MultipartResolver**,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置**MultipartResolver。**
前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以**二进制数据**发送给服务器;
**对表单中的 enctype 属性做个详细的说明:**
- application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
- multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
- text/plain:除了把空格转换为 "+" 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
```html
```
一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。
- Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
- 而Spring MVC则提供了更简单的封装。
- Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的**MultipartResolver**实现的。
- Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:
- CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。
> 文件上传
1、导入文件上传的jar包,commons-fileupload , Maven会自动帮我们导入他的依赖包 commons-io包;
```xml
commons-fileupload
commons-fileupload
1.3.3
javax.servlet
javax.servlet-api
4.0.1
```
2、配置bean:multipartResolver
【**注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!**】
```xml
```
CommonsMultipartFile 的 常用方法:
- **String getOriginalFilename():获取上传文件的原名**
- **InputStream getInputStream():获取文件流**
- **void transferTo(File dest):将上传文件保存到一个目录文件中**
我们去实际测试一下
3、编写前端页面
```html
```
4、**Controller**
```java
@RequestMapping("/upload")
public String upload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//获取文件名:file.getOriginalFilename();
String originalFilename = file.getOriginalFilename();
//如果文件名为空,直接返回首页
if((originalFilename).isEmpty()){
return "redirect:index.jsp";
}
System.out.println("上传文件名"+originalFilename);
//上传路径保存设置
String realPath = request.getServletContext().getRealPath("/upload");
//如果路径不存在,构建一个
File path = new File(realPath);
if (!path.exists()){
path.mkdir();
}
InputStream is = file.getInputStream();
FileOutputStream os = new FileOutputStream(new File(path, originalFilename));
//读取写出
int len = 0;
byte[] buffer = new byte[1024];
while ((len=is.read(buffer))!=-1){
os.write(buffer,0,len);
os.flush();
}
os.close();
is.close();
return "redirect:/index.jsp";
}
```
5、测试上传文件,OK!
**采用file.Transto 来保存上传的文件**
1、编写Controller
```java
/*
* 采用file.Transto 来保存上传的文件
*/
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
//上传文件地址
System.out.println("上传文件保存地址:"+realPath);
//通过CommonsMultipartFile的方法直接写文件(注意这个时候)
file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
return "redirect:/index.jsp";
}
```
2、前端表单提交地址修改
3、访问提交测试,OK!

> 文件下载
**文件下载步骤:**
1、设置 response 响应头
2、读取文件 -- InputStream
3、写出文件 -- OutputStream
4、执行操作
5、关闭流 (先开后关)
**代码实现:**
```java
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "1.png";//全局都是死代码,只需要更改这个文件名即可,以及上面存放图片的文件夹
//1、设置response 响应头
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8"); //字符编码
response.setContentType("multipart/form-data"); //二进制传输数据
//设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path,fileName);
//2、 读取文件--输入流
InputStream input=new FileInputStream(file);
//3、 写出文件--输出流
OutputStream out = response.getOutputStream();
byte[] buff =new byte[1024];
int index=0;
//4、执行 写出操作
while((index= input.read(buff))!= -1){
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return "ok";
}
```
前端
```html
点击下载
```
测试,文件下载OK,大家可以和我们之前学习的JavaWeb原生的方式对比一下,就可以知道这个便捷多了!
## 完结撒花