# 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的模式。** ![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7KwPOPWq00pMJiaK86lF6BjIXW7Wmm9KVEV1FXUfJMD0KzuYZ7ic5UHggsZDAzyYyrd4pLvnBIVM5zA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) ### 1.2、Model1时代 - 在web早期的开发中,通常采用的都是Model1。 - Model1中,主要分为两层,视图层和模型层。 ![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7KwPOPWq00pMJiaK86lF6BjIWe8RPcCUeexojBiaPtY7HibQonS3PdCy98oV24F0tYk8IxEUY43N93TA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) Model1优点:架构简单,比较适合小型项目开发; Model1缺点:JSP职责不单一,职责过重,不便于维护; ### 1.3、Model2时代 Model2把一个项目分成三部分,包括**视图、控制、模型。** ![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7KwPOPWq00pMJiaK86lF6BjICKszqy2wZemkK7XibCQwEn795uo9cRS7EQwjT8X7Gu2NuanIJfUQX6Q/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 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. ![image-20250331202606427](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250331202606427.png) 10. ![image-20250331202627346](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250331202627346.png) **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 基类)**。 ![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7KwPOPWq00pMJiaK86lF6BjI7ENu0jNibPiaiaiaBhyx6o9UUyU82Mddg4DjwzniaczmTLRbAtI9pKJq1tQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) SpringMVC的原理如下图所示: ​ 当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。 ![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7KwPOPWq00pMJiaK86lF6BjIaosVziclWLEJQkzobxHrpHcmtu2yTeVWPmEI4Yq5PaicS52VaJt8dYfQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) ### 2.3、SpringMVC执行原理 ![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7KwPOPWq00pMJiaK86lF6BjIbmPOkY8TxF6qvGAGXxC7dArYcr8uJlWoVC4aF4bfxgCGCD8sHg8mgw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 图为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. 最终视图呈现给用户。 ![image-20250403164323609](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250403164323609.png) 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包内有项目的全部依赖 ![image-20250403165353955](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250403165353955.png) 第三步:在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 ![image-20250403174734532](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250403174734532.png) 运行效果 ![image-20250403174748896](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250403174748896.png) 至此项目完成! ## 使用注解开发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} ``` 运行时报错: ![image-20250404001304269](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404001304269.png) **Unsupported class file major version 61** 经过查询发现是jdk版本过高,我用的是jdk17,将版本改到jdk11重启,将maven配置文件中的版本改为11即可 ![image-20250404001501736](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404001501736.png) 运行成功! 总结步骤: 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运行测试 ![image-20250404161805536](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404161805536.png) 小结: - 实现接口定义控制器有些过时了 - 缺点:一个控制器里只有一个方法,如果多个方法则需要定义多个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"; } ``` ![image-20250404163622036](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404163622036.png) 小结:从这里两次测试可以发现,我们的两个请求是可以指向一个视图的,但是页面显示的结果是不同的,表明这里的视图是被**复用**了,而控制器与视图之间是**弱耦合关系** ### 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"; } } ``` ![image-20250404165443667](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404165443667.png) ## 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.运行效果 ![image-20250404173229816](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404173229816.png) 使用路径变量的好处: - 是路径变的更加简洁高效且**安全**,**不会暴露参数** - 获得参数更加方便,框架会自动进行类型转换 - 通过路径变量的类型可以约束访问参数,如果请求类型不一样,则访问不到对应的请求方法,会**报错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"; } } ``` 直接运行结果: ![image-20250404175805795](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404175805795.png) 我们在写一个表单用post方法请求 a.jsp ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> Title
``` 测试:进入a.jsp提交表单 ![image-20250404180419300](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404180419300.png) ![image-20250404180423930](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250404180423930.png) 两次对比,我们发现两次测试的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、输入中文测试,发现乱码 ![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7IY4LDnTZkk5dLWKMlUlx0gqJOSOA4PQnmhg0rYQYexQXAvUWXvBRf8kN3hk6dDzHxC3w97QicX9XQ/640?wx_fmt=png&tp=wxpic&wxfrom=5&wx_lazy=1&wx_co=1) 不得不说,乱码问题是在我们开发中十分常见的问题,也是让我们程序猿比较头大的问题! 以前乱码问题通过过滤器解决 , 而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; } } ``` ![image-20250406145328921](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250406145328921.png) 【注意:使用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"}] } ``` ![image-20250406151711379](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250406151711379.png) #### 输出时间对象 ```Java @RequestMapping("/json3") public String json3() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); Date date = new Date(); return mapper.writeValueAsString(date); ``` ![image-20250406152126217](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250406152126217.png) 默认日期格式会变成一个数字,是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)); ``` 运行结果 : 成功的输出了时间! ![image-20250406152345238](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250406152345238.png) **解决方案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); ``` ![image-20250406152937806](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250406152937806.png) > 抽取为工具类 **如果要经常使用的话,这样是比较麻烦的,我们可以将这些代码封装到一个工具类中** ```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的请求很像,但不是,他还是在页面中去请求网站,并不是异步请求,局部刷新 ![image-20250408104823454](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250408104823454.png) > 真正的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} --%> <%--失去焦点的时候,发送一个请求(携带信息)到后台--%>

用户名:

``` ![image-20250408115745679](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250408115745679.png) 前后端分离示意图 ![image-20250408114607442](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250408114607442.png) 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; } ``` 效果展示: ![image-20250408162618440](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250408162618440.png) 成功实现了数据回显!可以体会一下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乱码问题】** 效果展示 ![image-20250408171240470](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250408171240470.png) ## 拦截器 > 概述 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时 ![image-20250408213327912](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250408213327912.png) 当返回值为false时 ![image-20250408213433853](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250408213433853.png) 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! ![image-20250409155640266](C:\Users\20893\AppData\Roaming\Typora\typora-user-images\image-20250409155640266.png) > 文件下载 **文件下载步骤:** 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原生的方式对比一下,就可以知道这个便捷多了! ## 完结撒花