学习笔记--Spring MVC
1、SpringMVC开发步骤
1)导入MVC相关坐标
2)配置SpringMVC核心控制器DispatcherServlet
<!-- 配置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>
<!-- 不配置说明是访问时启动服务,配置时就是一直启动服务 数字越小优先级越高 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!-- 如果配置的是缺省的,那么说明访问所有的网址都要启动这个服务servlet -->
<url-pattern>/</url-pattern>
</servlet-mapping>
这里的
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
一开始不明白,后来查阅资料后发现:
SpringMVC配置文件的默认路径是WEB-INF下面,如果你的配置文件不想放在WEB-INF目录下,则可以通过
contextConfigLocation这个初始化参数进行指定。classpath:xxx就是去resource目录下找。
3)创建Controller类和视图页面
4)使用注解配置Controller类中业务方法的映射地址
5)配置SpringMVC核心文件spring-mvc.xml(组件扫描之类的)
组件扫描必须在context命名空间下,需要自己添加设置。
xmlns:context="http://www.springframework.org/schema/context" //在xsi:schemaLocation中添加 http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
6)客户端发起请求测试
2、SpringMVC注解解析
2.1 @RequestMapping
RequestMapping用于建立请求URL和处理请求方法之间的对应关系。
简单来说就是跳转到某地址时会执行的方法。
有两种可以使用该方法的位置:
1、类上,说明请求URL的第一级访问目录。此处可以不写,就相当于应用的根目录。
2、方法上,说明请求URL的第二级访问目录,与类上使用@RequestMapping标注的一级目录一起组成访问的虚拟路径。
另外,该注解还附有3个参数:
value:用于指定请求的URL,如果只有这个参数,可以不写value
@RequestMapping("quick") = @RequestMapping(value = "quick")
method:用于指定请求的方式,如果将值设为RequestMethod.GET说明只有Get请求可以通过。
params:用于指定限制请求参数的条件。如果params = {"accountName"} 表示请求参数必须有accountName
2.2 配置内部资源视图解析器
作用:在配置跳转视图的时候,能够简化地址
<!-- 配置内部资源视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 用这个配置的,都是/jsp/xxx.jsp -->
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
用bean注入mvc包里的视图解析器类。这个类有prefix和suffix的set方法,可以自定义属性的值。
prefix:地址的共同前缀。
suffix:地址的共同后缀。
2.3 SpringMVC的数据响应方式
1)页面跳转
- 直接返回字符串
具体做法见2.2配置内部资源解析器
- 通过ModelAndView对象返回
ModelAndView对象返回方式的好处在于:Model是用于封装数据的,View是展示视图的。可以分离开。
有以下几种使用方式:
一、在方法内创建一个ModelAndView类
@RequestMapping("/quick2")
public ModelAndView save2(){
/*
Model: 模型 用于封装数据
View: 视图 用于展示视图
*/
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("username","xcc");
modelAndView.setViewName("success");
return modelAndView;
}
addObject方法是添加数据,参数是一个键值对,前面是名称,后面的是值,不仅仅是字符串,别的数据类型都行。
setViewName方法是命名视图地址,这个地址也受内置视图解析器的影响。
二、在请求方法中写ModelAndView形参。
/*
当springMVC收到这个跳转函数的时候,发现有modelandview实参,会自己创建一个注入。
*/
@RequestMapping("/quick3")
public ModelAndView save3(ModelAndView modelAndView){
modelAndView.addObject("username","xcnb");
modelAndView.setViewName("success");
return modelAndView;
}
MVC识别到请求方法中有形参的时候,会自动帮你注入一个实参。
优点是避免每次手动创建一个ModelAndView对象。其余的同一。
三、model和view进行分离
/*
这里将modelandview分开,model以参数的形式交给stringmvc处理,string以逻辑视图的方式返回。作用是一样的
*/
@RequestMapping("/quick4")
public String save4(Model model){
model.addAttribute("username","小超和你");
return "success";
}
视图以字符串格式回传,model以参数形式创建。
2)回写数据
- 直接返回字符串
- 返回对象或集合
基础用法(直接返回字符串):
/*
responsebody是告知函数返回的字符串不是跳转而是直接在http响应体中返回
*/
@RequestMapping("/quick5")
@ResponseBody
public String save5(){
//回写字符串
return "hello xcc";
}
与返回跳转类似,如果不需要跳转而是回写数据的话,需要在方法上加注释@ResoponseBody
ResponseBody意思是响应体,因为方法与res.writer.print相同,是通过响应数据回传的。
更好的用法:
因为一般回传的数据都是以对象的JSon格式为主,所以,可以用第三方对象转json格式的工具。
将需要回传的数据转换成json格式后再回传。
@RequestMapping("/quick6")
@ResponseBody
public String save6() throws JsonProcessingException {
User user = new User();
user.setAge(10);
user.setUsername("xcc");
//使用json的转换工具将对象转换成json格式字符串在返回
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
return json;
}
json转换工具需要导 jackson-databind 依赖包
最好的用法(返回对象和集合):
刚才使用的方法是用第三方的工具包来给我们将对象转换成Json格式的字符串。
但每次都要创建一个Mapper类显得有些麻烦,MVC帮我们底层完成了这个功能。
在配置文件中,加入mvc命名空间:
xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"
再加入以下配置:
<mvc:annotation-driven/>
这样之后,以后要取得返回的Json格式的字符串,就不必使用转换工具,直接返回该对象,处理器适配器
会自动将该对象转换成Json格式的字符串。
例子:
/*
如果想要让返回的对象是一个已经处理好的json字符串。
在spring-mvc中注入RequestMappingHandlerAdapter
向messageConverters注入MappingJackson2HttpMessageConverter
前提必须是@responsebody响应体返回
替换这个配置可以在xml中<mvc:annotation-driven/>这句注解替代
*/
@RequestMapping("/quick7")
@ResponseBody
public User save7() {
User user = new User();
user.setAge(12);
user.setUsername("xcc");
return user;
}
2.4 SpringMVC获得请求数据(服务器端)
1)普通数据类型
例子:
/*
controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动匹配
*/
@RequestMapping("/quick8")
@ResponseBody
public void save8(String username,int age) {
//当网址是localhost:8080/user/quick8?username=xxxx&age=xx时,会将username和age自动匹配
System.out.println(username);
System.out.println(age);
}
当方法是响应体方法、且业务方法的参数名称与请求参数的名称一致时,
MVC会自动匹配参数,将值传给该参数。
2)获得POJO类型数据
例子:
/*
获得pojo类的数据,请求参数恰好都是pojo类中的属性,会自动适配填补
*/
@RequestMapping("/quick9")
@ResponseBody
public void save9(User user) {
System.out.println(user);
}
当请求参数的名称与POJO类的属性名称一致时,MVC会自动适配填补。
3)获得数组类型数据
例子:
@RequestMapping("/quick10")
@ResponseBody
public void save10(String[] strs) {
System.out.println(Arrays.asList(strs));
//Arrays.asList() 是将数组转化成list集合的方法
//但不支持add,remove,clear方法
//遍历数组十分方便,但是如果要添加或者删除元素还是new一个list遍历添加。
}
当请求参数的名称与数组名称一致时,MVC会自动适配填补。
4)获得集合类型参数
获得集合类型参数与别的数据略有不同,需要将集合放入一个VO对象,通过对VO对象的注入来获得集合类型参数。
例子:
/*
获得集合类型的数据,是将集合放在pojo类中实现,然后传入pojo类,名称对应
*/
@RequestMapping("/quick11")
@ResponseBody
public void save11(VO vo) {
System.out.println(vo);
}
在方法中,和获得POJO类型的数据类似。
public class VO {
private List<User> userList;
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
@Override
public String toString() {
return "VO{" +
"userList=" + userList +
'}';
}
}
VO对象中,将需要注入的集合加入成员属性中。
<body>
<form action="${pageContext.request.contextPath}/user/quick11" method="post">
<input type="text" name="userList[0].username"><br/>
<input type="text" name="userList[0].age"><br/>
<input type="text" name="userList[1].username"><br/>
<input type="text" name="userList[1].age"><br/>
<input type="submit" value="提交">
</form>
</body>
在页面中,将要注入的集合中的某一对象的什么元素对应,MVC就会自动填充属性。
5)获得集合类型参数Ⅱ
当使用ajax提交数据时,可以指定contextT为json格式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装。
<script src="${pageContext.request.contextPath}/js/jquery-3.6.0.js"></script>
<script>
let userList = [];
userList.push({username:"zhansan",age:18});
userList.push({username:"lisi",age:20});
$.ajax({
type:"Post",
url:"${pageContext.request.contextPath}/user/quick12",
data:JSON.stringify(userList),
contentType:"application/json;charset=utf-8"
})
</script>
@RequestMapping("/quick12")
@ResponseBody
public void save12(@RequestBody List<User> userList) {
System.out.println(userList);
}
光是这样并不能找到jquery-3.6.0.js,因为之前在MVC配置中,DispatcherServlet的映射配置是缺省的,这意味着所有的地址请求
都要走这个Servlet,而在ajax请求前,先申请导入了jquery的资源文件,但这个并不能被RequestMapping映射找到,所以就略过不处理,这就导致了
其实jquery根本没有被导入。
解决方法1:
在Spring-mvc.xml中开放资源的访问。
<!-- 开放资源的访问 mapping是访问的地址格式、后面的是具体所在的目录--> <mvc:resources mapping="/js/**" location="/js/"/>
解决方法2:
在Spring-mvc.xml中使用默认的处理器
<!-- 功能是当mvc找不到对应的servlet时用原始的处理器(tomcat)处理 --> <mvc:default-servlet-handler/>
2.5 Post请求中如果出现乱码该如何解决
可以配置一个filter过滤器,将所有代码格式全部变为UTF-8
<!-- 配置全局过滤的filter -->
<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>
同样的<init-param>起到一个设置过滤器编码格式的作用。
2.6 参数绑定注解@requestParam
作用:当请求参数与需要的参数名称不一致,但仍想对应起来时,可以使用该注解配置value参数进行映射。
@RequestMapping("/quick13")
@ResponseBody
public void save13(@RequestParam("name") String username ,@RequestParam("age") String nianling) {
System.out.println(username);
System.out.println(nianling);
}
即:将请求地址为“http://localhost:8080/user/quick13?name=zhangsan&age=18” 依然可以对应注入参数。
补充:
@RequestParam有其他几个参数
required:默认为true,说明请求地址时必须包含此参数。
defaultValue: 当没有指定请求参数时,则使用指定的默认值赋值。
2.7 获得Restful风格的参数
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件。
基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP协议里面四个表示操作方式的动词如下:
GET:获取资源
POST:新建资源
PUT:更新资源
DELETE:删除资源
示例:
//访问localhost:8080/user/quick14/zhangsan 最后不同的 username 回传的参数也不同
@RequestMapping("/quick14/{username}")
@ResponseBody
public void save14(@PathVariable(value = "username") String username) {
System.out.println(username);
}
用 @PathVariable 注解来将占位符和参数绑定,value和{}中的属性要保持一致。
2.8 自定义类型转换器
客户端请求的任何数据到达服务端之后都会变成字符串。
但客户端发送的关于数字的请求依然可以正常的访问,说明MVC内部有将数字(int)转换成字符串的方法。
但不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。
自定义类型转换器的开发步骤:
1)定义转换器类实现Converter接口(复写接口的方法)
public class DateConverter implements Converter<String,Date> {
@Override
public Date convert(String s) {
// new 里面是样式
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = format.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
2)在配置文件中声明转换器。
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.xc.converter.DateConverter"/>
</list>
</property>
</bean>
3)在<annotation-driven>中引用转换器
<mvc:annotation-driven conversion-service="conversionService"/>
当遇到指定格式的时候,就会自动使用转换器。
2.9获得Servlet相关API
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
HttpServletRequest
HttpServletResponse
HttpSession
@RequestMapping("/quick16")
@ResponseBody
public void save16(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
2.10 获得请求头
1)@RequestHeader
使用@RequestHeader可以获得请求头信息。
注解属性如下:
value:请求头的名称
required:是否必须携带此请求头
@RequestMapping("/quick17")
@ResponseBody
public void save17(@RequestHeader(value = "User-Agent", required = false) String user_agent) {
System.out.println(user_agent);
}
2)@CookieValue
使用@CookieValue可以获得指定Cookie的值
注解属性如下:
value:指定Cookie的名称
required:是否必须携带此Cookie
@RequestMapping("/quick18")
@ResponseBody
public void save18(@CookieValue(value = "JSESSIONID") String jsessionId) {
System.out.println(jsessionId);
}
2.11 文件上传
1)文件上传客户端三要素
表单项type=“file”
表单的提交方式是post
表单的enctype属性是多部分表单形式,即enctype="multipart/form-data"
<form action="${pageContext.request.contextPath}/user/quick19" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件<input type="file" name="upload"><br/>
<input type="button" value="上传">
</form>
2) 用MVC实现单文件上传
导入fileupload和io坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
配置文件上传解析器
<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="500000"/>
</bean>
单文件上传的业务方法
@RequestMapping("/quick19")
@ResponseBody
public void save19(String username, MultipartFile uploadFile) throws IOException {
System.out.println(username);
//System.out.println(uploadFile);
//获得上传文件的名称
String originalFilename = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("D:\\upload\\"+originalFilename));
}
transferTo方法将接受到的上传文件以相同文件的格式保存在服务器端。
2.12 多文件上传
大致与单文件一致,但是业务方法接收时改用数组
@RequestMapping("/quick20")
@ResponseBody
public void save20(String username, MultipartFile[] uploadFile) throws IOException {
System.out.println(username);
for (MultipartFile multipartFile : uploadFile) {
String originalFilename = multipartFile.getOriginalFilename();
multipartFile.transferTo(new File("D:\\upload\\"+originalFilename));
}
}
<form action="${pageContext.request.contextPath}/user/quick20" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件<input type="file" name="uploadFile"><br/>
文件<input type="file" name="uploadFile"><br/>
<input type="submit" value="上传">
</form>
浙公网安备 33010602011771号