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