Spring MVC
spring mvc
用来开发基于web的应用程序;
- 主流的mvc框架
- struts 1.x
- webwork
- Struts 2.x(webwork+struts 1.x)
- springmvc 更容易上手,相对安全;
- mvc思想
- model模型 数据domain
- view视图 数据的展现方式 jsp、jstl、el
- controller控制器 结合模型和视图 控制请求流程 servlet、controller
servlet作为控制器的缺点:
- 请求参数处理麻烦
- 当业务比较多时,servlet类的数量膨胀不好管理
controller优点: - 可以利用spring中各种思想,提高了程序的可扩展性;
spring mvc要做的事情
- 将url映射到Java类或Java类的方法;
- 封装用户提交的数据;
- 处理请求——调用相关的业务处理——封装相应的数据;
- 将响应数据进行渲染;
spring mvc使用
- 添加依赖
<dependencies>
<!-- spring mvc 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.21.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.21.RELEASE</version>
</dependency>
<!-- servlet, jsp, 标签库 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
- 添加spring配置文件spingmvc.cml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描控制器类-->
<mvc:annotation-driven/>
<!--扫描控制器类-->
<context:component-scan base-package="com.westos.controller"/>
</beans>
- 添加控制器
spring提供的前控制器 DispatcherServlet (实际上是一个servlet):作为一个统一入口,请求先进入这个入口,由它来分发请求,进入真正的控制器;
需要使用web.xml文件配置它的路径;
file-project Structure
生成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">
<!--配置前控制器(Servlet)-->
<servlet>
<servlet-name>s</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--指明spring配置文件的位置 根据它创建spring容器-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--tomcat启东市,就创建和初始化sevlet,同时创建spring容器-->
<!--1表示优先级-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>s</servlet-name>
<!--用来匹配所有路径-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 自定义的控制器
@Controller
public class 控制器类{
//在web的请求路径和java代码之间建立映射关系,以方法为单位进行映射
@RequestMapper("请求路径") //请求路径不能重复
public void 方法1(){
}
@RequestMapper("请求路径")
public void 方法2(){
}
}
@Controller
public class HelloController {
@RequestMapping("/m1")
public String m1(){
return "hello";
}
@RequestMapping("/m2")
public String m2(){
return "hello2";
}
}
- 添加视图
@Controller
public class 控制器类{
@RequestMapper("请求路径") //请求路径不能重复
public String 方法1(){
return "hello"; //返回值代表视图名字,但是注意它不是完整路径
}
@RequestMapper("请求路径")
public void 方法2(){
}
}
配置视图的完整路径
springmvc.xml
<!--配置视图的完整路径(前缀 后缀) 配置视图解析器-->
<mvc:view-resolvers>
<!--将请求视图路径补充完整 再利用请求转发跳至jsp-->
<mvc:jsp prefix="/" suffix=".jsp"/>
</mvc:view-resolvers>
- 处理请求参数
方法一:只要把方法的参数名称和请求的参数名称对象,springmvc 会将请求参数的值赋值给方法参数,并完成类型转换日期类型需要特殊处理,请求参数类型转换出错,会出现400错误, 日期可以使用 @DateTimeFormat(pattern=“日期格式”)
@RequestMapping("/m1")
public String m1(String name, Integer age, @DateTimeFormat(pattern = "yyyy-MM-dd")Date birthday){
System.out.println("执行了m1方法:"+name+":"+age+":"+birthday);
return "hello";
}
方法二:当请求参数较多时,可以把请求参数映射到java对象的属性上
@RequestMapping("/m2")
public String m2(User user){
System.out.println(user);
return "hello2";
}
public class User {
private String name;
private int age;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}
方法三:把参数信息包含在路径中
//PathVariable获取一个路径变量的值{id},然后赋值给方法参数
@RequestMapping("/deleteuser/{id}")
public String m3(@PathVariable("id") int a){
System.out.println("参数值:"+a);
return "hello";
}
- 处理模型数据
Model接口,需要调用Model 的相关方法把数据存入模型,页面上在再从模型中获取数据并显示;
model不需要自己创建,只需要在控制器方法上声明一个Model类型的参数,spring会帮我们创建好;跳转至jsp视图之前,springmvc会把model中的每一项数据存入request作用域;model只跟request作用域相关;
@RequestMapping("/c4")
public String c4(Model model){
model.addAttribute("name","zhangsan");
model.addAttribute("list",Arrays.asList(1,2,3,4));
return "model";
}
@RequestMapping("/c5")
public ModelAndView c5(){
ModelAndView mav = new ModelAndView();
mav.addObject("name","张三");
mav.addObject("list",Arrays.asList(1,2,3,4));
mav.setViewName("model");
return mav;
}
model.jsp
<html>
<head>
<title>Title</title>
</head>
<body>
{name}
<ul>
<c:forEach items="${list}" var="a">
<li>${a}</li>
</c:forEach>
</ul>
</body>
</html>
汉字乱码
配置web.xml,CharacterEncodingFilter由spring提供
<!--配置字符编码过滤器-->
<filter>
<filter-name>bb</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>bb</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
@Controller
public class EncodingController {
@RequestMapping("/b")
public String encoding(String name){
System.out.println(name);
return "encoding";
}
}
<body>
<form method="post" action="/b">
用户名:<input type="text" name="name">
<br>
<input type="submit" value="提交">
</form>
</body>
使用传统servlet中的类
HttpServletRequest,HttpServletResponse,HttpSession,spring把这些类都创建好了,都可以作为控制器方法的参数,只传入方法就可以了;
El表达式中的变量名会依次查找四个作用域,直到找到为止;如果想直接指定作用域查找如:${sessionScrope.name}
只在session中查找如果找不到就不再查找;
异常处理
局部的异常处理器,只针对某一个控制器;
@ExceptionHandler(Exception.class) // 异常处理器, 可以指定具体的异常类型
public String error(Exception e){
return "error";
}
全局的异常处理器,针对所有的控制器;
@ControllerAdvice
public class ExcwptionAspect {
@ExceptionHandler(Exception.class)
public String exception(Exception e){
System.out.println("全局异常处理方法");
return "error";
}
}
局部得异常处理优先级高于全局的异常处理;
文件上传
- pom.xml添加依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
- springmvc.xml中对上传文件表单做特殊配置
<!--配置上传文件表单解析器 id是固定的-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
- 编辑表单页面
enctype规定表单格式
<form action="请求地址" method="post" enctype="multipart/form-data">
<input type="file" name="请求参数名">
</form>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" id="image">
<input type="submit" value="提交">
</form>
</body>
</html>
@Controller
public class Uploadfile {
@RequestMapping("/upload")
public String upload(MultipartFile image) throws IOException {
System.out.println("上传文件原始名称:"+image.getOriginalFilename());
System.out.println("上传文件类型:"+image.getContentType());
System.out.println("上传文件大小:"+image.getSize());
//上传的文件生成为临时文件,当请求结束时,此临时文件会被删除
//另存临时文件
image.transferTo(new File("d:\\"+image.getOriginalFilename()));
return "hello";
}
}
图片预览
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" id="image" onchange="preview()">
<input type="submit" value="提交">
</form>
<img src="" id="preview" >
<script>
function preview() {
var image = document.getElementById("image");
var file = image.files[0];
var reader = new FileReader();
reader.onload=function () {
document.getElementById("preview").src=this.result;
};
reader.readAsDataURL(file);
}
</script>
</body>
</html>
对上传文件的限制
从客户端角度限制
<input type="file" name="image" id="image" onchange="preview()" accept=".jpg,.png,.bmp">
服务器端要对上传的文件做检查
图片修改大小 成功
假图片修改大小 报异常
对文件大小做控制
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 限制上传文件的总大小,单位是字节-->
<property name="maxUploadSize" value="2000"></property>
</bean>
springmvc对静态文件的处理(图片、html、css、js)
springmvc 默认把所有路径都当做了控制器的路径了;
重定向请求
@RequestMapping("test1")
public String test1(){
//重定向到/test2 控制器
return "redirect:/test2"
}
spring中的 拦截器
请求到达控制器之前,先经过拦截器,才到达控制器;
Filter过滤器属于web组件,工作于tomcat;
HandlerIntercepter 拦截器属于spring提供的;
拦截器和过滤器的功能相似;
- 编写拦截器
package com.westos.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class Interceptor implements HandlerInterceptor {
//在控制器方法执行前被调用,返回true放行请求,返回false则拦截请求
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("======>进入了 interceptor preHandle");
return true;
}
//在控制器方法执行后被调用
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("======>进入了 interceptor postHandler");
}
//在控制器和视图都完成后被调用
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("======>进入了 interceptor afterCompletion");
}
}
- 在spring.xml里配置拦截器
<!--配置上传文件表单解析器 id是固定的-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
<!--
/user/* 只能匹配类似于 /user/insert 不能匹配/user/aaa/insert这种
/user/** 则都可以匹配
-->
<!--扫描拦截器-->
<context:component-scan base-package="com.westos.interceptor"/>
<!--配置 要拦截那些控制器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/*"/>
<ref bean="interceptor"/>
</mvc:interceptor>
</mvc:interceptors>
拦截器拦截的顺序,可以通过配置拦截器的顺序来决定;
对json数据的支持
- 添加依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
- @ResponseBody注解,加在控制器方法上,结果就是将控制器方法的返回转换为json字符串,并写入响应;
- @RestController注解,加在控制器类上,表示所有的控制器方法都加了@ResponseBody注解
@RestController //相当于@Controller 认为每个控制器的方法上都加了 @ResponseBody
public class JsonController {
// @RequestMapping(path = "/json",method = RequestMethod.GET)
@GetMapping(path = "/json")
@ResponseBody //获取控制器方法的返回值,并且把返回值转换为json 字符串,并作为响应体内容返回
public Student json(){
Student student = new Student();
student.setId(1);
student.setName("王小帅");
return student;
}
}
-
其他注解
@RequestMapping 映射路径,不区别get,post
RequestMapping细分:
@GetMapping 专门匹配 get请求 一般对应查询操作
@PostMapping 匹配post请求 一般对应新增操作
@DeleteMapping 匹配delete请求 对应删除操作
@PutMapping 匹配put请求 对应修改操作
-
RequestBody
把请求中的json字符串,转换成java对象
@PostMapping(path = "/json")
@ResponseBody //获取控制器方法的返回值,并且把返回值转换为json 字符串,并作为响应体内容返回
public Student json(){
Student student = new Student();
student.setId(1);
student.setName("王小帅");
return student;
}
@PostMapping("/json3")
//@RequestBody作用是吧请求体中的json字符串,转换为java中的对象
public void json3(@RequestBody Student student){
System.out.println(student);
}
<body>
<input type="button" value="获取学生数据" onclick="getStudent()">
<input type="button" value="发送学生数据" onclick="sendStudent()">
<script>
function getStudent() {
var xhr = new XMLHttpRequest();
xhr.onload=function () {
var student = JSON.parse(xhr.responseText);
console.log(student);
};
xhr.open("post","/json1",true)
xhr.send();
}
function sendStudent() {
var student={"id":2,"name":"李四"};
var xhr = new XMLHttpRequest();
xhr.open("post","/json3",true);
xhr.setRequestHeader("content-type","application/json");
//将js对象 转为json字符串 并作为请求体发送给服务器
xhr.send(JSON.stringify(student));
}
</script>
</body>
ajax 请求跨域
cors 技术名称,用来授权给不同的机器来访问我的资源;
ip不一样或端口号不一样都认为是不同的域,称之为跨域;
案例:
localhost:8080 提供控制器,返回json数据
localhost:9090 ajax 获取 8080上的json数据,默认情况下没有权限访问;
需要在8080上通过一种cors的技术允许其他域的机器访问我的数据
提供了@CrossOrigin("允许访问我的数据的ip地址")
注解加在方法上,表示只有这个方法允许跨域;也可以加在控制器类上,表示这个类的所有方法都允许跨域;仅仅针对于ajax;
上一篇: 小程序接口对接
下一篇: Spring MVC