Spring-MVC
一 . spring-MVC概述:
Spring Web MVC是最初建立在 Servlet API 之上的 Web 框架,从一开始就包含在Spring Framework中。正式名称Spring Web MVC来自其源模块的名称 ( spring-webmvc),但它更常被称为Spring MVC。
(1)MVC架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ddNJBdD-1681790466450)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666765771912.png)]
用户通过浏览器访问servlet:
servlet负责接收请求和处理请求(解析请求里的参数封装成具体的对象发送给模型层)
模型层拿到具体的数据,连接数据库对数据进行处理具体的业务,随后发送给控制层servlet。
servlet拿到后发送给视图层,视图层(jsp)根据数据进行展示显示,发送响应给浏览器的用户,随后用户在浏览器看到对应的显示画面。
(2)Spring MVC 的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mZaTFiFg-1681790466451)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666768841063.png)]
传统的模型层被拆分为了业务层(Service)和数据访问层(DAO,Data Access Object)。同时,在 Service层下可以通过 Spring 的声明式事务操作数据访问层。
(3)创建一个web工程:
project-structure—facets—点±—点Web,此时回自动生成。
点create artifacts,给它取个名叫app,点击apply应用,此时Web工程创建完毕:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ACfsezum-1681790466451)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666785068138.png)]
随后我们将这个Web工程部署上:
点击edit configuration,在里面找到Tomcat Server,点击Location。点击deployment,点+ ,点击artifact,就会自动将名叫app的Web工程部署上。点击应用apply,我们在server上修改此设置即可:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-978lHntX-1681790466451)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666785331532.png)]
最后pom.xml引入相关依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cgboy</groupId>
<artifactId>study-springmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--servlet api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!--编译插件-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<target>${maven.compiler.target}</target>
<source>${maven.compiler.source}</source>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
此时我们运行tomcat,运行成功后回弹出浏览器界面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZxJlXBu1-1681790466451)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666785812622.png)]
此时我们在web下建立一个index.jsp文件,在里面写入cc:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uTrD7juC-1681790466452)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666788540869.png)]
刷新资源再运行,此时跳转界面:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0AOvjkZT-1681790466452)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666788565553.png)]
我们使用注解的方式,不用xml和jsp的方式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ffduzHyA-1681790466452)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666789401381.png)]
重启服务器后浏览器跳出显示的url为默认的:http://localhost:8080/app/
此时我们加上hello:就会显示出我们在java写的内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n1NA0lmx-1681790466452)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666789354017.png)]
准备工作都准备好后,我们要引入spring的web相关依赖:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4B3ecAxv-1681790466453)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666789904369.png)]
配置web.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置一个ContextLoaderListener,他会在servlet容器启动时帮我们初始化spring容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--指定启动spring容器的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
<!--注册DispatcherServlet,这是springmvc的核心-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</init-param>
<!--加载时先启动-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--/ 匹配所有的请求;(不包括.jsp)-->
<!--/* 匹配所有的请求;(包括.jsp)-->
<!--所有的请求都会进入到:org.springframework.web.servlet.DispatcherServlet
中,也就是上面配置的那个servlet-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置app-context.xml文件:(其实就是个spring和springmvc共享的配置文件)
<?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" id="InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/page/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
(4)Model and View:数据和视图
数据和视图就可以形成一个展示的页面。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mChXfjoV-1681790466453)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666791135434.png)]
根据app-context.xml中的视图解析器,我们直接就可以在WEB-INF下建立一个page的包,然后在这个包里建立一个hello.jsp,在hello.jsp中我们写入数据:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rfDlG3Mu-1681790466453)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666790971629.png)]
这个el表达式写的msg的内容,就是一个数据,而整个hello.jsp就是一个视图。他们通过响应发送给浏览器后就能渲染出一个页面。(这部分都是解释Model and View)
我们创建一个类去编写数据和视图:
已知hello.jsp文件创建在WEB-INF下page包内:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vprLFByn-1681790466453)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666795033798.png)]
创建servlet实现类:这里的视图就是hello.jsp,数据就是msg,内部内容hello springmvc,对mv对象填充完毕后通过Model and View解析到对应的hello.jsp的内容中去。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0KV1h684-1681790466453)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666794459523.png)]
在app-config.xml中设置对应的bean:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YWY5IiD6-1681790466454)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666794750266.png)]
此时我们测试运行Tomcat报错,出现严重警告日志信息。
(5)通过日志去查找错误原因:
c盘—用户—CG—AppData—Local—jetBrains—IntelliJIdea2022.1—tomcat—找时间离我们操作最近的一个—logs—找时间离我们操作最近的一个叫localhost***.log文件,从里面查找到我们产生的错误原因,发现是我们的Web工程没有将依赖的jar引入。于是我们将其引入到工程中,重新部署tomcat:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kNcacSc-1681790466454)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666795716911.png)]
将原来的app删掉点击-号,重新构建一个点击+号。
然后我们运行tomcat,没有报错。跳出浏览器界面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u2PnVSUf-1681790466454)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666795816622.png)]
就是我们前面设置的初始界面,此时我们在url后面输入:hellomvc
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MuGEYOqZ-1681790466454)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666795859198.png)]
显示出hello.jsp的内容,这样写还是有问题,因为耦合性太高了。我们写的代码耦合到了spring中,而且如果我们要写多个相似内容时,我们就要多创建几个类,太繁琐了。
(6)@Controller
我们使用注解的方式:
修改app-config.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫包 -->
<context:component-scan base-package="com.cgboy"/>
<!-- 让Spring MVC不处理静态资源,负责静态资源也会走我们的前端控制器、试图解析器 -->
<mvc:default-servlet-handler />
<!-- 让springmvc自带的注解生效 -->
<mvc:annotation-driven />
<!-- 处理映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/page/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
还是曾经那个hello.sjp文件配置不变
对应类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jjb5zPde-1681790466454)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666797445226.png)]
加上这些注解后,我们运行tomcat显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eCF1VEmR-1681790466454)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666797465934.png)]
出现最开始我们设置的初始界面,我们加上url内容:test1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZJ03Pahw-1681790466455)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666797578156.png)]
或者test2:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMj9GzQI-1681790466455)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666797551839.png)]
这就意味着我们可在一个类中写多个视图和数据来渲染成一个界面,特别方便。
二 . 初识springmvc
1 . 组件说明:
DispatcherServlet:中央控制器,前端控制器
接收到前端的请求后,通过这个Servlet把所有请求拿过来,交给springmvc去处理。
handler:处理器,后端控制器
前端发送的请求,我们来处理其中业务,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。就比如我们在controller中写的每个处理的方法就是一个hander:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ctCAWKZ4-1681790466455)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666859291165.png)]
View:视图
就是上面我们说的Model and View里的视图层的hello.jsp。
HanderMapping:处理器映射器
映射关系,就是根据用户请求url来找到Hander(处理器)。pringmvc提供了不同的处理器映射器实现,如配置文件方式,实现接口方式,注解方式等。类似一个map存了很多数据,根据key(请求url)和value(请求url对应的hander)。
HandlAdapter:处理器适配器
负责调用具体的处理器,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。我们写的controller中的方法,将来就是会由处理器适配器调用。
ViewResolver:视图解析器
负责处理视图和数据,渲染成一个浏览器的页面。
2 . springmvc执行流程:(面试常问)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bzwhAF5W-1681790466455)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666860468794.png)]
用户发送请求,中央控制器接收到请求后,中央控制器通过处理器映射器根据请求的url匹配到对应的处理器执行器链(这个链包括多个拦截器(类似过滤器)和一个处理器),中央处理器拿到执行器链后首先会调用拦截器的所有的printHandler方法,之后使用处理器适配器调用具体的Handler生成一个Model And View。调用完具体的Handler之后,拦截器链会调用postHandle方法执行对应的结果。最后拿着Model And View丢给视图解析器进行渲染,视图解析器会将其渲染成我们要的页面。
其实这个处理过程简单一点回答总结如下:
- 通过url匹配一个过滤器链,其中包含多个过滤器和一个处理器
- 第一步调用拦截器的preHandle方法
- 第二步执行handler方法
- 第三部调用拦截器的postHandle方法
- 将结果给视图解析器进行处理
- 处理完成后调用afterCompletion
3 . 三个容器上下文:
Servlet上下文会保存key,一个key对应的value值为spring上下文。一个key对应soringmvc上下文,且spring上下文和springmvc上下文是父子关系,springmvc可以访问spring容器的对象,而spring不能访问springmvc容器的对象,途中的这两个springmvc上下文是相互独立的,不是同一个。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-71MGT6U9-1681790466455)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666862304968.png)]
(1)ServletContext:
对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是我们的ServletContext,其为后面的spring IoC容器提供一个宿主环境。ServletContext最基本的一个上下文。
(2)Spring上下文:
在web.xml的配置中,我们需要提供一个监听器ContextLoaderListener:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFBIGuhS-1681790466455)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666862588198.png)]
在web容器启动时,会触发容器初始化事件(servletcontext的初始化),此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,contextInitialized方法启动,使spring容器初始化。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cesR5zBN-1681790466456)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666863460796.png)]
(3)springmvc上下文:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ri4jUXRy-1681790466456)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666864687630.png)]
contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的springmvc的Servlet。这个servlet可以配置多个,通常只配置一个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。
DispatcherServlet在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。
初始化完毕后,spring以【“org.springframework.web.servlet.FrameworkServlet.CONTEXT”+Servlet名称】为Key,也将其存到ServletContext中,以便后续使用。(可理解为springmvc为spring的子类上下文)
注意:
springMVC容器只负责创建Controller对象,不会创建service和dao,并且他是一个子容器。而spring的容器只负责Service和dao对象以及声明式事务的管理,是一个父容器。子容器可以访问父容器的对象,而父容器看不见子容器的对象,这样各司其职。
三个容器的启动流程:
接收到的请求都会匹配到,然后根据需求创建Servlet容器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MaUoGzad-1681790466456)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666865432989.png)]
随后通过监听器监听到servlet容器启动后,初始化并启动spring容器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OggnG9jW-1681790466456)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666865486860.png)]
contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的springmvc的Servlet:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iwWDFjMF-1681790466456)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666865641783.png)]
三 . 更多细节核心技术
1 . 视图模型拆分案例:
已知hello.jsp文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-65ZdFPuz-1681790466456)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666873943054.png)]
handler方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XkMWiQli-1681790466457)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666873923698.png)]
运行后,浏览器默认显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HjcS6RH1-1681790466457)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666874076336.png)]
我们发送请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0jwoivCr-1681790466457)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666874099438.png)]
运行流程:
我们在浏览器输入url:http://localhost:8080/app/hello2 回车,发送请求给服务器,服务器的中央控制器收到请求后,通过处理器映射器拿到执行器链,根据执行器链通过处理器适配器找到我们的handler方法(hello方法),springmvc会自动给我们传入Model的参数,并调用hello方法,我们给hello.jsp文件的msg进行赋值,随后返回给视图层的hello.jsp文件,之后将视图发送给视图解析器进行渲染成我们需要的页面,发送给中央控制器,最后发送响应给浏览器显示出来。
加入重定向功能:
我们在handler方法中进行改动:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U6ux0mVS-1681790466457)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666875064202.png)]
运行tomcat后,我们在url后输入http://localhost:8080/app/hello2显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHx6mbqm-1681790466457)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666875134726.png)]
加入转发的功能:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nq1UfeCA-1681790466457)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666875365956.png)]
操作如上显示:报错404
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MoY6A6v3-1681790466458)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666875610344.png)]
这是因为转发是必须是视图层存在的对应的配置文件,且属性赋值是一一对应的,如果我们改成视图层存在的且属性赋值是一一对应的hello.jsp:重启tomcat:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VmEyllc6-1681790466458)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666876090042.png)]
注意:如果上面这种情况报错,显示404,我们需要明确它的所有路径,因为可能我们写了其他的@Controller导致转发时直接去其他的@Controller去找这个文件了,就会找不到:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nDO3P3fX-1681790466458)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666881886176.png)]
2 . RequestMapping和衍生注解:
(1)RequestMapping:
是个类级注解,也是方法级别的注解。
1、value, method;
- value: 指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
- method: 指定请求的method类型, GET、POST、PUT、DELETE等;
2、consumes,produces;
- consumes:指定处理中的请求的内容类型(Content-Type),例如application/json;
- produces:指定返回响应的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
3、params,headers;
- params: 指定request中必须包含某些参数值处理器才会继续执行。
- headers: 指定request中必须包含某些指定的header值处理器才会继续执行。
@RequestMapping(value = "add",method = RequestMethod.POST,
consumes = "application/json",produces = "text/plain",
headers = "name",params = {"age","times"}
)
@ResponseBody
public String add(Model model){
model.addAttribute("user","add user");
return "user";
}
@RequestMapping还有几个衍生注解,用来处理特定方法的请求:
@GetMapping("getOne")
public String getOne(){
return "user";
}
@PostMapping("insert")
public String insert(){
return "user";
}
@PutMapping("update")
public String update(){
return "user";
}
@DeleteMapping("delete")
public String delete(){
return "user";
}
源码中能看带GetMapping注解中有@RequestMapping作为元注解修饰:
@RequestMapping(
method = {RequestMethod.GET}
)
public @interface GetMapping {
3 . 获取请求参数的注解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WKJYJtsF-1681790466458)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666969953092.png)]
(1)@RequestParam:
获取请求里面的参数
首先我们发送请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WLqFuBx6-1681790466458)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666969324228.png)]
将请求里的user的参数传给username,将请求的password传给password
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1BAfruOP-1681790466458)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666969274189.png)]
测试显示出了:tom,12
由此我们可以知道:当请求的参数和我们hander方法中的参数的名字不一致时,我们可已使用这种解决,我们推荐平时常用这风格注解。
(2)@RequestHeader:
获取请求首部header的参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oIDkmp2M-1681790466459)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667098949271.png)]
发送请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9mHhN7LH-1681790466459)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666969887971.png)]
控制台输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qgNhmRn8-1681790466459)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666969904722.png)]
如果@RequestHeader()不写指定的信息,那么就会默认去参数里寻找。
(3)@CookieValue:
获取请求中cookie的值
接收到请求后我们创建cookie,设置值,我们调用handler方法创建cookie通过响应发送给前端。在之后前端再次发送请求时我们拿到cookie的值调用方法输出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4TxxNoAx-1681790466459)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666973618432.png)]
handler方法(user)接收到请求后会创建cookie,并跳转到user.jsp文件,user.jsp文件内部设置使用post请求填充表单去调用insertUser():
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4YJzCSqG-1681790466459)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666972744178.png)]
点击运行后,我们输入url:http://localhost:8080/app/user,此时跳转界面,我们检查也发现到了我们设置的cookie的值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rzcXDNgE-1681790466459)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666973436003.png)]
此时,我们输入内容并提交(发送请求给后台):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-imFIzs9a-1681790466460)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666972894562.png)]
后台接收后调用inserUser方法的内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t17URPfM-1681790466460)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666973671015.png)]
输出显示出cookie的值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nfngzMnt-1681790466460)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1666973782567.png)]
(4)小知识:required的使用:
当我们的设置了从请求中获取某参数,而请求中没有该参数时,会爆错:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BEVNxASN-1681790466460)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667014808001.png)]
我们设置了从请求获取age的参数,当请求没有时会爆错,我们可以使用required来解决:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p4jB6HBt-1681790466460)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667015279808.png)]
(5)@SessionAttribute:
从session拿属性值
设置session:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i4sTG3TM-1681790466460)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667016084809.png)]
在运行后,输入url:http://localhost:8080/app/user,发送请求给后台,后台接收到后,中央控制器根据处理器映射器找到执行器链,通过该链找到对应的处理器,在通过处理器适配器进行调用user方法,设置了cookie和session,随后跳转到user.jsp的视图上:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FL99isS4-1681790466461)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667016559840.png)]
再通过视图解析器渲染了登陆界面发送给中央控制器,中央控制器发送响应给前端,随后浏览器显示出了user.jsp中渲染的登录界面:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9HVCAK0-1681790466461)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667016342755.png)]
然后我们输入账户密码点击提交,因为user.jsp上制定了对表单操作会跳转到user/insertUse路径,此时url会跳转到调用insertUser上:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NqVrYCu8-1681790466461)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667016655212.png)]
发送请求给了后台,后台接收到后以上面相同的方式找到了对应的insertUser()并调用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xzZ2z9Jc-1681790466461)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667016508553.png)]
随后打印出对应的值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9PmRuDz-1681790466461)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667016531616.png)]
(6)RequestAttribute:
从请求拿属性值(从一个controller转发到另一个controller)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-akW2INXr-1681790466461)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667018340037.png)]
运行后,输入url:http://localhost:8080/app/user/goInsert,发送请求,后端接受后,调用goInsert方法,设置request的值,并请求转发到insertUser方法并调用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MQPWEW0r-1681790466461)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667018505502.png)]
控制台输出显示:因为有的值我们这里没有设置,所以默认为null:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pj0w4XYx-1681790466462)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667018545180.png)]
(7)@ModelAttribute:(用的不多)
设置一个时间在model上:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQJol51I-1681790466462)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667023385331.png)]
调用这个方法后转发到insertUser()并调用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XYO7mBsk-1681790466462)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667023537557.png)]
运行后,输入url:http://localhost:8080/app/user/goInsert,发送请求,后端接受后,调用goInsert方法,创建date,并请求转发到insertUser方法并调用输出:
因为有的值我们这里没有设置,所以默认为null:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3ClLrfy-1681790466462)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667023610210.png)]
特殊情况:@RequestMapping和@ModelAttribute同时放在一起:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7j17gnOZ-1681790466462)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667024401991.png)]
@ModelAttribute放在方法上:此时的返回的这个”ydlclass“不会走视图解析器,会把返回的结果放到@ModelAttribute中,key 为”name“,对应的value值为”ydlclass“。最后根据“model”去视图层找一个model.jsp的文件:(model.jsp路径:page包下的user包内)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5VX126VT-1681790466462)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667024497614.png)]
运行后测试,发送请求url:http://localhost:8080/app/user/model界面显示出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wsxAgOFA-1681790466463)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667024656594.png)]
(8)@SessionAttributes:
@SessionAttributes注解应用到Controller上面,可以将Model中的属性同步到session当中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G3VdD8TU-1681790466463)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667026208531.png)]
我们没有设置session的值,只是设置了model的值。
运行后,输入url:http://localhost:8080/app/user/goInsert,发送请求,后端接受后,调用goInsert方法,创建date,并请求转发到insertUser方法并调用输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azXxHixH-1681790466463)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667026273988.png)]
运行后,输入请求url:http://localhost:8080/app/user/goInsert,insertUser被调用,后台显示出:
model的值和session的值同步了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8YkwupY8-1681790466463)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667026405938.png)]
4 . 数组的传递:
(1)在请求体里边传值:
该hander方法被调用时传入一个数组,并遍历:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJwkVEZV-1681790466463)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667028006113.png)]
输入请求url:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-35SI0ICS-1681790466463)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667028091329.png)]
从请求中拿到三个id的参数的值1,2,3,后台接收q请求后,调用deleteByIds()输出遍历:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-14fWBiPD-1681790466463)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667028214583.png)]
还有另一种传递方式:
(2)在请求url上传递值:
以上d代码都不变,在请求url上写入对应参数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKWbgQdL-1681790466464)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667028352387.png)]
控制台输出结果[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FlbGgPYu-1681790466464)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667028214583.png)]
(3)在请求体内不同的传递值(重要):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KW5xtMAL-1681790466464)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667028443618.png)]
运行后,接收到请求调用对应方法,自己就会将这三个值转换成Long型,将值传入到数组中,也能拿到同样的值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x1BbHcku-1681790466464)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667028214583.png)]
5 . 复杂参数的传递:
创建一个类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AO0AEgW2-1681790466464)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667030986516.png)]
user.jsp:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YzrtaSr6-1681790466464)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667032115181.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e1YZ48ek-1681790466465)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667032180645.png)]
,程序运行后,输入url请求:http://localhost:8080/app/user,直接显示出user.jsp的页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ugjGkxLj-1681790466465)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667032239361.png)]
,我们在其中输入对应的值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y5kJl33Y-1681790466465)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667032871472.png)]
,提交发送请求,后台收到后,因为user.jsp指定到user/query,所以会去调用:query()方法:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XIQcHabV-1681790466465)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667032481390.png)]
拿到Queryvo的值并输出到控制台,随后将success.jsp的内容渲染成页面发送响应给前端:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZXBThgdt-1681790466465)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667032558972.png)]
前端显示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2sc1kuJw-1681790466465)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667032887346.png)]
最后我们查看控制台打印输出了对应的值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v2yNuIKZ-1681790466466)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667032923070.png)]
注意:这里我们不能加@RequestParam:会报错找不到值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFsYrLnx-1681790466466)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667033035047.png)]
因为因为会在请求中寻找一个叫queryVo的参数,是找不到的。
6 . VO,DTO,DO,PO
(1)VO:
视图对象(view object),用于展示层,把某个指定页面的所有数据封装起来。通常是用在controller上,用来接收数据。
(2)DTO:
DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
(3)DO:
DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
(4)PO:
PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqPPnAdf-1681790466466)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667033540378.png)]
7 . 设定字符集:
springmvc内置了一个统一的字符集处理过滤器,我们只要在web.xml中配置即可:
<!--过滤器,拦截所有的请求-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
8 . 返回json数据(序列化)
当前端需要一个user的集合,我们不能直接将后台java的那个集合传给它,我们要把它转换成一个字符序列(可被前端识别的字符序列)发送给前端,前端才能展示。
大致步骤如下:
- 将我们的对象转化为json字符串。
- 将返回的内容直接写入响应体,不走视图解析器。
- 然后将Content-Type设置application/json即可。
引入json依赖:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J50UPi7u-1681790466466)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667046150460.png)]
已知UserPo类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gti4NVlL-1681790466466)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667050088572.png)]
前端发送请求:
http://localhost:8080/app/user/getAll,后端接收到后,调用getAll()方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VEV3mBGt-1681790466467)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667050184269.png)]
设置好content-type的内容,将创建的UserPo对象存入集合中,并转换成json字符串通过响应发送到前端,前端因为content-type设置的能将json字符串转换成文本,所以在前端页面显示出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4dYh0ee9-1681790466467)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667050390764.png)]
但是这样好麻烦,我们可以修改一下getAll():
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g3oXy2EW-1681790466467)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667051166986.png)]
运行后前端显示报错[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xbQYjq02-1681790466467)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667051725537.png)]大致就是没有找到转化器将json字符串转化为原子类的集合的list。此时我们需要配置一个转化器:
这个转化器的作用就是:当不走视图解析器时,如果发现【返回值是一个对象】,就会自动将返回值转化为json字符序列
<mvc:annotation-driven >
<mvc:message-converters>
<bean id="fastjson" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!-- 这里顺序不能反,一定先写text/html,不然ie下会出现下载提示 -->
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
重启运行显示出了我们想要的内容:(出现这个界面是因为我们下载了json解析的相关插件)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5xRcKIFf-1681790466467)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667052171005.png)]
9 . jackson的配置:
功能跟json一样,帮我们序列化或反序列化,引入依赖:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GXkZtBGM-1681790466468)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667054930044.png)]
我们把前面json的配置注掉,在app-config.xml编写jackson配置文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WzhIehJC-1681790466468)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667057146713.png)]
创建CustomObjectMapper类,对序列化过程进行一些额外的配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gnSKwkxH-1681790466468)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667056628058.png)]
10 . 反序列化
上面8 . 9 都是使用序列化将数据转换成json字符串发送响应给前端接收,
反序列化:从请求中拿到json字符串转换成数据让后端接收。
首先我们要设置请求中传的数据是json:content-type的内容表示请求传的application/json
请求体内的内容是一个json字符串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MIIP3QmL-1681790466468)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667058592084.png)]
这个json字符串,后端必须用fastjson或者jackson去解析才能获取到内部具体的值。
已知我们此时已经配好了jackson的配置信息,我们运行程序后:
我们发送请求后,调用对应方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EmA6eDlA-1681790466468)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667058918978.png)]
控制台输出user的username和password都为null,说明是没有拿到请求发送的参数的值,此时我们需要@RequestBody注解来解决:
@RequestBody注解:前端用json发过来,使用此注解接收
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHIrQ5eL-1681790466468)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667059147390.png)]
我再次启动程序,发送相同请求,控制台输出显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-44F3Mh49-1681790466468)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667059252881.png)]
这是因为@RequestBody注解类似一个转换器,因为请求里的是user的属性值且为json字符串,先让jackson解析成数据,然后因为insert方法传入的参数是从请求里拿到一个UserVo对象,请求里只有解析了的userVo对象的属性值,通过@RequestBody这个转换器将其属性值赋值给UserVo对象,在传入insert方法内进行调用,所以才能拿到这个user的值
11 . 数据转化:
(1)全局日期转换:
就是项目整体都用整个日期格式
已知uservo类内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vkm9Zuk8-1681790466469)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667107515181.png)]
我们设置请求,在请求中设置了对应的属性值并发送:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rLUaVelC-1681790466469)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667107556021.png)]
后台接收请求后调用insert方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TyGb2kxr-1681790466469)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667108419325.png)]
显示报错:在请求中没有找到一个转化器去转化birthday对应的value值进行识别:2001-1-20
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPY2v1nj-1681790466469)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667107647066.png)]
所以我们需要一个转化器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jFi6bZXc-1681790466469)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667107905523.png)]
定义完成转化器后,我们还要把转化器注入容器中使用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tgx0SskT-1681790466469)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667108267764.png)]
注意,因为我们jackson配置中也有<mvc:annotation-driven >标签,所以我们应该把:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y9WF01Oq-1681790466470)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667108649048.png)]
删除转移到jackson配置的标签中,因为可能会导致两个相同标签的部分让系统默认调用了不是我们需要的:转移到这里,同时将conversionService注入:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CpIyDpaZ-1681790466470)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667108722341.png)]
最后我们再次运行,控制台输出了对应的内容完成了数据转化:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sh1cAeqx-1681790466470)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667108856018.png)]
在从请求拿到参数后,对应的value值是一个字符串,在进行对应的数值填充时发现value的key(birthday)是一个Date类型,此时系统会去寻找我们自定义的转化器去将字符串转换成一个Date对象,最后填充到user对象的birthday属性中。如果转换失败,则会报异常,且birthday的值因为找不到为null。
上述是一个全局的日期转换(就是项目整体都用整个日期格式)。而项目有的部分可能会用不同的日期格式,那么我们能不能单独为其设置一个相关的配置呢?
(2)@DateTimeFormat:
先注解掉conversionService的内容,不用全局的日期:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ePypzcpH-1681790466470)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667109997739.png)]
我们在uservo类上给birthday属性加上注解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X3XJzB5q-1681790466470)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667110348001.png)]
此时我们在运行发送请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DJh2SAAL-1681790466470)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667111312066.png)]
控制台输出显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UbYIebXy-1681790466471)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667111330663.png)]
这是因为给uservo的birthday属性加上注解@DateTimeFormat且规定了在拿到此参数的value值会自动按照:"yyyy-MM-dd"的日期格式进行转化最后传入birthday属性。
(3)@JsonFormat:
这是jackson当中的一个注解。当被注解属性需要被转换成json字符串时,会按照一下格式展现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D3MHoFzR-1681790466471)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667111884936.png)]
同理,当一个UserVo的json字符串需要转换成一个Uservo对象时也会按照上述格式进行显示。
12 . 数据转化总结:
Content-Type: application/x-www-form-urlencoded 代表我们是从请求参数中获取值
Content-Type: application/json 代表我们是从请求体获取json数据去获取值且要加@RequestBody
流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7TpXwgC-1681790466471)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667112898146.png)]
(1)从请求参数中获取:
是获取到的key-value形式的,拿到请求中的value值,然后UservO对象内部有username(String),password(String),birthday属性(Date类型),前两个拿到的请求的参数都是符合的String类型,而birthday拿到的时字符串类型,而birthday本身是Date,这时候就需要转化器根据borthday的Date类型将字符串进行转化成Date类型,转化成功后填充在UserVo中。
我们一般都会在最后设置一个全局的Convertor的时间日期的转化器作为一个兜底的设置,这样所有的时间都会转化成Date日期类型,但是也有问题:必须所有的时间都是一样的。
如果我们设置了全局的时间日期配置,又想对一个实例做一个单独的时间日期设置,那么就可以使用@DateTimeFormat来设置单独的日期设置。
(2)从请求体获取:
当我们我们的请求体内是一个json数据,我们通常是通过@RequestBody标注的参数,拿到对应的json字符串数据,通过例如jackson工具组件依托于@JsonForMat进行转化(根据有@JsonForMat字段的数据将对应的json数据转换成java数据),将json字符串转换为对应方法能识别的java数据。同理,因为我们content-type设置的是application/json,如果我们需要将处理了的数据发送响应,那么也要先将其转换成json字符串再发送,前端才能接收识别。
13 . 数据校验:
先引入依赖:(jdk11需要引入)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jnf03itu-1681790466471)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667118797471.png)]
- JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它包含在 JavaEE 6.0 中。
- JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。
| Constraint | 详细信息 |
|---|---|
@Null | 被注解的元素必须为 null |
@NotNull | 被注解的元素必须不为 null |
@AssertTrue | 被注解的元素必须为 true |
@AssertFalse | 被注解的元素必须为 false |
@Min(value) | 被注解的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注解的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注解的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注解的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注解的元素的大小必须在指定的范围内 |
@Digits (integer, fraction) | 被注解的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注解的元素必须是一个过去的日期 |
@Future | 被注解的元素必须是一个将来的日期 |
@Pattern(value) | 被注解的元素必须符合指定的正则表达式 |
Hibernate Validator 扩展注解
Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解
Hibernate Validator 附加的 constraint
| Constraint | 详细信息 |
|---|---|
@Email | 被注解的元素必须是电子邮箱地址 |
@Length | 被注解的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注解的字符串的必须非空 |
@Range | 被注解的元素必须在合适的范围内 |
例子:
对UserVo的属性进行校验:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k8DToQ81-1681790466471)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667119742455.png)]
加上这些注解后我们还要进行相关的配置app-config.xml:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QY8ukNj6-1681790466471)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667119664545.png)]
设置完成后,我们需要再后续对应方法涉及到该UserVo对象的参数上加上一个注解:
@Validated,
且还要紧跟一个BindingResult br 参数,将来校验发生错误时就放在BindingResult整个参数中。
要在数据校验发生错误时打印错误的日志,那么我们引入lobback日志:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IKCkvvfn-1681790466472)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667120447954.png)]
引入成功后,在Controller上加上注解:@Slf4j
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRF5GijN-1681790466472)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667120507818.png)]
后台接收到请求后,拿到对应的参数前进行数据校验,处理器调用insert方法,将user对象和数据校验产生的错误传入方法中,如果有数据校验的错误就通过日志打印,
如果有数据校验错误就请求转发到error.jsp:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8rhXGDlB-1681790466472)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667120669684.png)]
视图解析error.jsp后传给中央控制器,通过响应发送给前端显示,
如果没有数据校验错误就打印user的内容,随后请求转发到user.jsp,视图解析user.jsp后传给中央控制器,通过响应发送给前端显示,
已知我们userVo中有几个属性没有赋值,我们发送请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z5mc4iXr-1681790466472)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667122223299.png)]
随后控制台显示:userVo中有几个属性没有赋值所以为null,或者0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6KdSCqkG-1681790466473)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667122287678.png)]
14 . 视图解析器:
springmvc三大组件:处理器映射器,处理器适配器,视图解析器
如果我们想添加新的视图解析器,则需要给旧的新增一个order属性,或者直接删除原有的视图解析器:(只是演示,不真正添加)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MvWSqMpI-1681790466473)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667123214833.png)]
这里的order表示视图解析的【优先级】,数字越小优先级越大(即:0为优先级最高,所以优先进行处理视图),InternalResourceViewResolver在项目中的优先级一般要设置为最低,也就是order要最大。不然它会影响其他视图解析器。
当处理器返回逻辑视图时(也就是return “string”),要经过视图解析器链,如果前面的解析器能处理,就不会继续往下传播。如果不能处理就要沿着解析器链继续寻找,直到找到合适的视图解析器。
如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Al38Vk8-1681790470022)(null)]
(1)添加thymeleaf视图解析器
引入依赖:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSB1FmcD-1681790466473)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667123825218.png)]
,app-confi.xml配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nJKL096q-1681790466473)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667125264925.png)]
由于制定了html文件,所以我们创建一个index.html:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8CFRFaRo-1681790466474)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667125335247.png)]
创建一个对应controller类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F54ClQCO-1681790466474)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667125870045.png)]
运行后发送请求:http://localhost:8080/app/index
后台接收到请求后,中央控制器通过处理器映射器拿到处理器执行器链,通过该链找到对应hander方法:index(),之后调用该方法,将hello thymeleaf传到index.xml的msg当中,随后发送到视图层渲染后,发送响应到前端显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B32rCU9G-1681790466474)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667126073797.png)]
thymeleaf语法详解:thymeleaf官网:Thymeleafopen in new window
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XUwTt7fA-1681790466474)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667138523150.png)]
运行后发送那个请求:http://localhost:8080/app/index,则会显示出对应内容
15 . 全局异常捕获:
(1)HandlerExceptionResolver:
在Java中,对于异常的处理一般有两种方式:
-
一种是当前方法捕获处理(try-catch),这种处理方式会造成业务代码和异常处理代码的耦合。
-
另一种是自己不处理,而是抛给调用者处理(throws),调用者再抛给它的调用者,也就是一直向上抛,指导传递给浏览器。被异常填充的页面是长这个样子的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFDgcXrA-1681790470185)(null)]
在这种方法的基础上,衍生出了SpringMVC的异常处理机制。系统的dao、service、controller都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
小知识:service层尽量不要处理异常,如果自己捕获并处理了,异常就不生效了。特别是不要生吞异常一般controller,service,dao遇见异常都不会处理,他们会controller抛service,service抛给dao,dao在抛给service,service抛给controller,最后controller抛给中央处理器,统一由中央处理器将异常丢给HanderExecptionResolver去处理异常。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-88j7n7qe-1681790470288)(null)]
Spring MVC的Controller出现异常的默认处理是响应一个500状态码,再把错误信息显示在页面上,如果用户看到这样的页面,一定会觉得你这个网站太LOW了。
要解决Controller的异常问题,当然也不能在每个处理请求的方法中加上异常处理,那样太繁琐了。
通过源码我们得知,需要写一个HandlerExceptionResolver,并实现其方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MxrdCw1u-1681790466475)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667190217049.png)]
在app-config.xml创建该类的bean:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a1weoiaE-1681790466475)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667190460127.png)]
我们发送请求:http://localhost:8080/app/index,
后台收到后,去调用index()方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NhR61NSK-1681790466475)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667190293285.png)]已知Thymeleaf解析器已配置完成,在运行时1/0出现异常先抛出,到最后由异常解析器去处理,随后请求转发到error.html:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hy0jMh7n-1681790466475)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667190384262.png)]
视图解析器渲染后,中央控制器发送响应给前端显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QQVOX5sK-1681790466475)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667190416030.png)]
(2)ControllerAdive实现全局异常捕获:
使用此类型全局异常捕获,把HandlerExceptionResolver的xml的bean注掉:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cl4sjYjh-1681790466476)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667192063691.png)]
@ControllerAdvice注解:(常用)
加上这个注解就能有:对全局异常的处理,预设全局数据,请求参数预处理的功能。(后两个不重要)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxI9grTS-1681790466476)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667193196602.png)]
运行后,我们发送请求:http://localhost:8080/app/index
后台接收到后调用index()方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LUUE6lEO-1681790466476)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667193237448.png)]
1/0发生数学异常报错,检测到数学异常后跳转到GlobalExceptionResolverController(全局处理异常类)处理异常:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sRX7Uzlg-1681790466476)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667193386007.png)]
,因为是数学类异常,调用对应的处理数学类异常的方法:processArithmeticException():
随后打印错误日志信息,并跳转到error.html:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HISWqeJj-1681790466476)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667193635815.png)]
,视图解析器将error.jsp渲染发送到中央控制器,中央控制器将其通过相应发送到前端显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xIqpXGJd-1681790466476)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667193644909.png)]
改进:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UgOv9lRN-1681790466477)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667196469906.png)]
发送请求:http://localhost:8080/app/index
后台接收到后调用index()方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ioodZQAo-1681790466477)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667196500282.png)]
如果符合要求则抛出这个异常,随后调用处理这个异常的方法,在跳转到error.html,然后发送响应给前端:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vek8Qj6v-1681790466477)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667196564407.png)]
最后控制台输出库存预警的异常
16 . 处理静态资源:
已知我们在该路径下创建index.js:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IXUKoCSd-1681790466477)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667196892866.png)]
照常理来说,默认情况下不在WEB-INF路径,我们直接在请求输入目标文件的路径就能访问到:
http://localhost:8080/app/static/js/index.js,回车显示报错,404不存在。
这是因为后台把这个请求按照去controller相关内容去寻找并处理(我们把请求都交给了spring来管理了,当我们使用了springmvc后,所有的请求都会交给springmvc进行管理,当然也包括静态资源),那么肯定是访问不到index.js了,如果是让tomcat去处理这个请求,那么就一定能访问到。
我们添加一个配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGt2SpBX-1681790466477)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667198172176.png)]
此时我们重启,输入请求时我们只需要指定http://localhost:8080/app/js/index.js即可:
后台接收请求后,直接去js/index.js将其发送响应显示到前端:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HOJUAayE-1681790466477)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667198555755.png)]
17 . 拦截器:
SpringMVC提供的拦截器类似于JavaWeb中的过滤器,只不过SpringMVC拦截器只拦截被前端控制器拦截的请求,而过滤器拦截从前端发送的【任意】请求。
拦截器规则
我们可以配置多个拦截器,每个拦截器中都有三个方法。下面将总结多个拦截器中的方法执行规律。
- preHandle:Controller方法处理请求前执行,根据拦截器定义的顺序,正向执行。
- postHandle:Controller方法处理请求后执行,根据拦截器定义的顺序,逆向执行。需要所有的preHandle方法都返回true时才会调用。(postHandle如果出现异常后,postHandle不会执行)
- afterCompletion:View视图渲染后处理方法:根据拦截器定义的顺序,逆向执行。preHandle返回true也会调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kYY74rV2-1681790466478)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667201038298.png)]
发送请求后台接收请求后,中央控制器通过映射器拿到对应handle方法,再执行handle方法前会执行preHandle()方法,按照注册的先后顺序来一一执行(1,2, 3),而handle方法执行后开始执行postHandle(),按照逆序(先注册的后执行)来执行(3,2,1),发送给视图解析器后按照逆序(先注册的后执行)来执行afterCompletion(3,2,1)
执行顺序满足先进来的后出去。
自定义拦截器:
创建拦截器类:
public class LogingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("-----------pruHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("-----------postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("-----------afterHandle");
}
}
发送请求:http://localhost:8080/app/index,后台接收请求带哦用index方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-osWt1K3C-1681790466478)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667201596100.png)]
如果符合要求出现了异常则抛出异常,则调用全局异常处理中对应的异常处理方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jv5pW70L-1681790466478)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667201720309.png)]
,日志打印异常信息,并且视图解析器将视图层的error.html:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FRF6g7fA-1681790466478)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667201811824.png)]
解析完后发送给中央控制器,中央控制器将其发送响应到前端显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xFswtdns-1681790466478)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667201794774.png)]
我们查看控制台出现了:(乱码先不管他)handle方法执行前执行了prehandle,遇到异常后没有调用posthandle(),handle方法执行完后调用了afterCompletion()方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fraliIO5-1681790466478)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667201894791.png)]
如果没有异常则后台调用index方法将uservo的值传入到index.jsp:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N5ZCwKeP-1681790466479)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667226778747.png)]
前端也会显示index.html的内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6SnxumV9-1681790466479)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667202036784.png)]
控制台则:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q5jRh3Ip-1681790466479)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667202006182.png)]
前端显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wu3SfIUw-1681790466479)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667226804919.png)]
18 . 拦截器实现登录逻辑:
创建一个登录的controller类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ou4FOHSI-1681790466479)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667203760022.png)]
发送请求:http://localhost:8080/app/login,后台接收后调用toLogin(),该toLogin方法会跳转到login.html登陆页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JzwxgXka-1681790466479)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667203860703.png)]
视图解析后发送给中央控制器,中央控制器发送响应到前端显示登陆页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vyksvvMT-1681790466480)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667203887301.png)]
我们输入账户密码后点击登录,后台收到请求后会拿到username的值,调用相应的handler方法(login()),在调用前会执行preHandler(),而我们的登录逻辑就是写在preHandler()的,那么就可以完成判断了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aegy8UNy-1681790466480)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667204131816.png)]
如果判断我们请求中的username是否符合登录正确,如果不正确重定向到登陆页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jcBck6v8-1681790466480)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667204551577.png)]
正确返回登录成功页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yPIlsXE5-1681790466480)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667204197785.png)]
登录成功后我们才能做后续的操作
四 . restful
一种软件架构风格。
1 . Rest架构的主要原则:
网络上的所有事物都被抽象为资源
每个资源都一个唯一的资源标识符(uri)
同一个资源具有多种表现形式(xml,json等)
对资源的各种操作不会改变资源标识符(资源标识符不会发生变化,但是数据可能变化)
所有操作都是无状态的
符合Rest原则的架构成为restful
2 . 什么是restful:
est式的web服务是一种ROA(The Resource-Oriented Architecture)(面向资源的架构)。
在restful风格中,我们将互联网的资源抽象成资源,将获取资源的方式定义为方法,从此请求再也不止get和post了:
获取资源的请求都是用GET请求(如查询用户)
| 客户端请求 | 原来风格URL地址 | RESTful风格URL地址 |
|---|---|---|
| 查询所有用户 | /user/findAll | GET /user |
| 查询编号为1的用户 | /user/findById?id=1 | GET /user/1 |
| 新增一个用户 | /user/save | POST /user |
| 修改编号为1的用户 | /user/update | PUT /user/1 |
| 删除编号为1的用户 | /user/delete?id=1 | DELETE /user/1 |
(1)restful架构编写handler:
按照restful架构编写controller中的handler方法:
@ResopnseBody:
将java对象转换为json数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hRs3FGGC-1681790466480)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667211488515.png)]
@Controller
@RequestMapping("user")
public class GoodsController {
// /goods
@ResponseBody
@GetMapping()
public List<GoodsDo> getAll(){
//中间的过程省略:
return List.of();
}
// /goods/1
@ResponseBody
@GetMapping("{id}")
public GoodsDo getOneGoods(@PathVariable Integer id){
//中间的过程省略:
return new GoodsDo();
}
// /goods
@ResponseBody
@PostMapping()
public String insert(GoodsVo goodsVo){
//中间的过程省略:
return "操作成功";
}
// /goods
@ResponseBody
@PutMapping("{id}")
public String update(@PathVariable Integer id){
//中间的过程省略:
return "操作成功";
}
// /goods/1
@ResponseBody
@DeleteMapping("{id}")
public String delete(@PathVariable Integer id){
//中间的过程省略:
return "操作成功";
}
}
(2)复杂条件和统一结果返回:
我们最基本的查询修改删除只需要用类似/user/id的方式就能达到要求,但是如果有特殊的多条件查询比如分页,按照age和姓名都符合要求…等都需要在url上手动添加:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yc81uXeG-1681790466480)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667215620035.png)]
@RestController:
由于我们已经明确我们的controller是一个restful的架构的controller,所以我们可以使用这个注解:@RestController
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZNRsag0d-1681790466481)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667215812404.png)]
使用了这个注解也就代表我们的这个类是@Controller标注的,且它的所有handler方法都是被@ResponseBody标注的,所以我们的方法就不用加上@ResponseBody注解了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eWFnj3Tk-1681790466481)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667215987419.png)]
如果修改成复杂情况是:controller类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FpMApitG-1681790466481)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667226933467.png)]
已知对应的GoodsDo,GoodsVo类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-botWyRCb-1681790466481)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667226978197.png)]
操作后的状态工具类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dF7IrNgm-1681790466481)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667227060780.png)]
我们输入前端请求:http://localhost:8080/app/goods,会跳转到拦截器的登录界面(因为我们写了个登录拦截器):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5x6XG8jD-1681790466481)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667283203992.png)]
我们要登录成功后才能进行其他的处理请求的操作,否则一直跳转到登陆界面(通过设置session值来进行username的判断,相同则登录成功)。登录成功后,由于congtroller类中handler方法都没有特意标注执行哪个,所以默认执行第一个:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yo1GGBNm-1681790466482)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667227177538.png)]
操作完后返回操作状态显示到前端页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ePzs02BK-1681790466482)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667227167303.png)]
3 . 使用ajacx发送请求:
我们在app-config.xml配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W2RrmgqL-1681790466482)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667284791720.png)]
使用到最后到toGoods路劲的请求,会跳转到goods.html:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZnVqye3c-1681790466482)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667284874895.png)]
controller类:
我们测试看看能否拿到值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TneylW5m-1681790466482)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667285146039.png)]
运行,发出请求:http://127.0.0.1:8080/app/toGoods,会先被拦截器拦截进行登录验证,登录后哦我们在发出请求:http://127.0.0.1:8080/app/toGoods显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5BeT1KD-1681790466482)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667285040924.png)]
我们看后台控制台:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YpsK9Gvv-1681790466482)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667285237563.png)]
没有拿到值,说明前端传过来的数据后台没有接收到。
因为前端传送过来请求用的content-type:application/json
后台接收时使用的是content-type:application/x-www-form-urlencoded
此时我们可以使用@RequestBody在参数对象加上注解解决:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5oy0lUfJ-1681790466483)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667285623451.png)]
此时我们在运行,完成登陆验证后哦,在发送相同请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4x3ZnEgA-1681790466483)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667285834252.png)]
我们看后台发现拿到了对应的值:(乱码暂不考虑)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KhV27NeC-1681790466483)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667285897112.png)]
五 . 跨域:
单体项目 ,由上可知,我们在goods.html里面写了发送很多类型的请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0guL9aQh-1681790466483)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667290708110.png)]
我程序启动后,发送请求:http://127.0.0.1:8080/app/toGoods,后台会将这些请求通过响应发送到前端:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ATneGO7s-1681790466483)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667290801808.png)]
这种就是标准的单体项目。
前后分离:前端和后端各自是一个单独的工程。加入我们在vscode中写了一段前端代码,这段代码和我们idea的goods.html内容一模一样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sZXLUYUR-1681790466483)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667291083819.png)]
我们在浏览器发送请求一样能显示出来页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AbAXAzv1-1681790466484)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667291119157.png)]
但是index.html内部的哪些内容无法打开,无法发送到前端来,因为这些工程的基础内容是idea中设置的,只能通过idea来发送次啊能成功,你用其他的工具(vscode)想拿到这些内容肯定无法成功:因为你在127.0.0.1:5500端口下访问了index.htnl后,内部还有个要访问127.0.0.1:8080/app/(被拒绝了),然后就无法成功发送其内部的其他响应了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FNJ2LtsR-1681790466484)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667291289472.png)]
1、同源策略
同源()策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互(如上述5500域的脚本想要访问8080域的内容)。
所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同时,就会产生跨域。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8jUbV8yS-1681790471343)(null)]
举一个例子:从127.0.0.1:5000访问的页面中,有Javascript使用ajax访问127.0.0.1:8888的接口就会产生跨域;
| 当前页面url | 被请求页面url | 是否跨域 | 原因 |
|---|---|---|---|
| http://www.ydlclass.com/ | http://www.ydlclass.com/index.html | 不跨域 | 同源(协议、域名、端口号相同) |
| http://www.ydlclass.com/ | https://www.ydlclass.com/index.html | 跨域 | 协议不同(http/https) |
| http://www.ydlclass.com/ | http://www.baidu.com/ | 跨域 | 主域名不同(test/baidu) |
| http://www.ydlclass.com/ | http://blog.ydlclass.com/ | 跨域 | 子域名不同(www/blog) |
| http://www.ydlclass.com:8080/ | http://www.ydlclass.com:7001/ | 跨域 | 端口号不同(8080/7001) |
非同源限制
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。
- 无法接触非同源网页的 DOM
- 无法向非同源地址发送 AJAX 请求
2 . 两种请求方法:
前后端分离情况下,如果当某些情况一定要前端访问到后端的数据,那么就需要跨域:有两种请求方法:
全称是"跨域资源共享"(Cross-origin resource sharing);
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求:
(1) 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
(2)HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded、multipart/form-data、text/plain
这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是,只要表单可以发,AJAX 就可以直接发。
凡是不同时满足上面两个条件,就属于非简单请求。
浏览器对这两种请求的处理,是不一样的
(1)简单请求:(不用急记,了解即可)
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。
下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4V7xNc3Y-1681790466484)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667292599493.png)]
由图。来自api.bobo.com要访问api.ydlclass.cpm,显然时跨域无法访问的。前端能否访问到后端的资源,是取决于后端愿不愿意让前端访问:
如后端在响应头添加了允许该域能访问,那么api.bob.com就可以访问后端的资源了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YmxiIMe-1681790466484)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667293110154.png)]
上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。
(1)Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
(2)Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
(3)Access-Control-Expose-Headers
该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
(4)withCredentials 属性
上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。
Access-Control-Allow-Credentials: true
另一方面,开发者必须在AJAX请求中打开withCredentials属性。
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials。
xhr.withCredentials = false;
需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie
(2)非简单请求:
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。OPTIONS
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
下面是一段浏览器的JavaScript脚本。
var url = 'http://api.ydlclass.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
上面代码中,HTTP请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header。
浏览器发现,这是一个非简单请求,就【自动】发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.ydlclass.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
除了Origin字段,"预检"请求的头信息包括两个特殊字段。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。
(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。
预检请求的响应
服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
Access-Control-Allow-Origin: *
如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。
XMLHttpRequest cannot load http://api.ydlclass.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
服务器回应的其他CORS相关字段如下。
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
(1)Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
(2)Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
(3)Access-Control-Allow-Credentials
该字段与简单请求时的含义相同。
(4)Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求
3 . 跨域代码实战:
如上这种情况:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wUqyIypW-1681790466484)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667295684972.png)]
@CrossOrigin:跨域
类级注解(如果在类上加,那么该类所有方法都视作加了@CrossOrigin注解),它也是方法级别的注解(如果不想一个类所有的方法都是@CrossOrigin注解,只需要在目标方法加上该注解即可)
我们在ideajava代码的controller加上注解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DgCXRAql-1681790466485)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667296610970.png)]
我们允许5500域和5000域能访问。随后我们打开vscode的index.html(和idea的goods.html内容一样):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-avf1hzZ2-1681790466485)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667296658847.png)]
启动运行后发出请求url:
http://127.0.0.1:5500/login.html会跳转到前端页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-euPHC288-1681790466485)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667301369581.png)]
点击刷新:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X2ITVJ8V-1681790466485)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667312082476.png)]
4 . 使用配置类:
我们的著配置文件app-config.xml是可以通过配置类的方式来进行配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2QjBQt4Y-1681790466485)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667370959137.png)]
5 . 文件上传:
文件上传对前端表单有如下要求:为了能上传文件,必须将表单的【method设置为POST】,并将enctype设置为【multipart/form-data】。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。
引入依赖:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKE5oXNA-1681790466485)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667376683982.png)]
主xml配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mjv2ouV1-1681790466486)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667376741090.png)]
controller类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hDZwgPNQ-1681790466486)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667376767558.png)]
前端发送请求:http://127.0.0.1:8080/app/toUpload,后端接收请求后,页面跳转到upload.xml:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6UjIi75S-1681790466486)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667376872625.png)]
并发送响应到前端显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lXVZeG1v-1681790466486)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667376887735.png)]
我们将桌面的一个图片文件传入,并上传。后端接收到请求后,去调用controller方法upload(),随后将该图片文件上传到D:/img文件中
,随后返回成功界面到前端:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a3jndK2v-1681790466486)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667376810216.png)]
此时我们打开D:img我呢见发现了上传的图片文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EqHBtDN8-1681790466486)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667377042885.png)]
6 . 下载文件:
@GetMapping("/download1")
@ResponseBody
public R download1(HttpServletResponse response){
FileInputStream fileInputStream = null;
ServletOutputStream outputStream = null;
try {
// 这个文件名是前端传给你的要下载的图片的id
// 然后根据id去数据库查询出对应的文件的相关信息,包括url,文件名等
String fileName = "cgboy.jpg";
//1、设置response 响应头,处理中文名字乱码问题
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8"); //字符编码
response.setContentType("multipart/form-data"); //二进制传输数据
//设置响应头,就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名。
//Content-Disposition属性有两种类型:inline 和 attachment
//inline :将文件内容直接显示在页面
//attachment:弹出对话框让用户下载具体例子:
//URLEncoder.encode:防止改文件名是中文导致乱码,使用utf-8解决
response.setHeader("Content-Disposition",
"attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
// 通过url获取文件
File file = new File("D:/img/"+fileName);
fileInputStream = new FileInputStream(file);
outputStream = response.getOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) != -1){
outputStream.write(buffer,0,len);
outputStream.flush();
}
return R.success();
} catch (IOException e) {
e.printStackTrace();
return R.fail();
}finally {
if( fileInputStream != null ){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if( outputStream != null ){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
已知我们在D盘img下有个叫cgboy.jpg的图片文件,
运行后,我们发出请求:http://127.0.0.1:8080/app/download1,后台接收后会弹出窗口让我们指定下载地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OGPVfvmR-1681790466486)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667481474477.png)]
使用ResponEntity下载文件:
@GetMapping("/download2")
public ResponseEntity<byte[]> download2(){
try {
String fileName = "楠老师.jpg";
byte[] bytes = FileUtils.readFileToByteArray(new File("D:/upload/"+fileName));
HttpHeaders headers=new HttpHeaders();
// Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名。
headers.set("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));
headers.set("charsetEncoding","utf-8");
headers.set("content-type","multipart/form-data");
ResponseEntity<byte[]> entity=new ResponseEntity<>(bytes,headers, HttpStatus.OK);
return entity;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
运行后输入指令:http://127.0.0.1:8080/app/download1,与上面的情况相同。
六 . WebSocket:
1 . WebSocket 简介
WebSocket 协议提供了一种标准化方式,可通过单个 TCP 连接在客户端和服务器之间建立全双工、双向通信通道。它是与 HTTP 不同的 TCP 协议(三次握手建立连接),但旨在通过 HTTP 工作,使用端口 80 和 443。
WebSocket 交互以 HTTP 请求开始,HTTP请求中包含Upgrade: websocket 时,会切换到 WebSocket 协议。以下示例显示了这样的交互:
GET /spring-websocket-portfolio/portfolio HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080
成功握手后,HTTP 升级请求底层的 TCP 套接字保持打开状态,客户端和服务器都可以继续发送和接收消息。
#(1)HTTP 与 WebSocket
平时我们http请求发送一个请求,后台给一个响应然后连接断开了(短链接)。WebSocket是客户端和服务端建立一个tcp连接(长连接),只要不主动断开,这个连接就已知存在,就可以一直发请求回馈响应。
短轮询:
最简单的一种方式,就是你用JS写个死循环(setInterval),不停的去请求服务器中的库存量是多少,然后刷新到这个页面当中,这其实就是所谓的短轮询。
这种方式有明显的坏处,那就是你很浪费服务器和客户端的资源。客户端还好点,现在PC机配置高了,你不停的请求还不至于把用户的电脑整死,但是服务器就很蛋疼了。如果有1000个人停留在某个商品详情页面,那就是说会有1000个客户端不停的去请求服务器获取库存量,这显然是不合理的。
长轮询:
长轮询这个时候就出现了,其实长轮询和短轮询最大的区别是,短轮询去服务端查询的时候,不管库存量有没有变化,服务器就立即返回结果了。而长轮询则不是,在长轮询中,服务器如果检测到库存量没有变化的话,将会把当前请求挂起一段时间(这个时间也叫作超时时间,一般是几十秒)。在这个时间里,服务器会去检测库存量有没有变化,检测到变化就立即返回,否则就一直等到超时为止。
而对于客户端来说,不管是长轮询还是短轮询,客户端的动作都是一样的,就是不停的去请求,不同的是服务端,短轮询情况下服务端每次请求不管有没有变化都会立即返回结果,而长轮询情况下,如果有变化才会立即返回结果,而没有变化的话,则不会再立即给客户端返回结果,直到超时为止。
这样一来,客户端的请求次数将会大量减少(这也就意味着节省了网络流量,毕竟每次发请求,都会占用客户端的上传流量和服务端的下载流量),而且也解决了服务端一直疲于接受请求的窘境。
但是长轮询也是有坏处的,因为把请求挂起同样会导致资源的浪费,假设还是1000个人停留在某个商品详情页面,那就很有可能服务器这边挂着1000个线程,在不停检测库存量,这依然是有问题的。
#(2)何时使用 WebSocket
短轮询:
最简单的一种方式,就是你用JS写个死循环(setInterval),不停的去请求服务器中的库存量是多少,然后刷新到这个页面当中,这其实就是所谓的短轮询。
这种方式有明显的坏处,那就是你很浪费服务器和客户端的资源。客户端还好点,现在PC机配置高了,你不停的请求还不至于把用户的电脑整死,但是服务器就很蛋疼了。如果有1000个人停留在某个商品详情页面,那就是说会有1000个客户端不停的去请求服务器获取库存量,这显然是不合理的。
长轮询:
长轮询这个时候就出现了,其实长轮询和短轮询最大的区别是,短轮询去服务端查询的时候,不管库存量有没有变化,服务器就立即返回结果了。而长轮询则不是,在长轮询中,服务器如果检测到库存量没有变化的话,将会把当前请求挂起一段时间(这个时间也叫作超时时间,一般是几十秒)。在这个时间里,服务器会去检测库存量有没有变化,检测到变化就立即返回,否则就一直等到超时为止。
而对于客户端来说,不管是长轮询还是短轮询,客户端的动作都是一样的,就是不停的去请求,不同的是服务端,短轮询情况下服务端每次请求不管有没有变化都会立即返回结果,而长轮询情况下,如果有变化才会立即返回结果,而没有变化的话,则不会再立即给客户端返回结果,直到超时为止。
这样一来,客户端的请求次数将会大量减少(这也就意味着节省了网络流量,毕竟每次发请求,都会占用客户端的上传流量和服务端的下载流量),而且也解决了服务端一直疲于接受请求的窘境。
但是长轮询也是有坏处的,因为把请求挂起同样会导致资源的浪费,假设还是1000个人停留在某个商品详情页面,那就很有可能服务器这边挂着1000个线程,在不停检测库存量,这依然是有问题的。
#(2)何时使用 WebSocket
WebSockets 可以使网页具有动态性和交互性。但是,在许多情况下,Ajax 和 HTTP 长轮询的组合可以提供简单有效的解决方案。
例如,新闻、邮件和社交提要需要动态更新,但每隔几分钟更新一次可能也完全没问题。另一方面,协作、游戏和金融应用程序需要更接近实时。
延迟本身并不是决定因素。如果消息量相对较低(例如监控网络故障),HTTP轮询可以提供有效的解决方案。低延迟、高频率和高容量的组合是使用 WebSocket 的最佳案例。
2 . WebSocket实战:
引入依赖:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5hocfwqo-1681790466487)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667532209208.png)]
创建类:
@Slf4j
public class MessageHandler extends TextWebSocketHandler {
private Map<String,WebSocketSession> sessions = new ConcurrentHashMap<>(8);
//三次握手成功后,连接建立回调
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//统一保存session
sessions.put(session.getId(),session);
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
super.handleMessage(session, message);
}
//方法本身会在前端发送消息之后主动调用
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
//前端发过来的消息信息都保存在message中
String payload = message.getPayload();
//打印前端发来的消息:
log.info("前端发来消息:{}",payload);
//如果要主动给前端发送消息,需要调用此方法:
//接收到前端消息后,发送消息告诉前端收到了消息
session.sendMessage(new TextMessage("您的消息: ["+payload+"],已经接收到"
+ LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"))));
}
//连接关闭时,清除session
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session.getId());
}
}
配置类:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
//注册WebSocket
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler( new MessageHandler(),"/message")
.addInterceptors(new HttpSessionHandshakeInterceptor())//拦截器
.setAllowedOrigins("*"); //允许跨域访问
}
}
著配置文件页面跳转设置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2XSUQKkc-1681790466487)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667535522665.png)]
前端页面xml配置:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>websocket调试页面</title>
</head>
<body>
<div style="float: left; padding: 20px">
<!--和哪个连接建立的WebSocket连接-->
<strong>url:</strong> <br />
<input type="text" id="serverUrl" size="35" value="" /> <br />
<button onclick="connect()">connect</button>
<button onclick="wsclose()">disConnect</button>
<br /> <strong>message:</strong> <br /> <input id="txtMsg" type="text" size="50" />
<br />
<button onclick="sendEvent()">发送</button>
</div>
<div style="float: left; margin-left: 20px; padding-left: 20px; width: 350px; border-left: solid 1px #cccccc;"> <strong>消息记录</strong>
<div style="border: solid 1px #999999;border-top-color: #CCCCCC;border-left-color: #CCCCCC; padding: 5px;width: 100%;height: 172px;overflow-y: scroll;" id="echo-log"></div>
<button onclick="clearLog()" style="position: relative; top: 3px;">清除消息</button>
</div>
</div>
</body>
<!-- 下面是h5原生websocket js写法 -->
<script type="text/javascript">
let output ;
let websocket;
function connect(){ //初始化连接
output = document.getElementById("echo-log")
let inputNode = document.getElementById("serverUrl");
let wsUri = inputNode.value;
try{
websocket = new WebSocket(wsUri);
}catch(ex){
console.log(ex)
alert("对不起websocket连接异常")
}
connecting();
window.addEventListener("load", connecting, false);
}
function connecting()
{
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
}
function sendEvent(){
let msg = document.getElementById("txtMsg").value
doSend(msg);
}
//连接上事件
function onOpen(evt)
{
writeToScreen("CONNECTED");
doSend("WebSocket 已经连接成功!");
}
//关闭事件
function onClose(evt)
{
writeToScreen("连接已经断开!");
}
//后端推送事件
function onMessage(evt)
{
writeToScreen('<span style="color: blue;">服务器: ' + evt.data+'</span>');
}
function onError(evt)
{
writeToScreen('<span style="color: red;">异常信息:</span> ' + evt.data);
}
function doSend(message)
{
writeToScreen("客户端A: " + message);
websocket.send(message);
}
//清除div的内容
function clearLog(){
output.innerHTML = "";
}
//浏览器主动断开连接
function wsclose(){
websocket.close();
}
function writeToScreen(message)
{
let pre = document.createElement("p");
pre.innerHTML = message;
output.appendChild(pre);
}
</script>
</html>
运行后我们在浏览器输入请求:http://127.0.0.1:8080/app/websocket,页面显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lDCMlUmN-1681790466487)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667535598158.png)]
url:要连接的url进行连接
message:消息内容
七. ssm整合:
引入依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cgboy</groupId>
<artifactId>ssm-study</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--servlet api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!--springmvc的依赖,会自动传递spring的其他依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<!--jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
<!--数据校验-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.9.Final</version>
</dependency>
<!--logback日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.1</version>
</dependency>
<!--thymeleaf和spring的整合-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.14.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring4 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.14.RELEASE</version>
</dependency>
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<!-- mybatis 相关 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 数据库连接驱动 相关 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 提供了对JDBC操作的完整封装 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!-- 织入 相关 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!-- spring,mybatis整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!-- 集成德鲁伊使用 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</project>
application配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hoHU8O2V-1681790466487)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667486941937.png)]
<?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.cgboy"/>
<!-- 让Spring MVC不处理静态资源,负责静态资源也会走我们的前端控制器、试图解析器 -->
<mvc:default-servlet-handler />
<!-- 处理映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/page/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
<property name="order" value="10" />
</bean>
<!--thymeleaf的视图解析器-->
<bean id="templateResolver"
class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="classpath:/templates/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML" />
<property name="cacheable" value="true" />
</bean>
<!--thymeleaf的模板引擎配置-->
<bean id="templateEngine"
class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
<property name="enableSpringELCompiler" value="true" />
</bean>
<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine" ref="templateEngine"/>
</bean>
<bean id="customObjectMapper" class="com.cgboy.configration.CustomObjectMapper"/>
<bean id="localValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>
<!--作全局异常处理-->
<!-- <bean class="com.cgboy.handler.GlobalExceptionResolver"/>-->
<!--注册注解驱动,jackson消息转化器-->
<mvc:annotation-driven validator="localValidator" >
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<!-- 自定义Jackson的objectMapper -->
<property name="objectMapper" ref="customObjectMapper" />
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--处理静态资源
只要访问js就会自动去本地文件/static/js/路径找
其他的css,img类型的同理,就不会去走我们controller去找了
-->
<mvc:resources mapping="/js/**" location="/static/js/"/>
<mvc:resources mapping="/css/**" location="/static/css/"/>
<mvc:resources mapping="/img/**" location="/static/image/"/>
<!--配置页面跳转-->
<mvc:view-controller path="/index" view-name="index"/>
<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,单位为字节(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
</beans>
index.xml配置(引入thymeleaf命名空间):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HQ9nwWDy-1681790466487)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667487002280.png)]
web.xml配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5ppqpon-1681790466488)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667487030452.png)]
序列化相关的设置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-POmplsus-1681790466488)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1667487203803.png)]
LCompiler" value=“true” />
<bean id="customObjectMapper" class="com.cgboy.configration.CustomObjectMapper"/>
<bean id="localValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>
<!--作全局异常处理-->
<!-- <bean class="com.cgboy.handler.GlobalExceptionResolver"/>-->
<!--注册注解驱动,jackson消息转化器-->
<mvc:annotation-driven validator="localValidator" >
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<!-- 自定义Jackson的objectMapper -->
<property name="objectMapper" ref="customObjectMapper" />
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--处理静态资源
只要访问js就会自动去本地文件/static/js/路径找
其他的css,img类型的同理,就不会去走我们controller去找了
-->
<mvc:resources mapping="/js/**" location="/static/js/"/>
<mvc:resources mapping="/css/**" location="/static/css/"/>
<mvc:resources mapping="/img/**" location="/static/image/"/>
<!--配置页面跳转-->
<mvc:view-controller path="/index" view-name="index"/>
<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,单位为字节(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
```
index.xml配置(引入thymeleaf命名空间):
[外链图片转存中…(img-HQ9nwWDy-1681790466487)]
web.xml配置:
[外链图片转存中…(img-K5ppqpon-1681790466488)]
序列化相关的设置:
[外链图片转存中…(img-POmplsus-1681790466488)]
本文详细介绍了Spring-MVC框架的各个方面,包括MVC架构、Spring MVC的组件、创建Web工程、Model和View的概念、错误处理、@Controller注解的使用、视图解析器、数据和视图的结合、Spring MVC执行流程、三个容器上下文(ServletContext、Spring上下文、SpringMVC上下文)以及相关配置。文章还深入探讨了如何处理请求参数、数据转换、异常处理、视图解析、静态资源处理、拦截器、WebSocket的使用场景等。最后,文章介绍了SSM整合的步骤和配置。
5277

被折叠的 条评论
为什么被折叠?



