SpringMVC
SpringMVC概述
之前写WEB层,我们都要通过继承HttpServlet类来实现servlet,如果我们学习了SpringMVC后就可以不用继承了我们通过其完善的注解体系直接完成
SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于
SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中。
快速入门
让你体验下使用mvc过后servlet代码有多简单
开发步骤:
① 导入SpringMVC相关坐标
相关坐标之前都导过了只用增加一个
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
② web.xml中配置SpringMVC核心控制器DispathcerServlet
<!-- 配置SpringMVC前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
这里可以使用注解代替
创建类代替web.xml
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
@Override
//加载springmvc的容器对象
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx=new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
@Override
//定义哪些请求归mvc处理
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
//加载spring对应的容器对象
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx=new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
}
springmvc为简化上面的代码写了一个AbstractDispatcherServletInitializer类的子类AbstractAnnotationConfigDispatcherServletInitializer,我们继承子类只用传入对应的类就好了,以后也都是用这种
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
③ 创建Controller类和视图页面
④ 使用注解配置Controller类中业务方法的映射地址
这个就相当于原来的servlet文件,访问路径后跳转页面
@Controller
//将类存入spring容器(web层)
public class UserController {
@RequestMapping("/quick")
//定义访问路径
public String save(){
System.out.println("Controller save running...........");
return "success.jsp";
//跳转页面 相当于req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
}
⑤ 配置SpringMVC核心文件 spring-mvc.xml
这里呢我们主要是扫描java文件中使用注解的地方(添加包扫描),一般呢spring和spring-mvc业务是分开的所以要用不同的配置文件使得逻辑清晰
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<!--Controller组件扫描-->
<context:component-scan base-package="com.ember.controller"/>
</beans>
通过配置类配置的话
@Configuration
@ComponentScan("com.ember.controller")
public class SpringMvcConfig {
}
在spring配置类中排除扫描包controller
@Configuration
//排除扫描参数excludeFilters=@ComponentScan.Filter
@ComponentScan(value = "com.ember",excludeFilters=@ComponentScan.Filter(
//按注解排除
type= FilterType.ANNOTATION,
//注解类对应的注解@Controller
classes= Controller.class
))
public class SpringConfig {
}
⑥ 客户端发起请求测试
SpringMVC组件解析
1. 前端控制器:DispatcherServlet
用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。
2. 处理器映射器:HandlerMapping
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3. 处理器适配器:HandlerAdapter
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
4. 处理器:Handler
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。
5. 视图解析器:View Resolver
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6. 视图:View
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面
注解解析
@RequestMapping
作用:用于建立请求 URL 和处理请求方法之间的对应关系
位置:
类上,请求URL 的第一级访问目录。此处不写的话,就相当于应用的根目录
这个是为了防止不同模块路径相同导致的冲突(有多个controller类时要加)
方法上,请求 URL 的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径
注解中的属性:
value:用于指定请求的URL。它和path属性的作用是一样的(只定义单个路径可省略)
method:用于指定请求的方式
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样
例如:
params = {“accountName”},表示请求参数必须有accountName
params = {“moeny!100”},表示请求参数中money不能是100
@RequestMapping(value = "/user", method= RequestMethod.GET,params ={"username"})
以上例子指定访问方式必须是get且必须含有请求参数username
(注意如果在类上面使用了@RequestMapping定义了路径那么,在类中的方法返回跳转路径时路径文件前要加"/")/标识到webapp目录下找文件
其实这个return后的跳转地址默认是
return "forward/xxxx.jsp"
表示转发,地址不改变但是页面跳转,我们一般可以省略前面的forward
改成重定向方式跳转(地址改变)
return "redirect/xxxx.jsp"
一般我们会把jsp文件放到webapp下的jsp包中,那我们每次要跳转的时候写路径就比较繁琐要指明包和后缀
return"/jsp/xxx.jsp"
所以我们可以手动配置内部资源视图解析器
在spring-mvc配置文件中我们配置
<!-- 配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 定义路径前缀-->
<property name="prefix" value="/jsp/"></property>
<!-- 定义路径后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
那我们的跳转路径就可以直接写名字就好了
return "success";
组件中最重要和必须掌握的就是前端控制器,注解就是**@RequestMapping**
SpringMVC数据响应
数据响应其实我们之前在javaweb的时候就实践过了
我们通过response.getWriter().write(string);回写数据
通过 request.getRequestDispatcher(“/req6”).forward(request,response);转发
或者是重定向方式跳转页面
页面跳转
返回字符串形式
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转。,我们在快速入门中就是用的这种方式
<!-- 配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 定义路径前缀-->
<property name="prefix" value="/jsp/"></property>
<!-- 定义路径后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
return "success";
返回的跳转路径就是/jsp/succuee.jsp
返回ModelAndView对象实现跳转
这里有两种方式
@RequestMapping("/quick2")
public ModelAndView save2(){
ModelAndView modelAndView=new ModelAndView();
//设置模型数据 相当于原来的 存储到request域中 request.setAttribute("username",“ember");
modelAndView.addObject("username","ember");
//设置视图名称
modelAndView.setViewName("success");
return modelAndView;
}
@RequestMapping("/quick3")
public ModelAndView quickMethod3(ModelAndView modelAndView ){
modelAndView.addObject("username","ember");
//设置视图名称
modelAndView.setViewName("success");
return modelAndView;
}
我们向request域存数据也有两种方式上面已经用了一种
modelAndView.addObject("username","ember");
还有一种有点像原始方法(不常用)
//通过SpringMVC框架注入的request对象setAttribute()方法设置
@RequestMapping("/quick")
public String quickMethod(HttpServletRequest request){
request.setAttribute("name","zhangsan");
return "index"; }
回写数据
1.直接返回字符串
相当于web基础中的response.getWriter().print(“”)
@RequestMapping("/quick4")
public void save4(HttpServletResponse response) throws IOException {
response.getWriter().print("hello ember");
}
但是我们使用框架还是最要好使用框架的方式
在方法上加上@ResponseBody告诉spring这个return是直接返回字符实现回显,而不是跳转页面
@RequestMapping("/quick7")
@ResponseBody
public String save7() throws IOException {
return "hello ember";
}
一般呢我们不会简单返回一个字符串或对象,而是返回json格式的字符串,所以我们一般要使用工具将对象转换成json格式字符串返回
返回json格式数据
首先导入json的依赖包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.10</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.10</version>
</dependency>
@RequestMapping("/quick8")
@ResponseBody
public String save8() throws IOException {
User user=new User();
user.setName("张三");
user.setAge(10);
user.setSex("男");
ObjectMapper objectMapper=new ObjectMapper();
String str = objectMapper.writeValueAsString(user);
return str;
}
现在呢如果用的是全注解开发那么只需要导入一个依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
然后我们在前面加上@ResponseBody
springmvc会自动将字符串转换成json格式再返回的
//返回json格式
@RequestMapping("/toJsonPojo")
@ResponseBody
public User toJsonPojo(){
System.out.println("返回Json对象数据");
User user=new User();
user.setName("吴磊");
user.setAge(18);
return user;
}
返回json集合数据
//返回json格式集合
@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList(){
System.out.println("返回Json集合数据");
User user=new User();
user.setName("吴磊");
user.setAge(18);
User user2=new User();
user2.setName("小红");
user2.setAge(17);
List<User> userList=new ArrayList<>();
userList.add(user);
userList.add(user2);
return userList;
}
这里呢我个人觉得用web中的fastjson依赖比较方便(不用全注解开发这种简单,用全注解请用上面的方法)
当然spingmvc中也是有封装这种转字符串的方法的,只是需要你配置一下
你使用的json依赖不一样配置也不一样,我这里用到的是fastjson的
<!-- 配置处理器映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"></bean>
</list>
</property>
</bean>
@RequestMapping("/quick9")
@ResponseBody
public User save10() throws IOException {
User user=new User();
user.setName("张三");
user.setAge(10);
user.setSex("男");
return user;
}
现在springmva提供了配置我们直接在spring-mvc里面配置一下就不用搞这些配置了
在方法上添加**@ResponseBody**就可以返回json格式的字符串,但是这样配置比较麻烦,配置的代码比较多,
因此,我们可以使用mvc的注解驱动代替上述配置。
<!--mvc**的注解驱动**-->*
<mvc:annotation-driven/>
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使用mvc:annotation-driven自动加载 RequestMappingHandlerMapping(处理映射器)和
RequestMappingHandlerAdapter( 处 理 适 配 器 ),可用在Spring-xml.xml配置文件中使用
mvc:annotation-driven替代注解处理器和适配器的配置。
同时使用mvc:annotation-driven默认底层就会集成jackson进行对象或集合的json格式字符串的转换。
使用mvc提供的注解驱动(主流)
在spring-mvc.xml
<!-- mvc的注解驱动-->
<mvc:annotation-driven/>
就可以直接使用了
//.Springmvc自动将user对象转换成json格式的字符串
@RequestMapping("/quick9")
@ResponseBody
public User save10() throws IOException {
User user=new User();
user.setName("张三");
user.setAge(10);
user.setSex("男");
return user;
}
而且页面收到的中文数据显示还不乱码
SpringMVC 获得请求数据
客户端请求参数的格式是:name=value&name=value… …
服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:
基本类型参数
POJO类型参数(springmvc可以自当帮我们封装成对象)
数组类型参数
集合类型参数
获得基本类型参数
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
@RequestMapping("/quick11")
@ResponseBody
public void save11(String username,int age) throws IOException {
System.out.println(username);
System.out.println(age);
}
就是当我们传入的数据符合参数要求我们才能成功接收到数据
获得POJO类型参数
原来我们接受一个对象类型的json字符串我们要手动将其封装成对象
String params = reader.readLine();//.json字符串
//.将字符串转为brand对象
Brand brand = JSON.parseObject(params, Brand.class);
现在mvc对这个功能有封装了
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
就是请求参数含有对象的所有属性,且对应的属性名和请求数据的键一样就可以自动封装
首先创建好user类(对象类)写好set,get,tostring
@RequestMapping("/quick12")
@ResponseBody
public void save12(User user) throws IOException {
System.out.println(user);
}
当我们访问路径传入的是包含所有属性的数据时(名字要和类中一样)
则mvc会自动将其封装成对象User
那如果我们实体类中有引用属性,那么我们传参的时候用(引用属性名.其类属性名=xxxx)对其自身类属性一一赋值就可以了
获得数组类型的参数
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
@RequestMapping("/quick11")
@ResponseBody
public void quickMethod11(String[] strs) throws IOException {
System.out.println(Arrays.asList(strs));
}
http://localhost:8080/itheima_springmvc1/quick11?strs=111&strs=222&strs=333
获得集合类型参数
普通集合
//接收集合参数
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("pojo参数传递=》"+likes);
return "{'module':'list param'}";
}
}
对象集合的参数我们要写一个对象对其进行封装
public class VO {
private List<User> userList;
同样写好其get,set,tostring
<form action="${pageContext.request.contextPath}/user/quick13" method="post">
<%-- 标明是第几个user对象的username,age--%>
name:<input type="text" name="userList[0].name"><br>
age:<input type="text" name="userList[0].age"><br>
sex:<input type="text" name="userList[0].sex"><br>
name1:<input type="text" name="userList[1].name"><br>
age1:<input type="text" name="userList[1].age"><br>
sex1:<input type="text" name="userList[1].sex"><br>
<input type="submit" value="提交">
在页面中分别对其数组的第几个值的属性进行赋值
@RequestMapping("/quick13")
@ResponseBody
public void save13(VO vo) throws IOException {
System.out.println(vo);
}
但是有一种情况可以直接进行封装
当使用ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装。
data: JSON.stringify(userList),
contentType : 'application/json;charset=utf-8'
@RequestMapping("/quick13")
@ResponseBody
public void quickMethod13(@RequestBody List<User> userList) throws
IOException {
System.out.println(userList);
}
这里我们直接使用axios就好了不用他这种方法,直接发送就是json格式
axios({
method: "post",
url: "http://localhost:8080/cookie-demo/brand/deleteByIds",
data:_this.selectedIds
静态资源放行
注意:通过谷歌开发者工具抓包发现,没有加载到jquery文件(如果我们使用axios那也可能找不到对应的js文件),原因是SpringMVC的前端控制器DispatcherServlet的url-pattern配置的是/,代表对所有的资源都进行过滤操作,我们可以通过以下两种方式指定放行静态资源:
• 在spring-mvc.xml配置文件中指定放行的资源
<mvc:resources mapping=“/js/**” location=“/js/”/>
一般用下面这种
• 使用mvc:default-servlet-handler/标签
以下式全注解开发放行方法
创建个管理放行的的配置类
解决post请求数据乱码
当post请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤
<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>
添加后不乱玛了
参数绑定注解@requestParam
之前我们用的都是请求的参数名和业务方法的参数名一致的情况,那遇到参数不一样呢,就需要用注解进行绑定
<form action="${pageContext.request.contextPath}/quick14" method="post"> <input type="text" name="name"><br> <input type="submit" value="提交"><br>
</form>
@RequestMapping("/quick14")
@ResponseBody
public void quickMethod14(@RequestParam("name") String username) throws
IOException {
System.out.println(username);
}
注解@RequestParam还有如下参数可以使用:
value:与请求参数名称
required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
@RequestMapping("/quick14")
@ResponseBody
public void quickMethod14(@RequestParam(value="name",required =
false,defaultValue = "itcast") String username) throws IOException {
System.out.println(username);
}
自定义类型转换器
• SpringMVC 默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。
• 但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。浏览器只支持这种形式的日期http://localhost:8080/user/quick15?date=2022/4/25,其他格式就会报错
自定义类型转换器的开发步骤:
① 定义转换器类实现Converter接口
public class DateConverter implements Converter<String,Date> {
@Override
public Date convert(String source) {
//将日期字符串转换成日期对象
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
Date date=null;
try {
date= format.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
② 在配置文件spring-mvc.xml中声明转换器
<!-- 声明转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.ember.converter.DateConverter"></bean>
</list>
</property>
</bean>
③ 在中引用转换器(conversion-service属性传入我们自定义的转换器的id)
<mvc:annotation-driven conversion-service="conversionService"/>
如果我们用全注解开发的话那么就简单多了直接加一个注解@DateTimeFormat(pattern=“指定格式(如:yyyy-MM-dd)”)
//日期型参数
@RequestMapping("/dateParam")
@ResponseBody
public String dataParam(Date date,@DateTimeFormat(pattern = "yyyy-MM-dd")Date date1 ){
System.out.println("参数传递 date=》"+date);
return "{'module':'date param'}";
}
你会发现都可以传递了
获得Servlet相关的API
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
HttpServletRequest
HttpServletResponse
HttpSession
@RequestMapping("/quick16")
@ResponseBody
public void quickMethod16(HttpServletRequest request,HttpServletResponse
response,
HttpSession session){
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
获取请求头
. @RequestHeader
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
@RequestHeader注解的属性如下:
value:请求头的名称
required:是否必须携带此请求头
@RequestMapping("/quick17")
@ResponseBody
public void quickMethod17(
@RequestHeader(value = "User-Agent",required = false) String
headerValue){
System.out.println(headerValue);
}
. @CookieValue
使用@CookieValue可以获得指定Cookie的值
@CookieValue注解的属性如下:
value:指定cookie的名称
required:是否必须携带此cookie
@RequestMapping("/quick18")
@ResponseBody
public void quickMethod18(
@CookieValue(value = "JSESSIONID",required = false) String jsessionid){
System.out.println(jsessionid);
}
文件上传
优化web页面上传文件表单,之前我们的项目没有用到这个
. 文件上传客户端三要素
表单项type=“file”
表单的提交方式是post
表单的enctype属性是多部分表单形式,及enctype=“multipart/form-data”
文件上传的原理
当form表单修改为多部分表单时,request.getParameter()将失效。
enctype=“application/x-www-form-urlencoded”时,form表单的正文内容格式是:
key=value&key=value&key=value
当form表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成多部分形式:
单文件上传
步骤:
① 导入fileupload和io坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
② 配置文件上传解析器
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--上传文件总大小-->
<property name="maxUploadSize" value="5242800"/>
<!--上传单个文件的大小-->
<property name="maxUploadSizePerFile" value="5242800"/>
<!--上传文件的编码类型-->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
③ 编写文件上传代码
@RequestMapping("/quick20")
@ResponseBody
//注意MultipartFile对应的参数名要和你表单文件对应的name一样
//文件<input type="file" name="uploadFile"> <br>
public void quickMethod20(String name,MultipartFile uploadFile) throws
IOException {
//获得文件名称
String originalFilename = uploadFile.getOriginalFilename();
//保存文件
uploadFile.transferTo(new File("C:\\upload\\"+originalFilename));
}
多文件上传
可以使用笨方法(在页面加多个file类型input,再到方法中加多个MultipartFile类型参数),但是这也太笨了,一点不灵活后期肯定有其他办法
<h1>多文件上传测试</h1> <form action="${pageContext.request.contextPath}/quick21" method="post"
enctype="multipart/form-data">
名称:<input type="text" name="name"><br>
文件1:<input type="file" name="uploadFiles"><br>
文件2:<input type="file" name="uploadFiles"><br>
文件3:<input type="file" name="uploadFiles"><br> <input type="submit" value="提交"><br>
</form>
@RequestMapping("/quick21")
@ResponseBody
public void quickMethod21(String name,MultipartFile[] uploadFiles) throws
IOException {
for (MultipartFile uploadFile : uploadFiles){
String originalFilename = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("C:\\upload\\"+originalFilename));
} }
PostMan工具介绍
全注解开发
在上面快输入门中讲了用配置类代替黑心那个配置文件的使用,下面介绍下全注解开发除了基本使用的一些配置
post请求乱码
首先解决请求中含有中文,我们到web配置类中配置过滤器
//post乱码处理
@Override
protected Filter[] getServletFilters() {
//spring-mvc提供的过滤器
CharacterEncodingFilter filter=new CharacterEncodingFilter();
//设置解码
filter.setEncoding("UTF-8");
//传入返回的过滤器,多个过滤器{filter,filter1,.....}
return new Filter[]{filter};
}
Json数据传递参数
导入坐标
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
在springmvc配置类中开启功能注解@EnablewebMvc
其功能之一是将json字符串转化为对象
写方法
//接收集合参数 json格式
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
System.out.println("list-json参数传递=》"+likes);
return "{'module':'list param'}";
}
发请求
pojo类型json格式参数
pojo集合json格式参数
//接收pojo集合参数 json格式
@RequestMapping("/pojoListParamForJson")
@ResponseBody
public String pojoListParamForJson(@RequestBody List<User> userList){
System.out.println("list-json参数传递=》"+userList);
return "{'module':'list param'}";
}
Restful风格![image-20220501180108592](https://i-blog.csdnimg.cn/blog_migrate/ea843642aaa136ff3bc7669bd170b7b6.png)
之前我们web开发最后的项目将根据访问路径最后的单词来获取相应的方法,我们通过string的查找字符串来获得了最后的单词(也就是方法名),而如果我们使用Restful风格路径则可以通过注解直接获得
用提交请求方式区分访问什么资源
上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。在业务方法中我们可以使用@PathVariable注解进行占位符的匹配获取工作。
//http://localhost:8080/itheima_springmvc1/quick19/zhangsan
@RequestMapping("/quick19/{name}")
@ResponseBody
public void quickMethod19(@PathVariable(value = "name") String name){
System.out.println(name);
}
可以用这种方法优化之前我们的工程
//输入http://localhost:8080/user/quick14/张三
@RequestMapping("/quick14/{name}")
@ResponseBody
public void save14(@PathVariable(value = "name")String name) throws IOException {
System.out.println("name:"+name);
}
可以看到成功获取到路径值
入门案例
这种风格建议以xxs为访问路径
访问代码完成如下
@Controller
public class UserController {
@RequestMapping(value = "/users",method = RequestMethod.POST)
@ResponseBody
public String save(){
System.out.println("run save,,.....");
return "{'modle':'user save'}";
}
@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
System.out.println("run delete,,....."+id);
return "{'modle':'user delete'}";
}
@RequestMapping(value = "/users",method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user ){
System.out.println("run update,,....."+user);
return "{'modle':'user update'}";
}
@RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id){
System.out.println("run getById,,....."+id);
return "{'modle':'user getById'}";
}
@RequestMapping(value = "/users",method = RequestMethod.GET)
@ResponseBody
public String getAll(){
System.out.println("run getAll,,.....");
return "{'modle':'user getAll'}";
}
以上代码都是可以优化的
我们发现每个方法都有@ResponseBody ,于是我们直接将这个注解提到类上面
然而mvc又有个新注解@RestController是替代@ResponseBody +@Controller的
随后方法中也有新注解替代
// @RequestMapping(method = RequestMethod.POST)
@PostMapping
// @RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
@DeleteMapping("/{id}")
// @RequestMapping(method = RequestMethod.PUT)
@PutMapping
// @RequestMapping(value = "/{id}",method = RequestMethod.GET)
@GetMapping("/{id}")
最后优化代码如下
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public String save(){
System.out.println("run save,,.....");
return "{'modle':'user save'}";
}
@DeleteMapping("/{id}")
public String delete(@PathVariable Integer id){
System.out.println("run delete,,....."+id);
return "{'modle':'user delete'}";
}
@PutMapping
public String update(@RequestBody User user ){
System.out.println("run update,,....."+user);
return "{'modle':'user update'}";
}
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println("run getById,,....."+id);
return "{'modle':'user getById'}";
}
@GetMapping
public String getAll(){
System.out.println("run getAll,,.....");
return "{'modle':'user getAll'}";
}
}
综合案例
首先在原有基础上完成book实体类的创建以及BookController类的实现(这里应为我们只是在完成web层没有链接数据库只有模仿数据访问)
@RestController
@RequestMapping("/books")
public class BookController {
//保存功能
@PostMapping
public String save(@RequestBody Book book){
System.out.println("book save"+book);
return "{'module':'book save success'}";
}
//查询全部功能
@GetMapping
public List<Book> getAll(){
List<Book> bookList=new ArrayList<>();
Book book1=new Book();
book1.setName("Go语言编程");
book1.setType("计算机");
book1.setDescription("从入门到精通");
Book book2=new Book();
book2.setName("Go语法树学习");
book2.setType("计算机");
book2.setDescription("底层原理一览");
bookList.add(book1);
bookList.add(book2);
return bookList;
}
}
接着我们导入前端的资源发现无法访问静态页面,那是因为在web配置类中我们设置拦截了所有资源,tomcat无法访问到资源,除非mvc放行
上面也说过静态资源放行的操作,但是那是配置文件的方式,现在用的是全注解开发,没有配置文件
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/????时候,走pages目录的内容(就是放行pages目录下所有的资源)
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/jsp/**").addResourceLocations("/jsp/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
剩下的就需要完成下页面中的axios代码
拦截器
和过滤器的区别
制作拦截器功能类
配置拦截器的执行位置
这个直接放到之前的那个放行静态资源的类中(也可以重写个类继承WebMvcConfigurationSupport类),重写addInterceptors方法
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
//从容器中注入拦截器
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//访问带pages的地址时,指定到工程对应的pages目录(放行)
registry.addResourceHandler("/pages/**").addResourceLocations("/pages");
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
//指定访问路径的时候执行拦截器,可以写多个路径(注意第一个表示以books结尾的路径/books/2第一个是拦截不了的)
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
}
}
这里有个简化开发–直接让springmvcconfig类实现WebMvcConfigurer 接口(这种方式侵入性比较强)
@Configuration
@ComponentScan({"com.ember.controller"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//指定访问路径的时候执行拦截器
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
}
}
拦截器参数
wo们写拦截器实现HandlerInterceptor接口后会实现三个方法,我们发现这三个方法的参数都有
可见我们是可以在这些方法中获得请求信息的,可以通过获得的信息执行一些方法
重要的是handler参数我们通过
这样获取到原始操作的方法(反射的知识)
拦截器链配置
就是是配置多个拦截器
在interceptor包下在创建一个拦截器
配置执行位置
多个拦截器的执行顺序和addInterceptors方法中写的顺序有关
如果第一个拦截器的preHandle方法返回值为false则后面的方法都不能再执行
mvc全注解开发的配置如下(pom)
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<!-- spring基本坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- 测试类-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- c3p0连接池-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.16</version>
</dependency>
<dependency>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-provider-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>