SpringMVC
SpringMVC
一、简介
1.1 引言
java开源框架,Spring Framework的一个独立模块。
MVC框架,在项目中开辟MVC层次架构
对控制器中的功能包装简化扩展践行工厂模式,功能架构在工厂之上
1.2 MVC架构
1.2.1 概念
名称 | 职责 |
---|---|
Model | 模型:即业务模型,负责完成业务中的数据通信处理,对应项目中的service和dao |
View | 视图:渲染数据,生成页面。对应项目中的Jsp |
Controller | 控制器:直接对接请求,控制MVC流程,调度模型,选择视图。对应项目中的Servlet |
1.2.2 好处
- MVC是现下软件开发中的最流行的代码结构形态;
- 人们根据负责的不同逻辑,将项目中的代码分成MVC3个层次;
- 层次内部职责单一,层次之间藕合度低;
- 符合低精合高内聚的设计理念。也实际有利于项目的长期维护。
二、开发流程
2.1 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
2.2 配置核心(前端)控制器:DispatcherServlet
作为一个MVC框架,首先要解决的是:如何能够收到请求!
所以MVC框架大都会设计一款前端控制器,选型在Servlet或Filter两者之一,在框架最前沿率先工作,接收所有请求。
此控制器在接收到请求后,还会负责springMVC的核心的调度管理,所以既是前端又是核心。
在
web.xml
中
<!--SpringMVC前端(核心)控制器
1.前端,按收所有请求
2.启动SpringMVC工厂mvc.xml
3.springMVC流程调度
-->
<!-- SpringMVC前端控制器 -->
<servlet>
<servlet-name>mvc_shine</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc.xml</param-value>
</init-param>
<!--懒汉式 饿汉式加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc_shine</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2.3 后端控制器
等价于之前定义的Servlet
@Controller //声明控制器
@RequestMapping("/hello") //访问路径
public class HelloController {
@RequestMapping("/test1") //访问路径
public String hello1() { //service doGet doPost
System.out.println("hello1");
return "hello"; //跳转:hello.jsp
}
@RequestMapping("/test2") //访问路径
public String hello2() { //service doGet doPost
System.out.println("hello2");
return null;
}
}
2.4 配置文件
默认名称:核心控制器名-servet.xml默认位置:WEB-INF
随意名称:mvc.xml随意位置:resources但需要配置在核心控制器中
<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"
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
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 注解扫描 告诉SpringMVC 哪些包中含有注解 -->
<context:component-scan base-package="com.qf.web"/>
<!-- 注解驱动 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 视图解析器
作用:1.辅获后端控制器的返回值="hello"
2.解析:在返回值的前后拼接==>"/hello.jsp"
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
三、接受请求参数
3.1 基本类型参数
请求参数和方法的形参 同名即可
SpringMVC默认可以识别的日期字符串格式为:YYYY/MM/dd HH:mm:ss
通过@DateTimeFormat可以修改默认日志格式
// http://xxxx/param/test1?id=1&name=shine&gender=true&birth=2020-12-12 12:13:20
@RequestMapping("/test1")
public String test1(Integer id,
String name,
Boolean gender,
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") Date birth) {
System.out.println("test1");
System.out.println("id:" + id + " name:" + name + " gender:" + gender + " birthday:" + birth);
return "hello";
}
3.2 实体收参(重点)
请求参数和实体的属性 同名即可
//实体类
public class User {
private Integer id;
private String name;
private Boolean gender;
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date birth;
}
// http://xxxx/param/test2?id=1&name=shine&gender=true&birth=2020-12-12 12:13:20
@RequestMapping("/test2")
public String test2(User user) {
System.out.println("test2");
System.out.println(user);
return "hello";
}
3.3 数组收参
简单类型的 数组
<form action="${pageContext.request.contextPath}/param/test3">
<input type="checkbox" name="hobby" value="football">足球
<input type="checkbox" name="hobby" value="basketball">篮球
<input type="checkbox" name="hobby" value="volleyball">排球
<input type="submit" value="提交">
</form>
@RequestMapping("/test3")
public String test3(String[] hobby) {
System.out.println("test3");
System.out.println(hobby);
return "hello";
}
3.4 集合收参
//实体类
public class UserList {
private List<User> users;
}
//http://xxx/param/test4?users[0].id=1&users[0].name=shine&...
@RequestMapping("/test4")
public String test4(UserList userList) {
System.out.println("test4");
for (User u :
userList.getUsers()) {
System.out.println(u);
}
return "hello";
}
3.5 路径参数
// {id} 命名规则
// {id} 等价于 /test5/1 test5/2 test5/xxx
@RequestMapping("/test5/{id}")
public String test5(@PathVariable("id") Integer id) {
System.out.println("test5");
System.out.println("id:" + id);
return "hello";
}
3.6 中文乱码
tomcat中字符集设置,对
get
请求中,中文参数乱码有效
Tomcat配置:URIEncoding=utf-8
在
web.xml
中加入编码过滤器,对post
请求中,中文参数乱码有效
<!-- 编码过滤器 -->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
四、跳转
4.1 转发
@RequestMapping("/test1")
public String test1() {
System.out.println("test1");
// return "hello"; //转发
return "forward:/hello.jsp";
}
@RequestMapping("/test2")
public String test2() {
System.out.println("test2");
// return "forward:/jump/test1"; //转发
return "forward:test1"; //相对路径转发
}
4.2 重定向
@RequestMapping("/test3")
public String test3() {
System.out.println("test3");
return "redirect:/hello.jsp"; //重定向
}
@RequestMapping("/test4")
public String test4() {
System.out.println("test4");
// return "redirect:test3"; //重定向
return "redirect:/jump/test3"; //重定向
}
4.3 跳转细节
在增删改之后,为了防止请求重复提交,重定向跳转
在查询之后,可以做转发跳转
五、传值
c得到数据后,跳转到V,并向V传递数据。进而V中可以渲染数据,让用户看到含有数据的页面
转发跳转:Request作用域
重定向跳转:Session作用域
5.1 Request和Session
//在形参中可以获得Request和Session
@RequestMapping("/test1")
public String test1(HttpServletRequest request, HttpSession session) {
System.out.println("test1");
request.setAttribute("name", "张三");
session.setAttribute("age", 10);
return "data";
}
5.2 JSP中取值
name:${requestScope.name}<br>
age:${sessionScope.age}<br>
5.3 Model
//model中的数据,会在V渲染之前,将数据复制一份给request
@RequestMapping("/test2")
public String test2(Model model) {
model.addAttribute("city", "北京");
model.addAttribute("street", "长安街");
return "data2";
}
//JSP 通过EL表达式取值
city:${sessionScope.city}<br>
street:${sessionScope.street}
5.4 ModelAndView
//ModelAndView 可以集中管理跳转和数据
@RequestMapping("/test4")
public ModelAndView test4() {
//新建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//设置视图名即如何跳转
modelAndView.setViewName("forward:/hello.jsp");
//增加数据
modelAndView.addObject("claz", "001");
return modelAndView;
}
//JSP 通过EL表达式取值
${sessionScope.claz}
5.5 @SessionAttributes
@SessionAttributes(name={"city","street}) Model中的name和gender会存入session中
SessionStatus 移除Session
@Controller
@RequestMapping("/data")
@SessionAttributes(names = {"city", "street"})
public class DataController {
@RequestMapping("/test2")
public String test2(Model model) {
model.addAttribute("city", "北京");
model.addAttribute("street", "长安街");
return "data2";
}
@RequestMapping("/test3")
public String test3(SessionStatus status) {
//清空所有通过model存入的session
status.setComplete();
return "data2";
}
}
六、静态资源
6.1 静态资源问题
静态资源:html,js文件,css文件,图片文件
静态文件没有url-pattern,所以默认是访问不到的
之所以可以访问,是因为tomcat中有一个全局的servlet:
org.apache.catalina.servlets.DefaultServlet,它的url-pattern是"/"它是全局默认的Servlet.所以每个项目中不用匹配的静态资源的请求,由这个Servlet来处理即可。
但在SpringMVC中DispatcherServlet也采用了“/"作为url-pattern,所以项目中不会再使用全局的Serlvet,则静态资源不能完成访问,
6.2 解决方案1
DispathcerServlet采用其他的url-pattern
此时,所有访问handler的路径都要以action结尾!!
在
web.xml
文件中:
<servlet>
<servlet-name>mvc_shine</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>mvc_shine</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
6.3 解决方案2
DispathcerServlet的url-pattern依然采用"/",但追加配置
在
mvc.xml
文件中:
<!--
额外的增加一个handler,且其requestMapping: "/**" 可以匹配所有请求,但是优先级最低
所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
处理方式:将请求转会到tomcat中名为default的Servlet
RequestMapping /* /a /b /c /dxxxx /a/b
/**
-->
<mvc:default-servlet-handler/>
解决方案3
mapping是访问路径,location是静态资源存放的路径
将/html/** 中 /**匹配到的内容,拼接到/hhh/ 后 http://.../html/a.html 访问 /hhh/a.html
在
mvc.xml
文件中
<mvc:resources mapping="/html/**" location="/hhh/"/>
七、Json处理
7.1 导入依赖
<!-- SpringMVC默认的Json解决方案是Jackson,所以只需要导入jackson的jar,即可使用。-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.0</version>
</dependency>
7.2 发送Json数据
7.2.1 使用@ResponseBody
@Controller
@RequestMapping("/json")
public class JsonController {
@RequestMapping("/test1")
@ResponseBody //handler的返回值,转换成json,并将json响应给客户端。
public User test1() {
System.out.println("test1");
User user = new User(1, "张三",new Date());
return user;
}
@RequestMapping("/test2")
//@ResponseBody还可以用在handler的返回值上
public @ResponseBody List<User> test2() {
System.out.println("test2");
User user = new User(1, "张三");
User user1 = new User(1, "李四");
List<User> users = Arrays.asList(user, user1);
return users;
}
//如果返回值已经是字符串,则不需要转json,直接将字符串响应给客户端
@RequestMapping(value="/test3",produces ="text/html;charset=utf-8")//produces防止中文乱码
@ResponseBody
public String test3() {
System.out.println("test3");
// return "ok";
return "你好";
}
}
7.2.2 使用@RestController
Controller类上加了@RestController注解,等价于在类中的每个方法上都加了@ResponseBody
7.3 接收Json数据
7.3.1 使用@RequestBody
-
接收数据
public class User { private Integer id; private String username; private String password; private Boolean gender; }
@RequestMapping("/test4") public String test4(@RequestBody User user){//@RequestBody将请求体中的json数据转换为java对象 System.out.println(user); return "ok"; }
-
Ajax发送数据
function send_json() { var user = {id: 1, name: "shine"}; var json = JSON.stringify(user); $.ajax({ url: "${pageContext.request.contextPath}/json/test4", type: "post", data: json, contentType: "application/json", success: function (ret) { alert(ret); } }) }
7.4 Jackson常用注解
7.4.1 日期格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
public class User {
private Integer id;
private String name;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date birth;
}
7.4.2 属性名修改
//不再使用原属性名 而是“new_id”
@JsonProperty("new_id")
private Integer id;
7.4.3 属性忽略
@JsonIgnore
//生成Json时,忽略此属性
@JsonIgnore
private String name;
7.4.4 null和empty属性排除
Jackson默认会输出null值的属性,如果不需要,可以排除。
@JsonInclude(JsonInclude.Include.NON_NULL) 属性不输出null值
@JsonInclude(JsonInclude.Include.NON_EMPTY) 不输出empty属性(空串,长度为0的集合,null值)
7.4.5 自动化序列
@JsonSerialize(using = MySerializer.class)
private Double salary = 10000.126; //在输出此属性时,使用MySerializer输出
public class MySerializer extends JsonSerializer<Double> {
@Override
public void serialize(Double aDouble, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
//将Double salary 的值 四舍五入
String number = BigDecimal.valueOf(aDouble).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
//输出 四舍五入之后的值
jsonGenerator.writeNumber(number);
}
}
7.5 FastJson
7.5.1 导入依赖
<!-- FastJson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
7.5.2 安装FastJson
在
web.xml
中安装FastJson
<!-- 注解驱动 -->
<mvc:annotation-driven>
<!-- 安装FastJson转换器 -->
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!-- 声明转换类型 -->
<property name="supportedMediaTypes">
<list>
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
7.5.3 使用FastJson
@ResponseBody @RequestBody @RestController 使用方法不变
7.5.4 常用注解
- 日期格式化:@JSONField(format="yyyy/MM/dd")
- 属性名修改:@JSONField(name="birth")
- 忽略属性:@JSONField(serialize=false)
- 包含null值:@JSONField(serialzeFeatures=SerializerFeature.WriteMapNullValue)默认会忽略所有null值,有此注解会输出null
- @JSONField(serialzeFeatures=SerializerFeature.WriteNullStringAsEmpty)null的String输出为""
- 自定义序列化:@JSONField(serializeUsing=MySerializer2.class)
public class User2 {
@JSONField(serialize = false)
private Integer id;
@JSONField(name = "NAME", serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)
private String name;// ""
@JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue)
private String city;// null
@JSONField(format = "yyyy/MM/dd")
private Date birth;
@JSONField(serializeUsing = MySerializer2.class)
private Double salary; // 元
}
public class MySerializer2 implements ObjectSerializer {
@Override
public void write(JSONSerializer jsonSerializer, Object o, Object o1, Type type, int i) throws IOException {
Double value = (Double) o;
String text = value + "元"; //拼接“元”
jsonSerializer.write(text);
}
}
八、异常解析器
8.1 现有方案,分散处理
Controller中的每个Handler自己处理异常
此种处理方案,异常处理逻辑,分散在各个handler中,不利于集中管理
8.2 异常解析器,统一处理
Controller中的每个Handler不再自己处理异常,而是直接throws所有异常。
定义一个“异常解析器”集中捕获处理所有异常
此种方案,在集中管理异常方面,更有优势!
// 异常解析器
//任何一个Handler中抛出异常
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView = new ModelAndView();
if (e instanceof MyException1) {
//errorl.jsp
modelAndView.setViewName("redirect:/error1.jsp");
} else if (e instanceof MyException2) {
//error2.jsp
modelAndView.setViewName("redirect:/error2.jsp");
} else if (e instanceof MyException3) {
//error3.jsp
modelAndView.setViewName("redirect:/error3.jsp");
} else if (e instanceof MaxUploadSizeExceededException){
modelAndView.setViewName("redirect:/uploadError.jsp");
}
return modelAndView;
}
}
在
mvc.xml
中声明异常解析器
<!-- 异常解析器 -->
<bean class="异常解析器类名"/>
九、拦截器
9.1 作用
作用:抽取handler中的冗余功能
9.2 定义拦截器
执行顺序:preHandle-->postHandle-->afterCompletion
public class MyInterceptor implements HandlerInterceptor {
//在handler之前执行
//再次定义 handler中冗余的功能
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断登录状态
HttpSession session = request.getSession();
if (session.getAttribute("state") != null) {
return true; //放行 执行后续的handler
}
//中断之前响应请求
response.sendRedirect("/login.jsp");
return false; //中断请求,不再执行后续的handler
}
//在handler之后执行 响应之前执行
// 改动请求中的数据
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("post Handle");
}
//在视图渲染完毕后
//资源回收
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("after completion");
}
}
9.3 配置拦截路径
在
mvc.xml
中添加:
<!-- 拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- <mvc:mapping path="/inter/test1"/>-->
<!-- <mvc:mapping path="/inter/test2"/>-->
<mvc:mapping path="/inter/*"/>
<mvc:exclude-mapping path="/inter/login"/>
<bean class="拦截器类名"/>
</mvc:interceptor>
</mvc:interceptors>
十、上传
10.1 导入依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
10.2 上传表单
<form action="${pageContext.request.contextPath}/upload/test1" method="post" enctype="multipart/form-data">
file: <input type="file" name="source"><br>
<input type="submit" value="上传">
</form>
10.3 上传解析器
<!--
上传解析器 id必须是"multipartResolver"
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 最大可上传的文件大小 单位byte 超出后会抛出异常 -->
<property name="maxUploadSize" value="1048576"/>
</bean>
10.4 Handler
@Controller
@RequestMapping("/upload")
public class UploadController {
@RequestMapping("/test1")
public String test1(MultipartFile source, HttpSession session) throws IOException {
System.out.println("test1");
//获取上传文件的名称
String filename = source.getOriginalFilename();
//获取上传文件的类型
String contentType = source.getContentType();
//生成一个唯一的文件名
String uniqueFilename = UUID.randomUUID().toString();
//获取文件后缀
String extension = FilenameUtils.getExtension(filename);
//拼接唯一文件名
String uniqueFilename2 = uniqueFilename + "." + extension;
System.out.println(filename);
System.out.println(contentType);
//保存文件
// source.transferTo(new File("d:/abd.img"));
String realPath = session.getServletContext().getRealPath("/upload");
System.out.println("realpath" + realPath);
source.transferTo(new File(realPath + "\\" + uniqueFilename2));
return "index";
}
}
十一、下载
11.1 超链
<a href="${pageContext.request.contextPath}/download/test1?name=c93d8b03ecf53.jpg">下载</a>
11.2 Handler
@Controller
@RequestMapping("/download")
public class DownloadController {
@RequestMapping("/test1")
public void test1(String name, HttpSession session, HttpServletResponse response) throws IOException {
String realPath = session.getServletContext().getRealPath("/upload");
String filePath = realPath + "\\" + name;
//设置响应头告知浏览器,要以附件的形式保存内容 filename=浏览器显示的下载文件名
response.setHeader("content-disposition", "attachment;filename=" + name);
//响应过程
IOUtils.copy(new FileInputStream(filePath), response.getOutputStream());
}
}
十二、验证码
12.1 导入依赖
<!-- Kaptcha -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
12.2 声明验证码组件
在
web.xml
中添加:
<!-- 验证码组件 -->
<servlet>
<servlet-name>cap</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
<!-- 验证码的图片是否有边框 -->
<init-param>
<param-name>kaptcha.border</param-name>
<param-value>no</param-value>
</init-param>
<!-- 验证码字符长度 -->
<init-param>
<param-name>kaptcha.textproducer.char.length</param-name>
<param-value>4</param-value>
</init-param>
<!-- 验证码字符集 -->
<init-param>
<param-name>kaptcha.textproducer.char.string</param-name>
<param-value>abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</param-value>
</init-param>
<!-- 图片底色 -->
<init-param>
<param-name>kaptcha.backgroud.clear.to</param-name>
<param-value>211,229,237</param-value>
</init-param>
<!-- 将验证码存入session -->
<init-param>
<!--session.setAttribute("captchd","验证码")-->
<param-name>kaptcha.session.key</param-name>
<param-value>captcha</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>cap</servlet-name>
<!-- 生成验证码的路径 -->
<url-pattern>/captcha</url-pattern>
</servlet-mapping>
十三、REST
13.1 开发风格
是一种开发风格,遵从此风格开发软件,符合REST风格,则RESTFUL。
两个核心要求:
- 每个资源都有唯一的标识(URL)
- 不同的行为,使用对应的http-method
访问标识 | 资源 |
---|---|
http://localhost:8989/xxx/users | 所有用户 |
http://localhost:8989/xxx/users/1 | 用户1 |
http://localhost:8989/xxx/users/1/orders | 用户1的所有订单 |
请求方式 | 标识 | 意图 |
---|---|---|
GET | http://localhost:8989/xxx/users | 查询所有用户 |
POST | http://localhost:8989/xxx/users | 在所有用户中增加一个 |
PUT | http://localhost:8989/xx/users | 在所有用户中修改一个 |
DELETE | http://localhost:8989/xxx/users/1 | 删除用户1 |
GET | http://localhost:8989/xxx/users/1 | 查询用户1 |
GET | http://localhost:8989/xxx/users/1/orders | 查询用户1的所有订单 |
POST | http://localhost:8989/xxx/users/1/orders | 在用户1的所有订单中增加一个 |
13.2 使用
13.2.1定义Rest风格的Controller
@RequestMapping(value="/users",method=RequestMethod.GET)
等价于
@GetMapping("/users")
/**
* 查询:所有用户 id=xx 某一个用户
* 删除:id=xx 某一个用户
* 增加:在所有用户中 增加一个
* 修改:在所有用户中 修改一个
* <p>
* 资源:所有用户 /users
* id=xx 某一个用户 /users/{id}
*/
@RestController()
public class MyRestController {
@GetMapping("/users")
public List<User> queryUsers() {
System.out.println("query users with get");
User user = new User(1, "张三");
User user1 = new User(2, "李四");
return Arrays.asList(user, user1);
}
@GetMapping("/users/{id}")
public User queryOne(@PathVariable Integer id) {
System.out.println("query one user with get:" + id);
return new User(1, "张三");
}
@DeleteMapping("/users/{id}")
public String deleteOne(@PathVariable Integer id) {
System.out.println("delete one user with delete:" + id);
return "ok";
}
@PostMapping("/users")
public String saveUser(@RequestBody User user) {
System.out.println("save user with post:" + user);
return "ok";
}
@PutMapping("/users")
public String updateUser(@RequestBody User user) {
System.out.println("update user with put:" + user);
return "ok";
}
}
13.2.2 Ajax
<input type="button" value="queryALL" onclick="queryAll();">
<input type="button" value="queryOne" onclick="queryOne();">
<input type="button" value="saveUser" onclick="saveUser();">
<input type="button" value="updateUser" onclick="updateUser();">
<input type="button" value="deleteUser" onclick="deleteUser();">
<script>
function queryAll() {
$.ajax({
type: "get",
url: "${pageContext.request.contextPath}/users",
success: function (ret) {
console.log("查询所有:");
console.log(ret);
}
});
}
function queryOne() {
$.ajax({
type: "get",
url: "${pageContext.request.contextPath}/users/100",
success: function (ret) {
console.log("查询单个用户");
console.log(ret);
}
});
}
function saveUser() {
var user = {name: "shine", birth: "2020-12-12 12:12:20"}
$.ajax({
type: "post",
url: "${pageContext.request.contextPath}/users",
data: JSON.stringify(user),
contentType: "application/json",
success: function (ret) {
console.log("增加用户");
console.log(ret);
}
});
}
function updateUser() {
var user = {id: 1, name: "shine2", birth: "2020-12-13 12:12:20"}
$.ajax({
type: "put",
url: "${pageContext.request.contextPath}/users",
data: JSON.stringify(user),
contentType: "application/json",
success: function (ret) {
console.log("更新用户");
console.log(ret);
}
});
}
function deleteUser() {
$.ajax({
type: "delete",
url: "${pageContext.request.contextPath}/users/200",
success: function (ret) {
console.log("删除用户");
console.log(ret);
}
});
}
</script>
十四、跨域请求
14.1 域
域:协议+IP+端口
14.2 Ajax跨域问题
Ajax发送请求时,不允许跨域,以防用户信息泄露。
当Ajax跨域请求时,响应会被浏览器拦截(同源策略),并报错。即浏览器默认不允许ajax跨域得到响应内容。
互相信任的域之间如果需要ajax访问,(比如前后端分离项目中,前端项目和后端项目之间),则需要额外的设置才可正常请求。
14.3 解决方案
允许其他域访问
在被访问方的Controller类上,添加注解
@Crossorigin("http://localhost:8080")//允许此域发请求访问
- 携带对方cookie,使得session可用
- 在访问方,ajax中添加属性:withCredentials:true
十五、SpringMVC执行流程
十六、Spring整合
16.1 整合思路
此时项目中有两个工厂
- DispatcherServlet启动的springMVC工厂=负责生产C及springMVC自己的系统组件
- ContextLoaderListener启动的spring工厂==负责生产其他所有组件
- springMVC的工厂会被设置为spring工厂的子工厂,可以随意获取spring工厂中的组件
- 整合过程,就是累加:代码+依赖+配置。然后将service注入给controller即可
16.2 整合技巧
两个工厂不能有彼此侵入,即,生产的组件不能有重合。
在
mvc.xml
中添加:
<!--
告知SpringMVC哪些包中存在被注解的类
use-default-filters=true 凡是被 @Controller @Service @Repository 注解的类,都会被扫描
use-default-filters=false 默认不扫描包内的任何类,只扫描include-filter中指定的类
只扫描被@Controller注解的类
-->
<context:component-scan base-package="com.techoc" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
在
applicationContext.xml
中添加:
<!--
告知Spring
唯独不扫描eController注解的类
-->
<context:component-scan base-package="com.techoc" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>