SpringMVC笔记
SpringMVC
结构图:
HelloMvc
头:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
HelloController:
// 需要实现相关接口
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 模型和视图
ModelAndView mv = new ModelAndView();
// 封装对象,将属性放到mv中
mv.addObject("msg", "hellSpringMVC");
// 封装要跳转的视图,放入mv
// 这个helloMVC和注册的那个"/hello"有什么关联呢?
// 答:输入"/hello"之后,会运行这个mv,这个mv设置属性后会跳转到具体的页面
// 页面的名字为:前缀 + ViewName + 后缀
mv.setViewName("helloMVC");
return mv;
}
}
springmvc-servlet.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"
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"/>
<!--如何让解析器和所有的关联呢?-->
<!--答: 通过web.xml里的servlet对springmvc进行配置, 再让所有的跳转都处理改servlet,就能对所有的进行关联-->
<!--视图解析器-->
<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="it.controller.HelloController"/>
</beans>
web.xml:
<?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的配置文件-->
<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>
/
和/*
的区别:
/
只匹配所有请求,不会匹配jsp,就是输入/hello.jsp不会匹配,直接跳转
/*
当跳转的时候到了jsp,还会再匹配jsp,会无限嵌套。
helloMVC.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
SpringMVC执行原理
使用注解开发
web.xml:
不变的
springmvc-servlet.xml:
使用注解的写法,配置了新内容,也不用注册bean了
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
<context:component-scan base-package="it.controller"/>
<!--让Spring MVC不处理静态资源-->
<mvc:default-servlet-handler/>
<!--支持mvc注解驱动,可以代替原来的两个-->
<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>
HelloController:
package it.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
// 不用实现,直接写注解
@Controller
public class HelloController {
// 设置映射地址
@RequestMapping("/hello")
public String sayHello(Model model){
// 向模型中添加属性
model.addAttribute("msg", "hello,anno,mvc");
// return的是jsp路径,会被加上前缀和后缀
return "helloMVC";
}
}
我是不是要知道哪些工作被省略了?
RequestMapper
作用:用域映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上时,表示类中的所有响应请求的方法都是以该地址作为父路径。
代码:
@Controller
@RequestMapping("/h1")
public class HelloController {
// 设置映射地址
@RequestMapping("/hello")
public String sayHello(Model model){
// 向模型中添加属性
model.addAttribute("msg", "hello,anno,mvc");
// return的是jsp路径,会被加上前缀和后缀
return "helloMVC";
}
}
url输入:/h1/hello
RESTful风格
这一块怎么用呢,总不能自己手打参数吧,应该是某方法调用这个给方法,传参进来,然后跳转吧?
传统
操作方式:
通过不同的参数来实现不同的效果,方法比较单一
url输入:http://localhost:8080/add?a=1&b=2
代码:
@Controller
public class ResfulTest {
@RequestMapping("/add")
public String test1(int a, int b, Model model){
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
}
RESTful
普通方式:
输入:http://localhost:8080/add2/1/2
,让方法参数的值对应绑定到一个URL模板变量上。
代码:
@Controller
public class RestfulTest2 {
// 设定格式
@RequestMapping("/add2/{p}/{q}")
// 指定参数
public String test(@PathVariable int p,@PathVariable int q, Model model){
int res = q + p;
model.addAttribute("msg", "res: " + res);
return "test";
}
}
指定请求方法类型:
输入:http://localhost:8080/add3/1/2
代码:
@Controller
public class RestfulTest3 {
// 设定格式
@RequestMapping(value="/add3/{p}/{q}", method= RequestMethod.GET)
// 指定参数
public String test(@PathVariable int p,@PathVariable int q, Model model){
int res = q + p;
model.addAttribute("msg", "res: " + res);
return "test";
}
}
精简、重载版:
输入:http://localhost:8080/add4/1/bongba
输出:res: 1bongba Get
代码:
@Controller
public class RestfulTest4 {
@PostMapping("/add4/{p}/{q}")
public String test1(@PathVariable int p,@PathVariable String q, Model model){
model.addAttribute("msg", "res: " + p + q + " Post");
return "test";
}
@GetMapping("/add4/{p}/{q}")
public String test2(@PathVariable int p,@PathVariable String q, Model model){
model.addAttribute("msg", "res: " + p + q + " Get");
return "test";
}
}
重定向和转发
使用ServletAPI实现,不使用视图解析器:
// 此时关闭了视图解析器
@Controller
public class ServletAPI {
// 这个参数真是说有就有啊!
@RequestMapping("/t1")
public void test1(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 先打印个看看
resp.getWriter().println("Hello, servlet API");
}
@RequestMapping("/t2")
public void test2(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 重定向
resp.sendRedirect("/index.jsp");
}
@RequestMapping("/t3")
public void test3(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 转发,带上信息
req.setAttribute("msg", "转发");
// 别放了forward
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
}
}
利用SpringMVC框架,不使用视图解析器:
@Controller
public class MVCnoSt {
@RequestMapping("/m/t1")
public String test1(){
// 转发
return "/index.jsp";
}
@RequestMapping("/m/t2")
public String test2(){
// 转发2
return "forward:/index.jsp";
}
@RequestMapping("/m/t3")
public String test3(){
// 重定向
return "redirect:/index.jsp";
}
}
利用SpringMVC框架,使用视图解析器:
就是把路径加上了呗,然后还能识别redirect
关键字
@Controller
public class MVCst {
@RequestMapping("/h/t1")
public String test1(){
// 转发
return "test";
}
@RequestMapping("/h/t2")
public String test2(){
// 重定向
return "redirect:/index.jsp";
}
}
接受请求参数及数据回显
提交的域名和处理方法的参数名不一致:
使用注解改成别的名字,原来的名字也不能用了
url输入为:http://localhost:8080/hello?username=coco
@Controller
public class Commit {
// 打印传入的参数,并指定参数的名字
@RequestMapping("/hello")
public String test1(@RequestParam("username") String name){
System.out.println(name);
return "helloMVC";
}
}
提交的是一个对象:
首先要有个实体类,然后提交的表单域和对象的成员变量名一致,参数使用对象即可。
前端和实体类的参数名若不一致,改成员变量就会变成null
url输入为:http://localhost:8080/user?id=1&age=12&name=jojo
// 传入的是对象
@RequestMapping("/user")
public String test2(User user){
System.out.println(user);
return "helloMVC";
}
}
建议:
前端参数不管用不用都加上注解
这是个啥?
乱码问题
使用get可以解决:
在方法上用@GetMapping()
。
利用SpringMVC本身的类过滤:
<!--配置SpringMVC的乱码过滤-->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<!--很关键,别写错了-->
<url-pattern>/*</url-pattern>
</filter-mapping>
修改tomcat的配置文件:
位置在config/servlet.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/>
使用注解:
@RequestMapping(value = "/j2", produces = "application/json;charset=utf-8")
Spring统一配置:
可以批量解决编码问题,写在SpringMVC的配置文件中,但是要导个包
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="utf-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
JASON详解
前端生成json
HTML前端json对象:
可在开发者工具的console栏看到对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
// 这东西不能自闭和
// 编写一个JavaScript对象
var user={
name:"jojo",
age:3,
sex:"m"
};
// console打印
console.log(user)
</script>
</head>
<body>
</body>
</html>
Json和JavaScript对象互转:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
// 这东西不能自闭和
// 编写一个JavaScript对象
var user={
name:"jojo",
age:3,
sex:"m"
};
// console打印
console.log(user);
// json转字符串
var str = JSON.stringify(user);
console.log(str);
// 字符串转json
var json = JSON.parse('{"a":"hello", "b":"world"}');
console.log(json);
</script>
</head>
<body>
</body>
</html>
结果展示:
后端返回多种对象
导包:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
直接返回字符串:
很普通的字符串,不能转换为json。
@Controller
public class reJson {
@RequestMapping("/j1")
@ResponseBody // 有了这个注解就不走视图解析器,直接返回字符串
public String json1(){
User user = new User("jojo",3);
return user.toString();
}
}
输出为:User(name=jojo, age=3)
调包返回字符串
这个方法返回的字符串像json的格式,前端拿到以后能转换为json。
@RequestMapping("/j2")
@ResponseBody
public String json2() throws JsonProcessingException {
// jackson里面的类
ObjectMapper mapper = new ObjectMapper();
// 创建一个对象
User user = new User("jojo",3);
String str = mapper.writeValueAsString(user);
return str;
}
输出为:{"name":"jojo","age":3}
只返回字符串的注解:
@RestController
返回List:
@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<User> userList = new ArrayList<User>();
userList.add(new User("jostar", 2));
userList.add(new User("jotaro",3));
userList.add(new User("josehu", 4));
String str = mapper.writeValueAsString(userList);
return str;
}
输出为:[{"name":"jostar","age":2},{"name":"jotaro","age":3},{"name":"josehu","age":4}]
返回时间戳:
ObjectMapper会自动把时间换成时间戳返回
@RequestMapping("/j4")
public String json4() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Date date = new Date();
// ObjectMapper会自动把时间换成时间戳返回
String str = mapper.writeValueAsString(date);
return str;
}
输出为:1595723954367
返回自定义日期格式:
两种方法,一种是java自身做;另一种是使用mapper的方法做
// java方法
@RequestMapping("/j5")
public String json5(){
ObjectMapper mapper = new ObjectMapper();
// 自定义日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String format = sdf.format(date);
return format;
}
输出为:2020-07-26 08:46:55
,不带引号
// mapper方法
@RequestMapping("/j6")
public String json6() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// 自定义日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// mapper设置为不适用时间戳的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
// 指定格式
mapper.setDateFormat(sdf);
Date date = new Date();
String str = mapper.writeValueAsString(date);
return str;
}
输出为:"2020-07-26 08:46:12"
,带引号了
自定义输出的工具类:
工具类:
可以输入格式,也可以不输入格式
public class JsonUtils {
// 没给格式就是默认格式
public static String getJson(Object object){
return getJson(object, "yyyy-MM-dd HH:mm:ss");
}
public static String getJson(Object object, String sdf){
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(sdf);
mapper.setDateFormat(simpleDateFormat);
// 可能出错
try {
String str = mapper.writeValueAsString(object);
return str;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
测试:
@RequestMapping("/j7")
public String json7(){
Date date = new Date();
return JsonUtils.getJson(date);
}
@RequestMapping("/j8")
public String json8(){
Date date = new Date();
return JsonUtils.getJson(date, "yyyy-MM-dd");
}
}
后端的转换:
@RequestMapping("/j9")
public String json9(){
User user = new User("jojo",12);
System.out.println("java对象转json字符串");
String str = JSON.toJSONString(user);
System.out.println(str);
System.out.println("json字符串转java对象");
User user1 = JSON.parseObject(str, User.class);
System.out.println(user1);
System.out.println("java对象转json对象并获取单个元素");
JSONObject jsonObject = (JSONObject) JSON.toJSON(user);
// 可以通过key获取value,真正的json
System.out.println(jsonObject.getString("name"));
System.out.println("json对象转java对象");
User user2 = JSON.toJavaObject(jsonObject, User.class);
System.out.println(user2);
return "helloMVC";
}
}