spring-mvc

是基于spring的, ioc控制反转, di依赖注入, aop面向切面编程

model 模型 - 数据和操作数据的逻辑(狭义的就是数据) 包括了实体类和业务类(例如 User,UserService)
view 视图 - 数据展现, 包括(jsp, jstl, el)
controller 控制器 把模型和视图关联在一起, 包括servlet

让程序的各个部分分工清晰,各司其职。让程序的可维护性提高。

使用步骤:

1. 在pom.xml文件中添加 spring-webmvc等6个依赖

注意pom.xml文件中需要添加<packaging>war</packaging> (决定了是maven的web项目)
然后手动补充一个src/main/webapp目录, src/main/webapp/WEB-INF/web.xml
在配置tomcat时选择 项目名:war exploded

2. 在spring-mvc.xml中添加mvc的xml命名空间

3. 前控制器 – 统一的入口

它是由spring-mvc 提供的的一个servlet: DispatcherServlet
在web.xml 对它进行配置, maven 的目录结构中,该文件的路径是 src/main/webapp/WEB-INF/web.xml

<!-- 职责: 
   1.作为统一入口
   2.创建spring容器
   3.在tomcat启动时,就把spring容器创建好
-->
<servlet>
	<servlet-name>springmvc</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- 指明了spring配置文件的位置 -->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc.xml</param-value>
	</init-param>
	<!-- 在tomcat启动时,就创建这个servlet,并初始化该servlet -->
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>springmvc</servlet-name>
	<url-pattern>/</url-pattern> 
	<!-- 给 DispatcherServlet 指定路径 
	假设浏览器  /hello  没有,会找 / 这个路径
			   /s1     没有,会找 / 这个路径
			   
			 只要没有其他servlet能够精确匹配这个请求路径,这个请求都会被 / 的这个servlet来处理
	-->
</servlet-mapping>

4. 控制器

@Controller
public class 控制器类{
	
	@RequestMapping("/请求路径")
	public String 控制器方法(){
		
	}
}

// 控制器类要交给spring容器管理
并spring-mvc中加入:<mvc:annotation-driven/> 来启用mvc的相关功能

5. 处理视图

控制器方法的返回值是视图名
前缀+视图名+后缀就构成了最终jsp视图路径, 控制器会根据视图路径用请求转发跳转过去

<mvc:view-resolvers>
	<mvc:jsp prefix="/" suffix=".jsp"/>
</mvc:view-resolvers>

6. 请求被处理的流程

– 1) 请求从浏览器发送到tomcat, 地址为:http://localhost:8080/hello 2) tomcat先将该路径与servlet进行匹配,结果没有精确的地址与之匹配,就找到了 / 这个地址
/ 地址对应的是DispatcherServlet,由它来处理请求
3) DispatcherServlet 再到spring容器中找控制器类
把每个控制器中带有@RequestMapping的方法路径与请求路径进行匹配,
4) 匹配到了就执行该方法, 如果匹配不到报404错误
5) 根据方法的返回结果找到jsp视图
6) 由jsp视图生成html代码作为响应返回给浏览器

7. 以maven插件方式运行tomcat

在pom.xml中加入插件信息:

<plugin> <!-- 配置一个内嵌的tomcat -->
	<groupId>org.apache.tomcat.maven</groupId>
	<artifactId>tomcat7-maven-plugin</artifactId>
	<version>2.2</version>
	<configuration>
		<port>8080</port> <!-- 端口号 -->
		<path>/</path> <!-- 项目在tomcat中的访问路径 -->
		<uriEncoding>utf-8</uriEncoding> <!-- 设置get请求中解码的字符集,解决get请求中的中文乱码问题 -->
	</configuration>
</plugin>

然后在右侧的maven菜单中找到tomcat7插件,运行tomcat7:run这个命令

8. 接收请求参数

  1. 把方法参数和请求参数直接对应
