springMVC
- 一个实现了 MVC 框架模式的 Web 框架,底层基于 Servlet 实现。
帮助我们做了什么 ?
- 入口控制:SpringMVC 框架通过 DispatcherServlet 作为入口控制器,负责接收请求和分发请求。而在 Servlet 开发中,需要自己编写 Servlet 程序,并在 web.xml 中进行配置,才能接受和处理请求。
- 在 SpringMVC 中,表单提交时可以自动将表单数据绑定到相应的 JavaBean 对象中,只需要在控制器方法的参数列表中声明该 JavaBean 对象即可,无需手动获取和赋值表单数据。而在纯粹的 Servlet 开发中,这些都是需要自己手动完成的。
- IoC 容器:SpringMVC 框架通过 IoC 容器管理对象,只需要在配置文件中进行相应的配置即可获取实例对象,而在 Servlet 开发中需要手动创建对象实例。
- 统一处理请求:SpringMVC 框架提供了拦截器、异常处理器等统一处理请求的机制,并且可以灵活地配置这些处理器。而在 Servlet 开发中,需要自行编写过滤器、异常处理器等,增加了代码的复杂度和开发难度。
- 视图解析:SpringMVC 框架提供了多种视图模板,如 JSP、Freemarker、Velocity 等,并且支持国际化、主题等特性。而在 Servlet 开发中需要手动处理视图层,增加了代码的复杂度。
第一个 Spring MVC 的开发流程
- 创建一个空的工程
- 设置 JDK 版本
- 设置 maven 版本
- 创建 maven 模块
- 在 pom 文件设置打包方式 war 方式
- 引入依赖 springmvc 依赖 logback 依赖 thymeleaf 和 spring6 整合 servlet 依赖(scope 设置 provided 表示这个依赖由第三方容器来提供)
1.2 给 Maven 模块添加 web 支持
在模块 src/main 目录下新建 webapp 目录 (默认是带有小蓝点 没有需要自己添加 module 设置 ) 另外需要添加 web.xml 文件 注意添加的路径
1.3 在 web.xml 文件中配置前端控制器(springmvc 内置的一个类 DispatchServlet)所有的请求都应该经过 DispatcherServlet 处理
重点: / 表示 除了访问 xxx.jsp 结尾的请求路径外的所有路径 也就是说 只要不是 JSP 访问路径,一定会走 DispatcherServlet
1.4 编写 FirstController,在类上标注 @Controller 注解,纳入 IOC 容器
当然 也可以用 @Component 注解进行标注 @Controller 只是@Component 的别名
1.5 配置编写 Springmvc 框架自己的配置文件
配置文件默认的名字 配置文件默认存放的位置 WEB-INF 两个配置: 1 组件扫描 2 配置视图解析器
1.6 提供视图
在 WEB-INF/templates 目录下新建 first.html 文件 编写符合 html 模版的字符串
1.7 提供请求映射
最终返回逻辑视图名称 逻辑视图名称:first 物理视图名称:前缀 + first + 后缀 最终路径: /WEB-INF/templates/first.html
关于@RequestMapping 注解的 value 属性
value 属性本身是一个 String[] 字符串数组,说明多个请求可以映射同一个处理器方法
如果注解的属性是数组,并且在使用注解的时候,该数组只有一个元素,大括号可以省略
如果使用某个注解的时候,如果只使用一个 value 属性,那么 value 也是可以省略的
value 属性的别名是 path
path 属性的别名是 value
RequestMapping 的 value 属性支持 Ant 风格的 支持模糊匹配的路径
? 表示任意一个字符
- 表示 0 到 N 个任意字符 (排除'/' '?') ** 表示 0 到 N 个任意字符 ,可以出现路径分隔符 注意 ** 左边只能 '/' spring6 只能是末尾出现
关于@RequestMapping 注解的 value 属性占位符(重点)
现在流行 RESTFUL 风格的 URL:/springmvc/login/admin/123
@RequestMapping("/login/{username}/{password}")
public String testRESTFulURL(
@PathVariable("username")
String username,
@PathVariable("password")
String password) {
System.out.println("username: " + username + " password: " + password);
return "ok";
}
关于@RequestMapping 注解的 method 属性,通过该属性限制前端请求方式,如果请求方式不同,则会报 405 错误
@RequestMapping(value = "/user/login", method = { RequestMethod.GET, RequestMethod.POST })
public String userLogin() {
System.out.println("处理登陆的业务逻辑...");
return "ok";
}
衍生 Mapping
- @PostMapping
- @GetMapping
web 的请求方式
GET POST PUT DELETE HEAD
关于 requestMapping 注解的 params 属性
关于 requestMapping 注解的 headers 属性
获取请求的数据
- servlet API
在处理器方法参数上,HttpServletRequest springMvc 框架将自动将 Tomcat 服务器创建的 request 对象传递给处理器方法 我们直接在方法中使用 equest 对象即可。
- 注解 @requestParam
属性 value name required:设置该参数是否必传,如果该属性没传则提示 400 这个属性类似于 @RequestMapping 注解中的 params 可以设置为 false,则该参数不是必传 不会报 400,但是前端没有提供这个属性,默认为 null defaultValue:如果前端没有提供参数,可以设置该参数的默认值
- 行参名来接收,如果方法行参的名字和提交数据时的 name 相同,则@RequestParam 可省略
如果使用的是spring6+版本,则需要在pom文件上添加如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>17</source>
<target>17</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
注意:如果 控制器上的行参名 和 请求参数名 不一致,那么控制器上的行参默认值是 null
- 使用 POJO 类/JavaBean 接收请求参数(常用)
底层实现:反射机制 不过使用前提是:POJO 类的属性名和请求参数名一致 实现原理是什么? 假设提交一个请求,参数名是 username,那么 POJO 类必须有一个属性名也叫作:username 根据 username 进行 setUsername 注入赋值
重点:底层通过反射机制调用 set 方法给属性赋值,所以 set 方法名非常重要。 如果前端提交参数是 username,那么 POJO 类中必须有 seUsername 方法
获取请求头信息 ?
使用@RequestHeader注解获取
@RequestHeader(value = "Referer",required = false, defaultValue = "")
获取客户端提交的 Cookie ?
使用@CookieValue 注解 获取控制器方法上的行参
关于 javaweb 项目,如何解决 post 请求乱码问题?
request.setCharacterEncoding("utf-8"); 但是该执行语句必须在 request.getParameter("")之前执行才有效。 第一种:可以自己写 过滤器 Filter 第二种:使用 springmvc 内置的字符编码过滤器 CharacterEncodingFilter
Request 域数据共享
- 第一种方式:在处理器方法上,添加 HttpServletRequest 参数即可
- 第二种方式:在 springmvc 的处理器方法上添加一个接口类型 Model (ui.Model)
@RequestMapping("/testModel")
public String testModel(Model model) {
model.addAttribute("testRequest", "testModel");'
// 转发
return "ok";
}
- 第三种方式:在 springmvc 的处理器方法上添加一个接口类型 Map
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
map.put("testRequest", "testMap");
return "ok";
}
- 第四种方式:在 springmvc 的处理器方法上添加一个类 ModelMap
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap) {
modelMap.addAttribute("testRequest", "testModelMap");
return "ok";
}
研究下,Model 接口,Map 接口,ModelMap 类 三者之间的关系 ?
WARNING
表面是使用的不同接口和不同的类,实际上使用的是同一个对象 org.springframework.validation.support.BindingAwareModelMap
- 第五种方式: 使用 ModelAndView 类完成数据共享
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
// 创建模型视图对象
ModelAndView modelAndView = new ModelAndView();
// 给模型视图对象 绑定数据
modelAndView.addObject("testRequest", "testModelAndView");
// 给模型视图对象 绑定 视图
modelAndView.setViewName("ok");
// 返回模型视图对象
return modelAndView;
}
聊一个真相:
WARNING
对于处理器方法来说,不管使用的是 Model 对象,Map 对象, modelMap 类, ModelAndView 类,最终处理器方法执行 结束后,返回的都是 ModelAndView 对象,这个返回的 ModelAndView 对象给 DispatchServlet 类了
当请求路径不是 JSP 的时候,都会走前端控制器 DispatchServlet. DispatchServlet 中有一个方法 doDispatch,这个方法通过请求路径找到 处理器方法, 然后调用处理器方法返回一个视图名称(也可能是一个 ModelAndView 对象),底层会将逻辑视图名称转换 为 view 对象,然后结合 Model 对象,封装一个 ModelAndView 对象,然后将该对象返回给 DispatchServlet 类.
Session 域数据存储
- 第一种方式: 使用原生的 servlet API 实现(在处理器方法的参数上添加 httpSession 对象, springMVC 会自动将 session 对象传递给这个参数)
- 第二种方式: 使用@SessionAttributes 注解实现 session 域数据存储
Application 域数据存储
这个域使用较少,如果使用的话,一般采用 Servlet API 的方式使用.
@RequestMapping("/testApplication")
public String testApplicaitonScope(HttpServletRequest request) {
ServletContext application = request.getServletContext();
application.setAttribute("testApplication", "testApplication");
return "ok";
}
SpringMVC 中常用的视图
- InternalResourceView: 内部资源视图 (是- springMVC 内置的,专门用于解析 JSP 模版语法的, 另外也负责 转发 forward 功能实现)
- RedirectView: 重定向视图 (是 springMVC 内置的, 专门负责 重定向 redirect 功能实现)
- ThymeleafView: Thymeleaf 视图 (第三方, 专门负责解析 thymeleaf 模版语法) ...
实现视图的核心类和核心接口
- DispatcherServlet: 前端控制器 负责接收前端的请求 根据请求路径找到对应的处理器方法 执行处理器方法 并且最终返回 ModelAndView 对象. 再往下就是视图解析器
- ViewSource 接口: 视图解析器接口 (ThymeleafViewResolver 实现了 ViewSource 接口, InternalResourceView 也是实现了 ViewSource 接口) 这个接口做什么 ? 这个接口的作用就是将 逻辑视图名称 转换为 物理视图名称 并且最终返回一个 View 接口对象 核心方法是什么 ? View resolveViewName(String viewName, Locale locale) throws Exception;
- View 接口: 视图接口 这个接口做什么 ? 这个接口主要负责将模版语法的字符串转换为 html 代码, 并且将 html 代码响应给浏览器 (即渲染.) 核心方法是什么 ? void render(@Nullable Map<String, ?/> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
在 springMVC 中是怎么实现转发的 ?
/* 注意:
当 return pageA 的时候, 返回一个逻辑视图,这种方式跳转到视图
默认采用的是forward方式跳转过去的, 只不过这个底层创建的视图对象 是thymeleafView*/
@RequestMapping("/A")
public String toA() {
// 返回逻辑视图
return "pageA";
}
如何转发 , 什么格式 ?
"return forward: /B" 转发到 "/B" ,这是一次请求, 底层创建视图对象是, internalResourceView 对象
怎么重定向 , 什么格式 ?
"return redirect: /B" 转发到 "/B" ,发起两次请求,底层创建的是 RedirectView
WARNING
总结:
转发: "return forward: /B" ---> internalResourceView
重定向: "return redirect: /B" ---> redirectView
<mvc:view-controller />
INFO
这是个配置信息, 可以在 springmvc.xml 文件中进行配置, 作用是什么 ? 如果一个 Controller 上的方法是为了完成视图跳转,没有任何业务代码,那么这个 controller 可以不写. 直接在 springmvc.xml 中写上 <mvc:view-controller /> 注解即可.
<mvc:view-controller path="/test" view-name="test" />
<mvc:annotation-driven />
开启注解启动,会让整个项目中的注解再次开启.
关于静态资源处理.
- 假如有静态文件 static,如果想要直接访问有两种解决方式:
- 第一种: 开启默认的 Servlet 服务, 需要在 springmvc.xml 配置 开启静态资源访问jsServlet 服务器默认先走 DispatcherServlet ,如果发生 404, 则会自动走 defaultServlet 服务帮你定位静态资源.
<!--开启默认的defaultServlet 可以使用default访问静态资源--> <mvc:default-servlet-handler /> <mvc:annotation-driven />
- 在 springmvc.xml 文件中 添加如下配置:
js<!--配置处理静态资源--> <mvc:annotation-driven /> <mvc:resources mapping="/static/**" location="/static/" />
- 第一种: 开启默认的 Servlet 服务, 需要在 springmvc.xml 配置 开启静态资源访问
RESTful API
什么是 RESTful ?
RESTful 是对 WEB 服务接口的设计风格,提供的一套约束,可以让 WEB 服务接口更加简洁, 易于理解.
RESTful 是表述性状态转移
- 查询 get
- 新增 post
- 删除 delete
- 修改 put
RESTful 风格中要求,修改的时候,提交必须是一个 PUT 请求, 怎么提交 PUT 请求 ?
要想发送 put 请求, 前提必须是一个 POST 请求
在 POST 请求中添加隐藏域:
js<input type="hidden" name="_method" value="put" />
在 web.xml 中添加一个过滤器
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/\*</url-pattern>
</filter-mapping>
在 springMVC 中如何使用原生的 ServletAPI 完成 AJAX 请求的响应 ?
@RequestMapping("/ajax")
public void ajax(HttpServletResponse response) throws Exception {
PrintWriter writer = response.getWriter();
writer.print("hello ajax im spring mvc2w");
}
在 springMVC 中如何使用原生的 ServletAPI 完成 AJAX 请求的响应 ?
@RequestMapping("/ajax")
public void ajax(HttpServletResponse response) throws Exception {
PrintWriter writer = response.getWriter();
writer.print("hello ajax im spring mvc2w");
}
@ResponseBody 注解 (非常重要)
@GetMapping("/ajax")
@ResponseBody
public String ajax() throws IOException {
return "hello ajax im spring mvc2w";
}
WARNING
注意: 一旦处理器方法上添加了 @ResponseBody 返回将不再是 逻辑视图名称 ,而是将结果返回给浏览器 底册实现原理代替的就是
PrintWriter writer = response.getWriter();
writer.print("hello ajax im spring mvc2w");
以上使用的 http 消息转换器是 StringHttpMessageConverter
@GetMapping("/ajax")
@ResponseBody
public User ajax() throws IOException {
User user = new User(123L,"zhangsan", "1232");
return user;
}
当一个处理器上面有 @ResponseBody 注解,并且返回一个 java 对象,例如 user 对象,那么 springMVC 框架会自动将 user 对象转换成字符串响应给浏览器.
当然,你要在 pom.xml 文件引入 jackson 依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
以上使用 HTTP 转换器是: MappingJackson2HttpMessageConverter.
非常好用的注解 @RestController
INFO
出现在类上 = @Controller + @ResponseBody
@RestController 是一个复合注解.
表示该类上自动添加了 @Controller 注解, 并在该类中所有的方法上都会自动添加 @ResponseBody 注解.
关于@RequestBody 注解
该注解只能使用在处理器方法的形参上
这个注解的作用是直接将请求体传递给 Java 程序,在 Java 程序中可以直接使用一个 String 类型的变量来接收 这个请求体的内容
底层使用 HTTP 消息转换器是: FormHttpMessageConverter
关于@RequestBody 注解的重要用法: 如果前端请求体当中提交的数据是 JSON 格式,那么 @RequestBody 可以将提交的 JSON 格式的字符串转换成 java 对象.
注意: 同样是说过 jackjson 依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
以上前端请求体提交的 JSON 格式的字符串, 那么后端直接将 JSON 格式的字符串转换成 java 对象, 这里使用的消息转换器是: MappingJackson2HttpMessageConverter
RequestEntity
这个类的实例封装了整个请求协议
SpringMVC 自动创建好, 传递给处理器方法上的参数
你只需要在处理器的方法的参数上加上: (RequestEntity requestEntity)即可 springmvc 自动创建好该对象
传递到处理器方法的参数上.
通过它可以获取请求协议中个任何信息: 请求行 请求头 请求体
ResponseEntity
这个类的实例封装了整个响应协议 包括 状态行 响应头 响应体;
注意: 如果你要定制响应协议, 那么处理器的返回值类型必须是 ResponseEntity,
泛型为什么是 User, 因为响应体的信息是 user 信息.
文件上传
文件上传必须是 post 请求
文件上传的 form 标签中使用 enctype="multipart/form-data"
enctype 用来设置请求头的内容类型,默认类型 application/x-www-form-urlencoded
如果你是 spring6,需要在 web.xml 中 dispatcherServlet 中配置
<multipart-config>
<!--设置单个支持最大文件的大小-->
<max-file-size>102400</max-file-size>
<!--设置整个表单所有文件上传的最大值-->
<max-request-size>102400</max-request-size>
<!--设置最小上传文件大小-->
<file-size-threshold>0</file-size-threshold>
</multipart-config>
文件下载(固定格式)
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile(HttpServletResponse response, HttpServletRequest request) throws IOException {
File file = new File(request.getServletContext().getRealPath("/upload") + "/谢谢参与 2.png");
// 创建响应头对象
HttpHeaders headers = new HttpHeaders();
// 设置响应内容类型
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 设置下载文件的名称
String fileName = URLEncoder.encode(file.getName(), "UTF-8");
headers.setContentDispositionFormData("attachment", fileName);
// 下载文件
return new ResponseEntity<byte[]>(Files.readAllBytes(file.toPath()), headers, HttpStatus.OK);
}