SpringMVC
Spring为展现层提供的基于MVC设计理念的优秀的Web框架。
SpringMVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须实现任何接口。
MVC:M 模型 V 视图 C 控制器
搭建过程:
1.导入jar包
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
2.在web.xml 中配置springMVC的核心(前端)控制器DispatcherServlet。
3.创建一个POJO,在此类上加上@Controller注解,SpringMVC就会将此类作为控制层加载,让其处理请求响应。
4.在控制层中,需要在方法上设置@RequestMapping(value=”xxx”)。
SpringMVC就是通过此注解,将请求与控制层中的方法相匹配。
5.处理请求的方法会返回一个字符串,即视图名称。最终会通过配置文件中配置的视图解析器实现页面跳转
1.HelloWord
1.1web.xml配置
在web.xml 中配置SpringMVC的核心控制器 DispatcherServlet
web.xml配置三步走:
1.配置核心控制器
2.配置编码过滤器
3.配置 HiddenHttpMethodFilter,用来使用REST。
<?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_3_1.xsd"
version="3.1">
<!--配置 DispatcherServlet-->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置 DispatcherServlet 的一个初始化参数:配置SpringMVC配置文件的位置和名称-->
<!--实际上也可以不通过 contextConfigLocation 来配置 SpringMVC的配置文件,而使用
默认的配置文件:/WEB-INF/<servlet-name>-servlet.xml-->
<init-param>
<!--contextConfigLocation 这个名字是固定的,上下文配置路径,是SpringMVC为我们设置的一个参数,用来更改配置文件的名字和地址。-->
<param-name>contextConfigLocation</param-name>
<!--写 SpringMVC配置文件所在的地址-->
<param-value>classpath:com/guolian/SpringMVC/helloword/HelloWord.xml</param-value>
</init-param>
<!--用来设置 Servlet 的加载时间,默认在第一次访问时加载。
若设置此标签:会将Servlet的加载时间提前到项目启动时。
此标签需要写正整数,若写负数或0则使用默认配置。
值越小,优先级越高。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
1.2SpringMVC.xml
在Spring配置文件中配置
<?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: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/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描组件,加载注解-->
<context:component-scan base-package="com.guolian.SpringMVC.helloword"></context:component-scan>
<!--配置视图解析器:如何把 hello() 方法返回值解析为实际的物理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/"></property>
<property name="suffix" value=".jsp"></property>
<!--视图解析器可以配置多个,order配置视图的优先级。
一般情况下,只会创建一个。-->
<property name="order" value="1"></property>
</bean>
</beans>
1.3Controller
使用 @RequestMapping 注解来映射请求的URL:
当在页面中点击超链接<a href="helloword">。。。</a>
时,会自动跳转到hello()方法。
@Controller
public class Helloword {
/**
* 1.使用 @RequestMapping 注解来映射请求的URL
* 2.返回值会通过视图解析器解析为实际的物理视图。对于InternalResourceViewResolver 视图解析器,会做如下解析:
前缀和后缀在SrpingMVC.xml 中配置。
* prefix(前缀) + 视图名称(return) + suffix(后缀)
本例中:/views/success.jsp
//会自动跳转到 web/views/success.jsp页面
*/
@RequestMapping("/helloword")
public String hello(){
System.out.println("helloword");
//success :视图名称
return "success";
}
}
1.4JSP
1.4.1index.jsp
<body>
<a href="helloword">Hello World</a>
</body>
1.4.2success.jsp
<body>
<h1>SUCCESS</h1>
</body>
2.@RequestMapping
- @RequestMapping :设置请求映射, 把请求和控制层中的方法设置映射关系。
- @RequestMapping 可以加在类和方法上。
- 若类和方法上都加了 value 属性。则应该一层一层的访问,先访问类,再访问类中的方法。
2.1value属性
value:用来设置请求路径。当请求路径和value属性值一致时,则该注解所标注的方法即为处理请求的方法。
/**
* 当请求路径和 @RequestMapping 的value属性值一致时,则该注解所标注的方法即为处理请求的方法。
当属性只有一个value时。value 可以省略。
@RequestMapping(value = "/test") == @RequestMapping("/test")
*/
@RequestMapping(value = "/test")
public String test(){
System.out.println("SUCCESS");
return "success";
}
2.2method属性
method :用来设置请求方式,只有当客户端发送请求的方式与method一致时,才能处理请求。
2.2.1请求方式:
method = RequestMethod.GET //用来查询
method = RequestMethod.PUT //用来添加
DELETE //用来删除
POST //用来添加
HEAD
PATCH
OPTIONS
TRACE
实例:
//一个请求路径,多个处理方法。
@RequestMapping(value = "/test",method = RequestMethod.POST)
public String testPOST(){
System.out.println("SUCCESS.POST");
return "success";
}
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String testGET(){
System.out.println("SUCCESS.GET");
return "success";
}
2.3params属性
params:用来设置请求参数。(客户端传到服务器的数据)
它支持表达式。
例如: username: 请求参数里必须包含 username
!username: 请求参数里不能有 username
username=admin: 请求参数里必须包含 username=admin
username!=admin: 请求参数里不能包含 username=admin
@RequestMapping(value = "/test",params = {"username","age!=12"})
2.4headers
headers :设置请求头的信息,客户端所发送请求的请求头信息一定要和headers属性中所设置的一致。
@RequestMapping(value = "/test",headers = {"Accept-Language=zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"})
2.5Ant方式的访问路径
Ant支持3中匹配符:
?:匹配任意一个字符
*:匹配任意字符
**:任意匹配多层(几个”星”就几层)目录
//http://localhost:8080/SpringMVC01/avc/Antmm/testAnt
@RequestMapping(value = "/*/Ant??/testAnt")
public String testAnt(){
System.out.println("SUCCESS.GET");
return "success";
}
2.6映射请求占位符
@PathVariable(“….”):获取占位符中 …. 的值,并赋值给 形参里的 ….。
/**
* 以前: localhost:8080/SpringMVC01/testREST?id=1001&username=admin
* 现在: localhost:8080/SpringMVC01/testREST/1001/admin
*/
@RequestMapping("/testREST/{id}/{username}")
public String testREST(@PathVariable("id")Integer id,@PathVariable("username")String username){
//@PathVariable("id"):获取占位符中 id 的值,并赋值给 形参里的 id。
//@PathVariable("id"):获取占位符中 username 的值,并赋值给 形参里的 username。
System.out.println("id:"+id+",username:"+username);
return "success";
}
3.REST
REST:(资源)表现层状态转化。是目前最流行的一种互联网软件架构。
资源:网络上的实体。
表现层:把资源具体显示出来的形式。
状态转化:每发出一个请求,就代表了客户端和服务器的一次交互过程。
- HTTP协议里四个表示操作的动词:GET、POST、PUT、DELETE。
- GET:获取资源 POST:新建资源 PUT:更新资源 DELETE:删除资源
使用REST出现405错误
解决方法:
以上代码在控制台可以输出PUT但是页面405,所以在success.jsp目标页面的头信息加了这样一段
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8" isErrorPage="true"%>
REST使用
jsp:
<body>
<a href="testREST/1001">测试GET请求</a>
<br/>
<form action="testREST" method="post">
<input type="submit" value="测试POST"/>
</form>
<br/>
<form action="testREST" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="测试PUT"/>
</form>
<br/>
<form action="testREST/1001" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="测试DELETE"/>
</form>
</body>
Controller:
@Controller
public class RESTController {
@RequestMapping(value = "/testREST/{id}",method = RequestMethod.GET)
public String getUserById(@PathVariable("id")Integer id){
System.out.println("GET,id:"+id);
return "success";
}
@RequestMapping(value = "/testREST",method = RequestMethod.POST)
public String InsertUser(){
System.out.println("POST");
return "success";
}
@RequestMapping(value = "/testREST",method = RequestMethod.PUT)
public String updateUser(){
System.out.println("PUT");
return "success";
}
@RequestMapping(value = "/testREST/{id}",method = RequestMethod.DELETE)
public String DeleteUser(@PathVariable("id")Integer id){
System.out.println("DELETE,id:"+id);
return "success";
}
}
filter:
<filter>
<filter-name>hidden</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hidden</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4.处理请求数据
4.1获取请求参数
在SpringMVC获取客户端传递的数据:
- 在处理请求的方法中,加入相对应的形参,保证形参参数名与传递的数据参数名一致,就可以自动赋值。
<form action="param" method="post">
username:<input type="text" name="username"/><br/>
password:<input type="text" name="password"/><br/>
<input type="submit" />
</form>
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(String username,String password){
//形参名与传递数据的参数名一致。
System.out.println("username:"+username+",password:"+password);
return "success";
}
- 当不满住赋值条件时,可以使用@RequestParam注解的value属性来指定映射关系。
@RequestParam属性:required 设置形参是否必须被赋值。默认是true。
defaultValue 若形参所得值为null,则设置一个默认值。
required 和 defaultValue 可以搭配使用,如果形参没有被赋值,则可以使用默认值。
@Controller
public class ParamController {
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(@RequestParam(value = "username",required = false,defaultValue = "admin")String name, String password){
System.out.println("username:"+name+",password:"+password);
return "success";
}
}
4.1.1@RequestParam
@RequestParam:设置映射关系。
**value**:用来指定映射关系,里面写传递的参数名。
**required**:设置形参是否必须被赋值,默认为true。
如果是true,没有赋值就会报错。false,不报错。
**defaultValue**:设置形参的默认值。如果传递过来的参数是null,则为形参使用默认值。
4.1.2@RequestHeader
@RequestHeader:获取请求头的信息。
与@RequestParam的使用方法一致。
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(@RequestHeader(value = "Accept-Language")String language){
System.out.println("language:"+language);
return "success";
}
4.1.3@CookieValue
@CookieValue:在处理请求的方法上,获取Cookie的信息。
与@RequestParam的使用方法一致。
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(@CookieValue(value = "JSESSIONID")String JSESSIONID){
System.out.println("JSESSIONID:"+JSESSIONID);
return "success";
}
4.2使用POJO作为参数
可以使用POJO获取客户端数据。
要求:实体类中的属性名一定要和页面中表单元素的name属性值一致。而且支持级联关系。
级联关系:
查找address对象下的province属性:<br />
name=”address.province”
jsp:
<body>
<form action="param" method="post">
username:<input type="text" name="username"/><br/>
password:<input type="text" name="password"/><br/>
age:<input type="text" name="age"/><br/>
<!--级联关系,查找address对象下的province属性-->
省:<input type="text" name="address.province"/><br/>
市:<input type="text" name="address.city"/><br/>
县:<input type="text" name="address.country"/><br/>
<input type="submit" />
</form>
</body>
controller:
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(User user){
System.out.println("user:"+user);
return "success";
}
4.3使用Servlet原生API作为参数
可以通过设置形参的方式,获取ServletAPI。
MVC的Hander方法可以接受哪些 ServletAPI类型的参数:
1.HttpServletRequest 2.HttpServletResponse 3.HttpSession
4.java.security.Principal 5.Locale 6.InputStream
7.OutputStream 8.Reader 9.Writer
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(User user, HttpServletRequest request, HttpServletResponse response){
String username = request.getParameter("username");
System.out.println(username);
return "success";
}
4.4ModelAndView类
管理模型(实体)和视图(页面)。
在最底层仍然是使用的 request.setAttribute() 和 request.getRequestDispatcher(“”).forward(request,response);
设置request的作用域和请求转发。
@RequestMapping(value = "/param",method = RequestMethod.POST)
public ModelAndView param(){
ModelAndView mav = new ModelAndView();
//往Request作用域中放值。
mav.addObject("username","root");
//设置视图名称,实现页面跳转
mav.setViewName("success");
return mav;
}
4.5三种方式往作用域放值
4.5.1ModelAndView
- ModelAndView
4.5.2Map
- 与 ModelAndView的执行过程是一样的(所使用的是同一段源码)。
(因为在ModelAndView的底层就是使用这个Map存储参数和赋值)
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(Map<String,Object> map){
//向作用域中放值
map.put("username","admin");
//返回视图名称
return "success";
}
4.5.3Model
- 与 ModelAndView的执行过程是一样的(所使用的是同一段源码)。
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(Model model){
model.addAttribute("username","张三");
return "success";
}
4.5.4总结:
根据ModelAndView源码调试。不管使用哪种方式给作用域赋值,最终都会把Model数据和View数据封装到 ModelAndView中。
5.视图解析
return "success";
return "redirect:/success.jsp";
return "forward:/success.jsp";
//这里面的返回值,就是视图名称。
5.1SpringMVC如何解析视图
不管控制器返回一个 Strng、ModelAndView、View 都会转换为 ModelAndView对象,由视图解析器(InternalResourceViewResolver)解析对象,然后,进行页面的跳转。
5.1.1View作用:
处理模型数据,实现页面跳转(转发、重定向)。
5.1.2View类型:
InternalResourceView:转发视图。(不支持JSTL)
JstlView:转发视图。(支持JSTL)
RedirectView:重定向视图。
5.1.3View底层源码
redirect:/index.jsp(重定向到index.jsp)
最底层:response.sendRedirect()
forward:/index.jsp(转发到index.jsp)
最底层:request.forward()
6.Spring中的编码过滤器
编码过滤器必须配置在所有过滤器的第一个。(Spring 里有缓存)
在 init-param 标签里所写的 param-name 一定是class 里的一个属性。
<!--设置编码过滤器-->
<filter>
<filter-name>Cha</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--为编码过滤器类里的属性赋值,确定编码格式-->
<!--在 init-param 里所写的一定是class里的一个属性-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Cha</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
7.静态资源处理
静态资源一般都是交给Tomcat的默认servlet 来处理:DefaultServlet。
然而当我们重新定义了一个servlet(dispatcher-servlet)且url-pattern的值与其相同时。
系统就会默认的先执行我们所定义的servlet,此时就会造成静态资源访问不到。
就需要如下两行代码来设置:当前servlet找不到资源时,交给DefaultServlet来处理。
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--mvc驱动,只要放在这就会自动实现功能-->
<mvc:annotation-driven></mvc:annotation-driven>
dispatcher-servlet.xml:
<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">
<context:component-scan base-package="com.guolian.crud"></context:component-scan>
<bean id="views" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
<!-- <!–视图解析器可以配置多个,order配置视图的优先级–>
<property name="order" value="1"></property>-->
</bean>
<!--配置Tomcat中默认的servlet:DefaultServlet
当DefaultServlet所设置的 url-pattern 和开发人员所配置的servlet的 url-pattern相同
以开发人员所配置的servlet优先。-->
<!--当客户端发送请求,由于DefaultServlet所设置的 url-pattern 和dispatcher-servlet的url-pattern相同,
因此先通过 dispatcher-servlet 来处理请求,找该请求是否有相对应的处理器,有则处理,无则交给 DefaultServlet处理-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--mvc驱动,只要放在这就会自动实现功能-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
8.SpringMVC处理JSON
1.导入jackson的jar包
jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar
2.在springMVC的配置文件中开启MVC驱动
<mvc:annotation-driven></mvc:annotation-driven>
3.在处理JSON的方法上加上 注解 @ResponseBody
4.将要转换为JSON,且响应到客户端的数据,作为方法的返回值直接返回。
8.1例子:
jsp页面:
<head>
<title>$Title$</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function () {
$("#btn").click(function () {
$.ajax({
url:"testJson",
type:"POST",
dataType:"json",
/*msg:响应回来的数据*/
success:function (msg) {
/*遍历JSON数组,i为数组下标*/
for(var i in msg){
var emp = msg[i];
alert("id="+emp.id+",lastName="+emp.lastName);
}
}
});
});
});
</script>
</head>
<body>
<input id="btn" type="button" value="测试Ajax">
<a href="testJson">测试json</a>
</body>
java代码:
@Controller
public class TestJsonController {
@Autowired
private EmployeeDao employeeDao;
/**
* SpringMVC 处理 JSON
* @ResponseBody :响应体,将该方法的返回值作为响应返回到客户端。
* 就算返回的是字符串,也会直接显示到客户端。
*/
@RequestMapping("/testJson")
@ResponseBody
public Collection<Employee> testJson(){
Collection<Employee> all = employeeDao.getAll();
return all;
}
}
8.2对JSON的简单操作:
<head>
<title>$Title$</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function () {
$("#btn").click(function () {
$.ajax({
url: "testJson",
type: "POST",
dataType: "json",
/*msg:响应回来的数据*/
success: function (msg) {
/*/!*遍历JSON数组,i为数组下标*!/
for(var i in msg){
var emp = msg[i];
alert("id="+emp.id+",lastName="+emp.lastName);
}*/
var tb = "<table>";
tb += "<tr><th>id</th><th>lastName</th><th>email</th><th>gender</th><th>departmentName</th></tr>";
for (var i in msg) {
var emp = msg[i];
tb += "<tr><td>" + emp.id + "</td><td>" + emp.lastName + "</td><td>" + emp.email + "</td><td>" + emp.gender + "</td><td>" + emp.department.departmentName + "</td></tr>";
}
tb += "</table>";
$("body").append(tb);
}
});
});
});
</script>
</head>
<body>
<input id="btn" type="button" value="测试Ajax">
<a href="testJson">测试json</a>
</body>
9.文件的上传和下载
9.1文件的下载
9.1.1HttpMessageConverter接口
HttpMessageConverter<T>负责将请求信息转换为一个对象(类型为T),将对象(类型为T)输出为响应信息。
DispatcherServlet默认装配RequestMappingHandlerAdapter,
而RequestMappingHandlerAdapter默认装配了 HttpMessageConverter。
9.1.2使用HttpMessageConverter
需要使用两个注解和两个类型实现:
使用 @RequestBody/@ResponseBody 对处理方法进行标注
使用 HttpEntity/ResponseEntity 作为处理方法的入参或返回值
ResponseEntity :实现下载 (Response:响应,Entity:实体)
9.1.3实现下载(代码):
HttpSession :主要是为了获取服务器的真实地址。
我们写代码的地方叫:命名空间。
服务器存储数据的地方,又是一个地址:叫真实地址。
@Controller
public class TestUploadAndDownController {
@RequestMapping(value = "/down")
public ResponseEntity<byte[]> down(HttpSession session) throws IOException {
//获取下载文件的路径(映射到Tomcat里的真实路径)
String realpath = session.getServletContext().getRealPath("img");
//File.separator:文件分隔符,实际上就是 “/”
String fianlPath = realpath + File.separator + "夹心酱2.png";
FileInputStream fileInputStream = new FileInputStream(fianlPath);
//available():获取输入流所读取的文件的最大字节数
byte[] bytes = new byte[fileInputStream.available()];
//把文件里的字节读取到数组中
fileInputStream.read(bytes);
//设置请求头
HttpHeaders httpHeaders = new HttpHeaders();
//attachment:附件的意思,以附件的方式下载。
//filename=夹心.png:设置文件的名字
httpHeaders.add("Content-Disposition","attachment;filename=夹心.png");
//设置响应状态
HttpStatus status = HttpStatus.OK;
//关闭流
fileInputStream.close();
//返回值
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, httpHeaders,status);
return responseEntity;
}
}
9.2文件的上传
9.2.1MultipartFile
SpringMVC 独有的文件类型,它自动的帮我们封装了File类型,使用起来更加的方便。
它通过CommonsMultipartResolver(文件解析器)类 将File 类型封装成了 MultipartFile。
不过需要在SpringMVC里配置一下:
<!--文件解析器,将客户端上传的文件处理成 MultipartFile-->
<!--id 只能是 multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--这个bean里的属性都有默认值,都可以不写-->
<!--设置文件解析的编码,要和页面的pageEncoding保持一致-->
<property name="defaultEncoding" value="UTF-8"></property>
<!--maxUploadSize:最大上传文件的大小,单位是字节。有默认值,可不写-->
<property name="maxUploadSize" value="1000000000"></property>
</bean>
9.2.2初步实现上传(代码):
@RequestMapping(value = "/up_old",method = RequestMethod.POST)
public String up_old(String desc, MultipartFile uploudFile,HttpSession session) throws IOException {
//MultipartFile :SpringMVC自动帮我们封装了File类型。
/* //获得表单元素的name属性
String name = uploudFile.getName();
System.out.println("name:"+name);//uploudFile*/
//获得上传的文件名
String originalFilename = uploudFile.getOriginalFilename();
// System.out.println("originalFilename:"+originalFilename);//夹心酱.png
//设定上传的文件存储在服务器的哪个位置
String Path = session.getServletContext().getRealPath("img")+File.separator+originalFilename;
//获取输入流
InputStream inputStream = uploudFile.getInputStream();
//获取输出流
File file = new File(Path);
FileOutputStream fileOutputStream = new FileOutputStream(file);
int i = 0;
while ((i=inputStream.read())!=-1){
fileOutputStream.write(i);
}
fileOutputStream.close();
inputStream.close();
return "success";
}
9.2.3上传最终代码:
若要实现批量上传:
1.将形参里的传过来的 MultipartFile 改成 MultipartFile[]
2.用 for 循环 将主要代码包起来。
@RequestMapping(value = "/up",method = RequestMethod.POST)
public String up(String desc, MultipartFile uploudFile, HttpSession session) throws IOException {
//获得上传的文件名
String filename = uploudFile.getOriginalFilename();
//UUID.randomUUID():获取一个通用唯一识别码(一个很长的唯一的字符串)
//filename.substring(filename.lastIndexOf(".")); :截取文件的后缀名。(判断同类型文件是否重名)
String finalFileName = UUID.randomUUID() + filename.substring(filename.lastIndexOf("."));
//设定上传的文件存储在服务器的哪个位置
String Path = session.getServletContext().getRealPath("img") + File.separator + finalFileName;
File file = new File(Path);
//transferTo :转换,将 uploudFile 中的文件转换到 file 里。
uploudFile.transferTo(file);
return "success";
}
10.拦截器
10.1自定义拦截器概述
SpringMVC 也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能。
自定义的拦截器可以实现 HandlerInterceptor 接口,也可以继承 HandlerInterceptorAdapter适配器类。
Interceptor:拦截器。 Adapter:适配器。
HandlerInterceptorAdapter 实现了 HandlerInterceptor。但是方法全部都是空方法。
拦截器有三个方法:
preHandler():这个方法在业务处理器处理请求之前被调用,对用户的请求request进行处理。
返回false,进行拦截。返回true,放行。
在生成 ModelAndView 之前执行。
postHandler():这个方法在业务处理器处理完请求后,但是DispatcheServlet向客户端返回响应之前被调用,对用户的请求request进行处理。
在生成 ModelAndView 之后,但在执行ModelAndView之前,执行。
afterCompletion():这个方法在DispatcherServlet完全处理完请求之后被调用,一般用来清理资源。
10.1.1拦截器和过滤器的区别
10.1.2简单的拦截器例子:
1.拦截器页面:
@Component
public class FirstInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("First:preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("First:postHandle");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("First:afterCompletion");
}
}
2.配置拦截器(SpringMVC.xml):
<!--配置拦截器-->
<mvc:interceptors>
<!--1.默认拦截所有请求-->
<!--这两句作用一模一样,只不过ref方法要求拦截器类上必须要加上@Component-->
<bean class="com.guolian.Interceptor.FirstInterceptor"></bean>
<!--<ref bean="firstInterceptor"></ref>-->
</mvc:interceptors>
3.控制层:
@Controller
public class TestInterceptor {
@RequestMapping("/testInterceptor")
public String testInterceptor(){
//ModelAndView
return "success";
}
}
4.页面:
<body>
<a href="testInterceptor">测试Interceptor</a>
</body>
10.2设置自定义拦截方式:
<!--配置拦截器-->
<mvc:interceptors>
<!--设置自定义拦截方式-->
<mvc:interceptor>
<!--设置拦截器类-->
<bean class="com.guolian.Interceptor.FirstInterceptor" </bean>
<!--设置需要拦截的路径-->
<mvc:mapping path=""></mvc:mapping>
<!--设置在拦截路径里需要排除掉的路径-->
<mvc:exclude-mapping path=""></mvc:exclude-mapping>
</mvc:interceptor>
</mvc:interceptors>
10.3多个拦截器的执行顺序
<!--配置拦截器-->
<mvc:interceptors>
<!--1.默认拦截所有请求-->
<bean class="com.guolian.Interceptor.FirstInterceptor"></bean>
<bean class="com.guolian.Interceptor.SecondInterceptor"></bean>
</mvc:interceptors>
</beans>
当有多个拦截器时,preHandle()按照拦截器数组(按顺序存储的拦截器)的正序执行。
**postHandle()按照拦截器数组的反序执行**。
**afterCompletion()按照拦截器数组的反序执行**。
当多个拦截器的preHandle()有不同返回值时:
这里的第一个、第二个指的是排除了默认拦截器的排序。
第一个返回false,第二个返回false。只有第一个拦截器的preHandle()会执行。
第一个返回true,第二个返回false。
第一、二个(全部)拦截器的preHandle()都会执行,
但是(全部)postHandler()都不会执行,
第一个的afterCompletion()会执行(返回false的拦截器之前的拦截器的afterCompletion()都会执行)。第一个返回false,第二个返回true。只有第一个拦截器的preHandle()会执行。
11.异常处理
Spring MVC通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射,数据的绑定,以及目标方法执行时发生的异常。
11.1DefaultHandlerExceptionResolver
springMVC默认的异常处理器。我们测试程序时,在页面报出的异常信息,就是这个类处理后的结果。
例子:
这就是处理之后的结果。
11.2SimpleMappingExceptionResolver
自定义异常处理
配置:
<!--配置自定义异常处理类-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--key:写需要处理的异常-->
<!--标签里的文本写出现异常时,需要跳转到的页面。使用的是视图解析器-->
<prop key="java.lang.NullPointerException">error</prop>
</props>
</property>
</bean>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
12.运行流程图:
1.客户端请求发送到web.xml,查看是否匹配DispatchServlet的url-pattern。
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2.如果匹配,则交给 DispatcherServlet 处理。DispatcherServlet 查看是否存在与请求对应的映射关系。
@Controller
public class TestException {
//映射关系
@RequestMapping(value = "/testException",method = RequestMethod.GET)
public String testException(){
String s = null ;
System.out.println(s.substring(0,5));
return "success";
}
}
3.如果不存在对应的映射关系,则查看是否配置了默认的servlet。
如果默认的Servlet不能处理,则页面404。控制台不会报错。
<!--配置默认的Servlet,当DispatcherServlet 处理不了时,交给默认的Servlet处理。-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
4.如果存在,则由 HandlerMapping 获取 HandlerExecutionChain 对象。然后再获取 HandlerAdapter对象。
HandlerMapping : 存储所有的请求和处理器之间的关系。
HandlerExecutionChain:找到某一个指定的请求和处理器、拦截器之间的关系。
通过**HandlerMapping.getHandler(),获得HandlerExecutionChan对象**。
HandlerAdapter:处理器适配器。用来执行当前的Handler(控制层中的方法)。
5.当 HandlerAdapter 要调用控制层中的方法时,先经过 拦截器的perHandler(),再生成 ModelAndView对象,再调用 拦截器的 postHandler()。再根据 ModelAndVIew 对象得到实际的 view,跳转到相应的页面。最后调用拦截器的afterCompletion()。跳转功能的底层就是:请求转发。
6.如果调用 ModelAndVIew 时出现异常。那么,根据 HandlerExceptionResolver 处理异常,得到新的ModelAndView 对象。
13.Spring整合SpringMVC
SpringMVC所管理的控制层Servlet,是基于 Spring管理的 业务层service 的。
所以,Spring.xml(applicationContext.xml) 的加载应该在 SpringMVC.xml(dispatcher-servlet.xml) 之前。
在servlet生成之前有:过滤器、监听器。
由于过滤器是每请求一次,就生成一次。所以,我们在监听器中加载Spring.xml。
加载顺序:context(容器)—>listener(监听器)—>filter(过滤器)—>servlet
13.1使用监听器模拟整合
为什么是模拟整合?因为,SpringMVC已经为我们自动的整合了Spring。现在只是模拟看一下过程。
右键项目,new-Listener
SpringListener.java:
@WebListener()
public class SpringListener implements ServletContextListener,
HttpSessionListener, HttpSessionAttributeListener {
// Public constructor is required by servlet spec
public SpringListener() {
}
//在servlet容器初始化时,加载 Spring.xml。并将其对象放入 servletContext域中。
public void contextInitialized(ServletContextEvent sce) {
/* This method is called when the servlet context is
initialized(when the Web application is deployed).
You can initialize servlet context related data here.
*/
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("com/guolian/applicationContext.xml");
ServletContext servletContext = sce.getServletContext();
servletContext.setAttribute("ac",ac);
}
}
控制层:
如果没有返回值,也就是参数为 void。则ModelAndView的默认值为:RequestMapping 中value的值。
@Controller
public class TestController {
@RequestMapping(value = "/testListener",method = RequestMethod.GET)
private String testListener(HttpSession session){
//获取Spring所管理的teacher对象
ServletContext servletContext = session.getServletContext();
ApplicationContext ac = (ApplicationContext)servletContext.getAttribute("ac");
Teacher teacher = ac.getBean("teacher", Teacher.class);
System.out.println(teacher);
return "success";
}
}
web.xml:
在web.xml中配置监听器
<listener>
<listener-class>com.guolian.listener.SpringListener</listener-class>
</listener>
13.2正式整合
在web.xml下配置:
在idea中,生成SpringMVC项目就会自动生成。
<!--用来设置上下文参数
如果不加这个配置,则默认寻找WEB-INF下的applicationContext.xml-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--配置spring.xml的加载路径-->
<param-value>classpath:com/guolian/spring.xml</param-value>
</context-param>
<!--配置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--自己配置的监听器,必须要写在默认监听器的后面-->
<listener>
<listener-class>com.guolian.listener.SpringListener</listener-class>
</listener>
SpringLIstener:
public class SpringListener implements ServletContextListener,
HttpSessionListener, HttpSessionAttributeListener {
// Public constructor is required by servlet spec
public SpringListener() {
}
// 容器初始化时
public void contextInitialized(ServletContextEvent sce) {
//获取默认的监听器自动帮我们依赖注入的容器对象。
ApplicationContext context = (ApplicationContext) sce.getServletContext() .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
sce.getServletContext().setAttribute("ac",context);
}
}
监听器,可以在ServletContext加载时,通过监听器加载spring的配置文件,创建spring容器。
spring提供的监听器:ContextLoaderListener。
在springMVC只扫描控制层,在spring中,通过包含排除对所扫描的包进行指定。
spring是父容器。 springMVC是子容器。
子容器能够调用访问父容器中的bean,而父容器不能调用访问子容器中的bean。
springMVC管理:controller。 spring管理:service、dao。