@RequestMapping("/hello")
    // 接收参数:把请求参数与控制器方法参数相对应,并且会帮助我们完成数据类型的转换
    // http://localhost:8080/hello?username=zhangsan&age=18&birthday=1996-1-1
    public String abc(String username, Integer age,@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
        System.out.println("进入了spring控制器的abc方法");
        System.out.println("username:" +username + " age:" +age+" birthday:" +birthday);
        // 方法的返回值,称为视图名,底层是请求转发
        return "hello";
    }
  1. 写一个实体类型,把实体类的属性与请求参数对应
    实体类:
package com.westos.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

public class User {
    @JsonProperty("name")//对象转为json时改变属性名
    private String username;
    @JsonIgnore
    private Integer age;
    @DateTimeFormat(pattern = "yyyy-MM-dd") // 接收表单参数的日期格式
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // 对象转为json时的日期格式
    private Date birthday;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }
}
@RequestMapping("/s2")
    public String s2(User user) {
        System.out.println("请求参数是:" + user);
        return "hello";
    }
  1. 路径参数(把参数值不是写在?之后,而是包含在路径当中)
    /deleteUser?id=1 传统写法
    /deleteUser/1 /zhangsan 路径参数 要删除1号用户
@RequestMapping("/deleteUser/{id}/{name}") // {id} 用来获取路径中的参数值
    // @PathVariable("id") 把获取到的值,再赋值给方法参数
    public String s3(@PathVariable("id")  int x, @PathVariable("name") String n) {
        System.out.println("x:"+x +" n:" +n);
        return "hello";
    }

路径参数可以通过@PathVariable 与方法参数一一对应

9. 使用模型数据

Model 接口, 代表了模型数据, 它也是加在控制器方法之上 可以直接使用它的实现类ModelMap或Map
向模型添加数据: model.addAttribute(“变量名”, 值);
model中的数据在转发之前,都会被存入request作用域

springmvc中常见错误:

400 错误:发生在请求参数赋值给方法参数时,发生了类型转换问题
例如:字符串转整数
字符串转日期 对于日期需要加 @DateTimeFormat(pattern=“日期格式”)

中文乱码问题:
可以配置spring提供的字符编码过滤器

<!-- spring 提供的字符编码过滤器 -->
<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>

静态资源的 404 错误
静态资源是指 图片、css、html、js
原因是DispatcherServlet的路径是/ 跟tomcat中处理图片的Servlet路径冲突了
所以所有图片的请求被DispatcherServlet所处理,把图片的路径当做了控制器路径
解决办法:
在spring配置文件中加入:

<mvc:default-servlet-handler/>

10. json格式的响应

js 代表javascript object 对象, notation 标记

之前返回的大部分响应类型 text/html
json也是一种响应格式:也是文本格式的,更类似于javascript的对象写法
{
“username”:“张三”,
“age”:18
}
如果转换json时出现了错误: No converter found for return value of type:
是因为json转换的jar包没有加, 可以在pom文件中加入相应的依赖:

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.1</version>
</dependency>

在控制器方法上添加@ResponseBody注解,它可以把方法的返回值转换成json字符串

java中 map以及实体类 会被转换为json中的对象: {属性名:属性值}
java中 list或数组 会被转换为json中的数组 : [元素1, 元素2, …]

jackson常见注解:
@JsonFormat 可以对日期类型进行格式化
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”, timezone = “GMT+8”) 注意加8小时才是北京时间
@JsonIgnore 可以在转换时忽略加了@JsonIgnore的属性
@JsonProperty(“新的属性名”) 可以在转换时改变属性的名字

11. 在springmvc中使用servlet对象:

// 只要在控制器方法上加入request,response,session类型的参数,springmvc框架会把这些对象准备好作为方法参数传入
public String s1(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
}

还可以使用@CookieValue注解获取cookie的值

@RequestMapping("/servlet2")
// @CookieValue("user") 含义是到请求中找一个名为user 的cookie
public String s2(@CookieValue("user") String abc ) {
	System.out.println("user cookie的值是: " + abc);
	return "hello";
}

12. springmvc中的重定向

在视图名前面添加 redirect:,这时候springmvc就会把字符串当做重定向进行跳转,而不是再通过视图解析器进行解析

