SpringMVC
SpringMVC
初识springmvc
介绍:
- 是基于java的轻量级web框架
- 特点:
- 高效,基于请求响应的web框架
- 与spring无缝衔接
- 约定大于配置
- 简单灵活
- 功能强大:restful,数据验证,格式化,本地化,主题等
核心组件dispatcherservlet
- 介绍:springwebmvc框架是围绕dispatcherservlet设计,以请求为驱动
- 作用:所有的请求都会经过dispatcherservlet,它相当于一个servlet的管理器,负责将请求分发到各个处理器上。
- 本质:也是一个serlvet,dispatcherservlet->frameworkservlet->httpservletbean->httpservlet->genericservlet->servlet
dispatcherservlet中两个重要的方法
SpringMVC-HelloWorld
步骤
- 导入依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
- 注册dispatcherservlet
<?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">
<!--1.注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动级别-1-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--/ 匹配所有的请求;(不包括.jsp)-->
<!--/* 匹配所有的请求;(包括.jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 在springIOC容器中添加处理器映射器和处理器适配器和视图解析器和对应的处理请求的类
<?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/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--请求处理类-->
<bean id="/hello" class="com.chen.controller.HelloController"/>
</beans>
- 处理类controller
package com.chen.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();
//封装对象,放在ModelAndView中。Model
mv.addObject("msg","HelloSpringMVC!");
//封装要跳转的视图,放在ModelAndView中
mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
return mv;
}
}
- 启动访问即可
- 测试结果
HelloSpringMVC!
- 注意点:处理器适配器和处理器映射器可以省略,前提是处理类实现了Controller接口
springmvc的执行原理
收到一个请求,经过dispatcherservlet,然后经过关联的配置文件,处理器映射器处理对应的请求,根据请求映射名找到对应的spring容器中id和请求名相同的bean,然后返回dispatcherservlet,然后交由处理器适配器,找到对应的bean来处理请求,然后返回dispatcherservlet,根据处理器适配器返回的数据,交给视图解析器解析并跳转页面
使用注解开发(springmvc的精髓)
步骤
- 配置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:mvc="http://www.springframework.org/schema/mvc"
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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- Spring自动扫描包,支持注解开发-->
<context:component-scan base-package="com.chen.AnnotationController"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 注解开发使用的注解
- @Controller:被标记的类,相当于实现了原来的Controller接口
- @RequestMapping(value="") ...:被标记的方法处理相对应的请求 value的值是映射的路径名。被标记的Controller类,所有的处理方法的映射路径前面要加上类映射路径才能被处理。
- @RestController:被标记的类,所有处理方法不会走视图解析器,只会返回字符串。被标记的方法,该方法只会返回字符串
拓展点:
1.自动注册映射处理器和适配器2.映射异常处理3.数据校验4.数据类型转换和数据格式化5.支持使用@RequestBody、@ResponseBody6.支持静态资源文件加载和请求映射同时使用
- annotation-driven和annotation-config和compent-scan的区别
< mvc:annotation-driven />:虽然有这么多功能,但主要还是为了Spring MVC来用的,提供Controller请求转发,json自动转换等功能。
< context:annotation-config/>:向 Spring 容器注册 4 个BeanPostProcessor。注册这4个 BeanPostProcessor的作用,就是为了系统能够识别相应的注解。常见的如:@ Resource 、@Required、@Autowired等。
不过,其实包扫描配置<context:component-scan base-package=”XX.XX”/> 也提供上述功能,因此当启动用了包扫描就不必再配置< context:annotation-config/>了。
controller层理解
在springmvc中实现controller的方式
- 实现Controller接口
package com.chen.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();
//封装对象,放在ModelAndView中。Model
mv.addObject("msg","HelloSpringMVC!");
//封装要跳转的视图,放在ModelAndView中
mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
return mv;
}
}
- 使用注解
package com.chen.AnnotationController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AnnotationController {
@RequestMapping("/hello")
public String[] hello(Model model){
String msg = "Hello,SpringmvcAnnotation";
model.addAttribute("msg",msg);
String[] arr = new String[]{"springmvc","springboot","mybatis"};
return arr;
}
}
RestFul风格
好处
- 简介,安全,缓存高效
实现方式:springmvc中可以用@Pathvariable注解,让参数的值对应到url模板上
package com.chen.contoller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class RestfulController {
@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
public String add(@PathVariable int a,@PathVariable int b, Model model){
int result = a + b;
model.addAttribute("result1","url(restful)"+result);
return "add";
}
@PostMapping("/add/{a}/{b}")
public String add2(@PathVariable int a,@PathVariable int b,Model model){
int result = a+b;
model.addAttribute("result2","表单:"+result);
return "add";
}
@RequestMapping("/add")
public String add3(int a,int b,Model model){
int result = a+b;
model.addAttribute("result3","老式url:"+result);
return "add";
}
}
同一个请求可以实现不同的结果(利用请求的方式不同来实现)
@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
public String add(@PathVariable int a,@PathVariable int b, Model model){
int result = a + b;
model.addAttribute("result1","url(restful)"+result);
return "add";
}
@PostMapping("/add/{a}/{b}")
public String add2(@PathVariable int a,@PathVariable int b,Model model){
int result = a+b;
model.addAttribute("result2","表单:"+result);
return "add";
}
怎么实现不同的请求设置
1.@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
2.@PostMapping("")
所有的请求方式
- get,post,put,delete,patch 浏览器地址栏默认是get请求
springmvc重定向和转发
示例
package com.chen.AnnotationController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class AnnotationController {
@ResponseBody
@RequestMapping("/hello")
public String[] hello(Model model){
String msg = "Hello,SpringmvcAnnotation";
model.addAttribute("msg",msg);
String[] arr = new String[]{"springmvc","springboot","mybatis"};
return arr;
}
@ResponseBody
@RequestMapping("/redirect")
public String info(){
return "重定向请求1";
}
@ResponseBody
@RequestMapping("/forward")
public String info2(){
return "转发请求2";
}
@RequestMapping("/redirecttest")
public String redirect(){
return "redirect:hello";
}
@RequestMapping("/forwardtest")
public String forward(){
return "forward:hello";
}
}
springmvc接收请求及数据回显
环境说明
-
页面:html
-
接口:controller
-
通信:ajax
-
数据格式:json或者其他
ajax(jquery)
$.ajax({
url:"${pageContext.request.contextPath}/ajaxtest",
// data:{"name":$("#user").val()},
// contentType:'application/json; charset=UTF-8',
// data:{"user1":personjson,"hobbys1":hobbysjson},
type:'POST',
data:personjson,
contentType:'application/json;charset=UTF-8',
success:function (data){
alert(data)
},
error:function () {
alert("请求失败")
}
})
利用ajax和springmvc通信的例子
- 示例1(直接传递json字符串)(contentType="application/json")
-
<script type="text/javascript"> var person = {username:'陈浪涛',age:'21',sex:'男',hobbys:['打篮球','题注求','敲代码','包补']} var hobbys = ['打篮球','题注求','敲代码','包补'] var hobbysjson = JSON.stringify(hobbys) var personjson = JSON.stringify(person) function a(){ $.ajax({ url:"${pageContext.request.contextPath}/ajaxtest", // data:{"name":$("#user").val()}, // contentType:'application/json; charset=UTF-8', // data:{"user1":personjson,"hobbys1":hobbysjson}, type:'POST', //直接传json字符串,要把contentType格式设为application/json;charset=UTF-8 data:personjson, contentType:'application/json;charset=UTF-8', success:function (data){ alert(data) }, error:function () { alert("请求失败") } }) } </script>
-
对于直接的json字符串数据格式(使用map集合接收,并指定@RequestBody)
@RequestMapping("/ajaxtest")
public String ajaxtest(@RequestBody Map<String,Object> jsonmap){
// JSONObject jsonObject = JSON.parseObject(user);
// User user1 = jsonObject.toJavaObject(User.class);
// System.out.println(user1);
//
// String[] list = JSON.parseObject(hobbys, String[].class);
// System.out.println(Arrays.toString(list));
System.out.println(jsonmap);
return null;
}
- 示例2(用对象封装json字符串)(contentType=" application/x-www-form-urlencoded; charset=UTF-8 ")
-
<script type="text/javascript"> var person = {username:'陈浪涛',age:'21',sex:'男',hobbys:['打篮球','题注求','敲代码','包补']} var hobbys = ['打篮球','题注求','敲代码','包补'] var hobbysjson = JSON.stringify(hobbys) var personjson = JSON.stringify(person) function a(){ $.ajax({ url:"${pageContext.request.contextPath}/ajaxtest", // data:{"name":$("#user").val()}, // contentType:'application/json; charset=UTF-8', data:{"user1":personjson,"hobbys1":hobbysjson}, type:'POST', // data:personjson, success:function (data){ alert(data) }, error:function () { alert("请求失败") } }) } </script>
-
cotroller接收(用@RequestParam接收)
- 方法一(通过一个个的key来接收,但是如果key多了,就比较麻烦)
@RequestMapping("/ajaxtest")
public String ajaxtest(@RequestParam("user1") String user,@RequestParam("hobbys1") String hobbys){
//解析对象的json字符串
JSONObject jsonObject = JSON.parseObject(user);
User user1 = jsonObject.toJavaObject(User.class);
System.out.println(user1);
//解析数组的json字符串
String[] list = JSON.parseObject(hobbys, String[].class);
System.out.println(Arrays.toString(list));
return null;
}
- 优化方法一(对于key比较多的情况,用对象接收)
package com.chen.pojo;
public class GetJson {
private String user1;
private String hobbys1;
public String getUser1() {
return user1;
}
public void setUser1(String user1) {
this.user1 = user1;
}
public String getHobbys1() {
return hobbys1;
}
public void setHobbys1(String hobbys1) {
this.hobbys1 = hobbys1;
}
@Override
public String toString() {
return "GetJson{" +
"user1='" + user1 + '\'' +
", hobbys1='" + hobbys1 + '\'' +
'}';
}
}
@RequestMapping("/ajaxtest")
public String ajaxtest(GetJson getJson){
System.out.println(getJson);
//解析对象的json字符串
JSONObject jsonObject = JSON.parseObject(getJson.getUser1());
User user1 = jsonObject.toJavaObject(User.class);
System.out.println(user1);
//解析数组的json字符串
String[] list = JSON.parseObject(getJson.getHobbys1(), String[].class);
System.out.println(Arrays.toString(list));
return null;
}
对于@RequestParam和@RequestBody和@PathVariable的理解
- @RequestParam:这个注解主要是用来处理url上的数据和contextType="application/x-www-form-urlencoded; charset=UTF-8",请求方式为GET或者POST的请求的数据,且可以指定接收的具体key。如:@RequestParam("user1")
- @RequestBody:这个注解主要是用来处理请求体中的数据,用于处理非contentType="application/x-www-form-urlencoded",请求方式为post的请求
- @PathVariable:这个注解主要用来处理url上的restful风格的数据,将url上的数据绑定到处理方法的对应的参数
web乱码问题解决(主要是JSON乱码和响应数据乱码)
解决JSON乱码问题
- 方法一:在服务器指定响应的数据格式,加上produces="application/json;charset=UTF-8"
@RequestMapping(value = "/touserajax",produces = "application/json;charset=UTF-8")
@ResponseBody
public String touserajax(){
User user = new User();
user.setAge("12");
user.setUsername("陈浪涛");
return JSON.toJSONString(user);
// return "陈浪涛处理乱码问题";
}
- produces说明:这个属性可以指定这个处理方法的返回值类型和编码格式,也就是响应的ContentType
- consumes说明:这个属性可以指定这个方法可以处理请求的数据类型,如 consumes="application/json" 可以处理json的数据类型
- 注意:这个时候不需要在ajax中指定dataType:'json',也可以解决json乱码问题
- 方法二:在ajax中指定dataType:'json',在服务端可以不用指定响应的数据类型为json,同样也可以解决json乱码问题
$.ajax({
url:"${pageContext.request.contextPath}/touserajax",
dataType:'json',
// data:{"name":$("#user").val()},
// contentType:'application/json; charset=UTF-8',
// data:{"user1":personjson,"hobbys1":hobbysjson},
// type:'POST',
// data:personjson,
// contentType:'application/json;charset=UTF-8',
success:function (data){
alert(data.username)
console.log(data)
},
error:function () {
alert("请求失败")
}
})
- json乱码总结:如果在服务端和ajax中都不指定数据类型,服务器会默认给ajax的回调函数指定响应的数据类型为纯文本类型:Content-Type:'text/plain; charset=ISO-8859-1',所以会出现中文乱码问题。如果直接通过url访问,Content-Type:'text/html;charset=ISO-8859-1',所以页面响应的数据也会出现中文乱码问题。