MVC思想
MVC是一种软件架构的思想
M:Model,模型层,指工程中的JavaBean,作用是处理数据
V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器
SpringMVC
SpringMVC是Spring的一个后续产品,是Spring的一个子项目。
SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案,(表述层=页面+控制层)
SpringMVC的特点:
1.Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
2.基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
比如:Strust1封装了servlet,Strust2封装过滤器,都是apache产品
Strust1和Strust2俩个根本不是同一个产品。
SpringMVC入门案例
框架本质:jar包+配置文件
1.创建maven的web工程,并配置好tomcat,方式如下:
22-06-02 javaweb(13) 西安 maven、项目构建和依赖管理_£小羽毛的博客-CSDN博客
2.引入依赖
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
3.web.xml注册SpringMVC的前端控制器DispatcherServlet
DispatcherServlet是SpringMVC所提供的前端控制器,对浏览器发送的请求统一进行处理
<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<!--
设置springMVC的核心控制器所能处理的请求的请求路径
/所匹配的请求可以是/login或.html或.js或.css方式的请求路径
但是/不能匹配.jsp请求路径的请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
问题1: <url-pattern>/</url-pattern>, 这里为什么不是"/*" ?
/不包括.jsp /* 包括.jsp的访问。
写/,jsp被JspServlet处理,其他请求DispatcherServlet处理,合情合理
JspServlet:处理访问jsp的请求,会把jsp翻译成所对应的servlet
问题2:在下面这个web.xml中有一个配置,DefaultServlet用来处理静态资源,它的 url-pattern 配置也是“/”
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
这时候冲突了就近原则,
后面会再去解决处理静态资源的问题。
4.WEB-INF下创建springMVC-servlet.xml
为什么是这个名字呢?
固定位置,固定名称。位置是WEB-INF下,名称是 "<servlet-name>的值"-servlet.xmlspringmvc配置文件在DispatcherServlet初始化时加载,获取ioc容器
springMVC-servlet.xml文件内容是:
扫描控制层组件:component-scan
配置Thymeleaf视图解析器,解析Thymeleaf视图。
<?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"
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">
<!-- 自动扫描包 -->
<context:component-scan base-package="com.atguigu.controller"/>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
</beans>
5.创建com.atguigu.controller.HelloController
标识@Controller,SpringMVC才能够识别控制器的存在
@Controller
public class HelloController {
@RequestMapping("/")
public String index() {
//设置逻辑视图
return "index";
}
@RequestMapping("/hello")
public String HelloWorld() {
return "success";
}
}
6.创建templates目录,index.html移进到该目录,之所以是templates,因为我们在springMVC-servlet.xml中的视图前缀的配置是这样的!
index.html内容如下:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>index.html</h1> <a th:href="@{/hello}">hello</a> </body> </html>
success.html内容如下:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>success</h1> </body> </html>
启动项目后,自动跳转到首页
点击超链接"hello",跳转到如下页面
来,分析一波:
核心:将浏览器发送的请求和控制器方法所标识的注解@RequestMapping进行匹配
@RequestMapping("/hello") public String HelloWorld() { return "success"; }
若匹配成功,@RequestMapping注解所标识的方法就是处理请求的方法
在控制器方法中处理请求之后,需要返回一个字符串,该字符串就是逻辑视图
该视图会被配置文件中视图解析器解析,拼接前缀和后缀,结果Thymeleaf渲染响应浏览器
web.xml优化配置:
问题1:为什么第一次访问那么慢?,因为做的事实在太多了。springmvc的配置文件在DispatcherServlet初始化时加载
解决办法:将servlet的初始化时间提前到服务器启动时
<load-on-startup>1</load-on-startup>
问题2:我们的配置文件都在resources下,怎么把springmvc的配置文件也放在resources下?
解决办法:设置springmvc配置文件自定义的位置和名称,注意标签的设置顺序
<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 --> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 --> <init-param> <!-- contextConfigLocation为固定值 --> <param-name>contextConfigLocation</param-name> <!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的src/main/resources --> <param-value>classpath:springMVC.xml</param-value> </init-param> <!-- 作为框架的核心组件,在启动过程中有大量的初始化操作要做 而这些操作放在第一次请求时才执行会严重影响访问速度 因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时 --> <load-on-startup>1</load-on-startup> </servlet>
@RequestMapping注解
1.@RequestMapping作用:处理请求和控制器方法之间的映射关系
若请求的信息和注解所配置的信息能够匹配成功,那么注解所标识的方法就是处理该请求的方法
2、@RequestMapping所标识的位置
标识在类上:设置所匹配请求的初始信息
标识在方法上:设置所匹配请求的具体信息
先看一种情况,俩个Controller处理的请求就一样,俩个@RequestMapping("/hello"),这种情况就很常见,怎么区分呢?
@Controller
public class RequestMappingController {
@RequestMapping("/hello")
public String HelloWorld() {
return "success";
}
}
@Controller
public class HelloController {
@RequestMapping("/hello")
public String HelloWorld() {
return "success";
}
}
启动报错:
我们在其中一个controller上加一个@RequestMapping就好了。
标识在类上:匹配的请求的初始信息
方法:具体信息
@Controller
@RequestMapping("/test")
public class RequestMappingController {
@RequestMapping("/hello")
public String HelloWorld() {
return "success";
}
}
这样以后再去访问就得加test了
@RequestMapping属性
设置的@RequestMapping属性越多,匹配的请求越精确
1、@RequestMapping注解的value属性
通过请求路径将请求和控制器方法创建映射关系
* value属性是字符串数组类型,浏览器发送的请求的请求路径和数组中的任何一个路径匹配成功,就可以通过该方法处理请求, 即浏览器发送的多个请求都可以被同一个控制器方法处理
* 若请求的请求路径和控制器中任何一个@RequestMapping注解的value属性都不匹配,此时页面显示404
@RequestMapping(value = {"/hello", "/abc"})
public String HelloWorld() {
return "success";
}
2、@RequestMapping注解method属性
在value属性匹配请求路径的基础上,再通过请求方式去匹配
* method属性是一个RequestMethod类型的数组,RequestMethod是枚举类
* 若当前请求的请求路径匹配成功,但是请求的请求方式不是@RequestMapping所支持的请求方式则抛出异常405,Request method "XXX" not supported(请求方式不被允许)
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
@RequestMapping(value = {"/hello", "/abc"},method = RequestMethod.GET)
public String HelloWorld() {
return "success";
}
@RequestMapping派生注解:
- 处理get请求的映射-->@GetMapping
- 处理post请求的映射-->@PostMapping
3、@RequestMapping注解的params属性
params属性通过请求的请求参数匹配请求映射
params属性要求所匹配的请求必须携带或者不携带某些请求参数
params属性是一个字符串类型的数组(数组里面的表达式必须都匹配)
params属性的值可以是四种表达式:
"param":表示所匹配请求必须携带param请求参数
"!param":表示所匹配请求不能携带param请求参数
"param=value":表示所匹配请求必须携带param请求参数,且值必须为value
"param!=value":表示所匹配请求携带param请求参数时(不是必须携带),值不能为value。
* 若当前请求的其他属性能够匹配成功,但是params匹配不成功
* 页面报错400 - Parameter conditions "username" not met for actual request parameters
如下写法,表示请求参数中必须有username请求参数,可以有password,但是不能为123456
@RequestMapping(value = "/hello",params = {"username","password!=123456"})
public String HelloWorld() {
return "success";
}
4、@RequestMapping注解的headers属性
通过请求的请求头信息匹配请求映射
headers属性要求所匹配的请求必须携带或者不携带某些请求头信息headers属性是一个字符串类型的数组
* 该属性的用法和params一样
* 若当前请求的其他属性能够匹配成功,但是headers匹配不成功,页面报错404
四种表达式
"header":要求请求映射所匹配的请求必须携带header请求头信息
"!header":要求请求映射所匹配的请求必须不能携带header请求头信息
"header=value":要求请求映射所匹配的请求必须携带header请求头信息且header=value
"header!=value":要求请求映射所匹配的请求携带header请求头信息且是header!=value
@RequestMapping(value = "/hello",headers={"referer"})
public String HelloWorld() {
return "success";
}
SpringMVC支持的2种暗黑风格
1、@RequestMapping支持ant风格的路径
在@RequestMapping所设置value属性中,可以使用ant风格的特殊符号
?:表示任意的单个字符(不包括?和/)
*:表示任意的0个或多个字符(里面不能包含?和/)
**:表示任意层数的任意目录
注意:使用**时,必须写在双//中,且前后不能有其他内容,否则会被当做两个单个的*被解析
@RequestMapping("/a?a/ant")
public String testAnt(){
return "success";
}
?:表示任意的单个字符(不包括?和/)
@RequestMapping("/**/ant")
public String testAnt(){
return "success";
}
("/**/ant") 表示任意层数的任意目录
2、SpringMVC支持路径中的占位符
是新的传输请求参数的方式,把请求参数拼接在路径中。
可以在@RequestMapping的value属性匹配路径时,通过{}占位符的方式表示该请求参数
在控制器方法中,可以通过@PathVariable注解将占位符的值和控制器方法的参数进行绑定
@RequestMapping("/path/{username}/{password}")
public String testPath(@PathVariable("username") String username, @PathVariable("password") String password){
return "username:"+username+"===="+"password:"+password;
}
额外扩展,这么写也可以
@RequestMapping("{id}.html") public String queryById(@PathVariable("id") String id){ return "query:id="+id ; }
SpringMVC获取请求参数
1、用servletAPI获取请求参数
将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象
@RequestMapping("/servletAPI")
public String getParamByServletAPI(HttpServletRequest request){
HttpSession session = request.getSession();
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:"+username+",password:"+password);
return "success";
}
2、控制器方法的形参获取请求参数
控制器方法的形参要和请求参数名要保持一致
@RequestMapping("/getParam")
public String getParam(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}
特殊情况:
控制器方法的形参要和请求参数名不一致。
@RequestParam 设置请求参数和控制器方法形参的映射关系
value:设置要和形参绑定的请求参数的参数名
required:是否必须携带value所指定的请求参数,默认值为true,表示必须携带该请求参数
defaultValue:若当前value所指定请求参数为传输,则为绑定的形参赋值默认值,此时和required为true或false无关,有了defaultValue,required形同虚设!
@RequestMapping("/param")
public String getParam(@RequestParam(value = "name", required = true,
defaultValue = "syl") String username, String password)
{
System.out.println("username:"+username+",password:"+password);
return "success";
}
@RequestHeader("请求头的键")
@RequestHeader注解将请求报文中请求头信息和控制器方法的形参进行绑定,用法和@RequestParam一样
@RequestMapping("/header")
public String testHeader(@RequestHeader(value = "referer") String referer){
System.out.println(referer);
return "success";
}
@CookieValue("cookie的键")
@CookieValue注解将cookie数据和控制器方法的形参进行绑定,用法和@RequestParam一样
//把键为JSESSIONID的cookie的值赋值给JSESSIONID
@RequestMapping("/cookie")
public String testCookie(@CookieValue("JSESSIONID") String JSESSIONID){
System.out.println(JSESSIONID);
return "success";
}
3、通过POJO获取请求参数。(用的最多)
针对要获取的请求参数有多个。但是要保证一致:请求参数的参数名和实体类中的属性名一致
此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值
@RequestMapping("/pojo") public String getParamByPOJO(User user){ System.out.println(user); return "success"; }
user不可能为空,springmvc一定会创建User的对象,再解析接收请求参数
4、特殊情况,同名的请求参数
比如爱好
<form th:action="@{/test/pojo}" method="post">
爱好:<input type="checkbox" name="hobby" value="A">A
<input type="checkbox" name="hobby" value="B">B
<input type="checkbox" name="hobby" value="C">C<br>
<input type="submit" value="添加">
</form>
方式一:通过字符串类型的数组获取,数组中是每个请求参数的值,[A,B,C]
@RequestMapping("/pojo") public String getParamByPOJO(String[] hobby){ System.out.println(Arrays.toString(hobby)); return "success"; }
方式二:通过字符串类型的形参获取,获取的结果为每个请求参数的值中间使用逗号拼接的结果,A,B,C
@RequestMapping("/pojo") public String getParamByPOJO(String hobby){ System.out.println(hobby); return "success"; }
解决请求参数的乱码问题
CharacterEncodingFilter编码过滤器是spring提供给的,不是springMVC,不需要我们自己写
注意:
get请求不会出现乱码。post请求出现乱码。
SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效
<!--配置springMVC的编码过滤器-->
<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>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>