重定向中的值传递:

方法1: 使用了session传值
优点:信息安全、对象类型可以复杂
缺点:会占用服务器内存

@RequestMapping("/c1")
public String c1(HttpSession session) {
	System.out.println("c1....");
	// name=zhangsan 希望name传递给c2使用
	session.setAttribute("name", "zhangsan");
	return "redirect:/c2";
}

@RequestMapping("/c2")
public String c2(HttpSession session) {
	System.out.println("c2....");
	System.out.println("接收name:" +session.getAttribute("name"));
	session.removeAttribute("name"); // 用完后及时清除
	return "hello";
}

方法2:利用重定向地址后跟请求参数的方式
优点:不会占服务器内存,可以向站外地址传递参数
缺点:数据有大小限制、有安全问题、不能是复杂类型

@RequestMapping("/c1")
public String c1() {
	System.out.println("c1....");
	// name=zhangsan 希望name传递给c2使用
	return "redirect:/c2?name="+"zhangsan";
}

@RequestMapping("/c2")
public String c2(String name) {
	System.out.println("c2....");
	System.out.println("接收name:" + name);
	return "hello";
}

方法3:springmvc提供的办法

@RequestMapping("/c1")
	// RedirectAttributes 由springmvc提供,专门在重定向时传参
	public String c1(RedirectAttributes ra) {
		System.out.println("c1....");
	//        ra.addAttribute("name", "张三"); // 使用了方法2即方法名后跟参数
		ra.addFlashAttribute("name","张三"); // 使用了方法1即session
		return "redirect:/c2";
	}
	
	@RequestMapping("/c2")
	//@ModelAttribute("参数名")来接收重定向参数
	public String c2(@ModelAttribute("name") String aaa) {
		System.out.println("c2....");
		System.out.println("接收name:" + aaa);
		return "hello";
	}

13. 管理异常

方式1:传统方式管理异常
// 自己用try -catch 管理异常

@RequestMapping("/exception1")
public String e1() {
	try {
		int i = 1 / 0;
		return "hello";
	} catch (Exception e) {
		return "error";
	}
}

方法2:使用@ExceptionHander , 把它加在要捕获异常的方法之上, 例如:

@ExceptionHandler(ArithmeticException.class)
public String catch1(ArithmeticException e) {
	System.out.println("catch1: "+e.getMessage());
	return "error";
}

注意:

  1. 该方法只能捕获本控制器类出现的异常
  2. 匹配异常类型时,会匹配一个最接近的异常类型

要做全局的异常处理:

@ControllerAdvice 全局控制器的通知类
加在通知类上,这样当某个控制器出现异常时,先找本类的异常处理器,
如果找不到,那么再到通知类中找全局的异常处理器

// 所有控制器的通知类
@ControllerAdvice
public class GlobalHandler {
    // 处理异常方法
    @ExceptionHandler(ArithmeticException.class)
    public String catch1(ArithmeticException e) {
        System.out.println("catch1: "+e.getMessage());
        return "error";
    }
    // 处理异常方法
    @ExceptionHandler(Exception.class)
    public String catch2(Exception e) {
        System.out.println("catch2: "+e.getMessage());
        return "error";
    }
}

14. 文件上传

jsp

<form action="/upload" method="post" enctype="multipart/form-data">

    <input type="text" name="username">
    <input type="file" name="abc">
    <input type="submit" value="提交">
</form>

controller

@RequestMapping("/upload")
public String upload(String username, MultipartFile abc) throws IOException {
	System.out.println("原始名:"+abc.getOriginalFilename());
	System.out.println("大小:"+abc.getSize());
	System.out.println("类型:"+abc.getContentType());
	// 把上传的文件保存到哪里
	abc.transferTo(new File("d:\\"+abc.getOriginalFilename()));
	return "hello";
}

需在springmvc配置文件中配置:

<!-- 上传文件的解析器  -->
<bean id="multipartResolver"
	  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<property name="maxUploadSize" value="1000000"/><!--自己设置属性-->
</bean>