通过处理器方法返回值指定返回视图
SpringMVC中的处理器方法的返回值用来指定页面跳转到哪个视图,处理器的返回值可以为String,void,ModelAndView对象.
处理器返回String对象: 转发到字符串指定的URL
处理器方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址.
@Controller
@RequestMapping("/user")
public class UserController {
//返回值类型是String
@RequestMapping("/testString")
public String testString(Model model){
System.out.println("testString执行了");
//模拟从数据库查询出的User对象
User user = new User();
user.setUsername("meimei");
user.setAge(23);
user.setPassword("123");
//model对象存储数据
model.addAttribute("user",user);
return "success";
}
处理器返回void: 转发到当前URL
若处理器返回void,表示执行完处理器方法体内代码后,不进行请求转发,而直接转发到当前URL.若没有在web.xml中配置当前对应的url-pattern,则会返回404错误.
//返回值类型是void
@RequestMapping("/testVoid")
public void testVoid(Model model){
System.out.println("testVoid执行了");
}
处理器返回ModelAndView对象: 更灵活地添加属性和指定返回视图
ModelAndView为我们提供了一种更灵活地为页面添加属性和指定返回视图的方法,其主要方法如下:
public ModelMap getModelMap()
: 返回当前页面的ModelMap对象.
public ModelAndView addObject(Object attributeValue)
: 向当前页面的ModelMap对象中添加属性
public void setViewName(@Nullable String viewName)
: 指定返回视图,viewName会先被视图解析器处理解析成对应视图.
//返回值类型是ModelAndView
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(Model model){
ModelAndView mv = new ModelAndView();
System.out.println("testModelAndView");
//模拟从数据库查询出的User对象
User user = new User();
user.setUsername("meimei");
user.setAge(23);
user.setPassword("123");
//把user对象存储到mv对象中,也会把user对象存入到request对象
mv.addObject("user",user);
//跳转到哪个页面
mv.setViewName("success");
return mv;
SpringMVC响应json数据
准备
1.要将json字符串与JavaBean对象相互转换,我们需要引用jackson的jar包,在pom.xml中添加依赖坐标如下:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency
2.jsp在页面上引入jQuery以发送json数据,因此需要向服务器发起一个对jQuery的请求.像这种对静态资源的请求,不应当经过具体的某个处理器处理,而应当直接返回对应的静态资源.
因此我们需要在Spring容器配置bean.xml中使用<mvc:resources>
标签声明该资源为静态资源,否则请求该资源会报404错误.该标签的属性如下:
location
属性: 表示该资源在服务器上所在的位置,必须是一个有效的目录
mapping
属性: 指定匹配的URL
我们在springmvc.xml中配置各静态文件的位置如下:
<!--前端控制器,哪些静态资源不拦截-->
<!-- location属性必须是一个有效的目录,因此必须以 / 结尾 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
发送json数据
在jsp页面中编写代码发送json请求如下:
// 页面加载,绑定单击事件
$(function () {
$("#btn").click(function () {
//alert("hello")
//发送Ajax请求
$.ajax({
//json格式.设置属性和值
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
data:'{"username":"hehe","password":"123","age":30}',
dataType:"json",
type:"post",
success:function (data) {
//data指服务器端响应的json数据,进行解析
alert(data);
alert(data.username);
alert(data.age);
alert(data.password) }
})
})
})
在控制器中编写代码响应json数据
使用@RequestBody注解将请求体绑定到控制器方法参数上,使用@ResponseBody注解表示将该方法的返回值直接写回到HTTP响应中,而不是存入Model或解析为视图名.
jackson包自动完成从Java实体类到json数据之间的相互转换.
//模拟异步请求响应
@RequestMapping("/testAjax")
public @ResponseBody User testAjax(@RequestBody User user){
System.out.println("testAjax");
System.out.println(user);
//做响应
user.setUsername("aas");
user.setAge(54);
return user;
}
SpringMVC文件上传
实现文件上传的前提
1.<form>
表单的enctype属性取值必须是multipart/form-data(默认值是application/x-www-form-urlencoded),表示表单内容是分块的.这时request对象的getParameter()方法将失效.
2.<form>
表单的method属性取值必须是post,因为get请求长度有限制.
3.提供一个<input/>
标签,用来选择上传文件.
index.jsp
<h3>传统文件上传</h3>
<form action="/user/fileupload1" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload" /><br/>
<input type="submit" value="上传" />
</form>
导入jar包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
文件上传的三种实现
使用JavaEE进行文件上传(非重点)
传统的JavaEE文件上传思路是通过解析request对象,获取表单中的上传文件项并执行保存.
/**
* 文件上传
*
* @return
*/
@RequestMapping("/fileupload1")
public String fileuoload1(HttpServletRequest request) throws Exception {
System.out.println("文件上传...");
// 使用fileupload组件完成文件上传
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 创建该文件夹
file.mkdirs();
}
// 解析request对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析request
List<FileItem> items = upload.parseRequest(request);
// 遍历
for (FileItem item : items) {
// 进行判断,当前item对象是否是上传文件项
if (item.isFormField()) {
// 说明普通表单向
} else {
// 说明上传文件项
// 获取上传文件的名称
String filename = item.getName();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上传
item.write(new File(path, filename));
// 删除临时文件
item.delete();
}
}
return "success";
}
使用SpringMVC进行单服务器文件上传
可以使用SpringMVC提供的文件解析器实现文件上传,在Spring容器中注入文件解析器CommonsMultipartResolver对象如下:
spring.xml
<!--配置文件解析器对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760" />
</bean>
只要在处理器方法的参数列表中定义一个与表单文件项同名的MultipartFile参数,就可以将上传的文件绑定到该MultipartFile对象上,调用其transferTo(File file)方法即可保存文件.
/**
* SpringMVC文件上传
*/
@RequestMapping("/fileupload2")
public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
System.out.println("spring文件上传...");
// 使用fileupload组件完成文件上传
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 创建该文件夹
file.mkdirs();
}
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上传
upload.transferTo(new File(path, filename));
return "success";
}
使用SpringMVC进行跨服务器文件上传
我们可以引入jersey库进行服务器间通信,实现将文件上传到一个专用的文件服务器,需要在pom.xml中引入jersey库的坐标如下:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
在处理器方法中创建Client对象实现服务器间通信,将文件上传到文件服务器上,代码如下:
/**
* 跨服务器文件上传
*
* @return
*/
@RequestMapping("/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
System.out.println("服务器文件上传...");
// 定义上传文件服务器路径
String path = "http://localhost:9090/fileupload_war_exploded/uploads/";
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 创建客户端的对象
Client client = Client.create();
// 和图片服务器进行连接
WebResource webResource = client.resource(path + filename);
// 上传文件
webResource.put(upload.getBytes());
return "success";
}
异常处理器和拦截器
异常处理器
当程序发生错误时,错误最终会传递给DispatcherServlet,由DispatcherServlet进行异常处理.
下面演示使用SpringMVC的异常处理机制处理异常
1.创建自定义异常类
package cn.maoritian.exception;
public class SysException extends Exception {
// 存储提示信息的
private String message;
// 构造方法
public SysException(String message) {this.message = message; }
// get,set方法
public String getMessage() {return message; }
public void setMessage(String message) {this.message = message; }
}
2.创建异常处理器,异常处理器必须实现HandlerExceptionResolver接口,其resolveException()方法执行异常处理.
package cn.maoritian.exception;
// 自定义异常处理器
public class MyExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
myException e = null;
if (ex instanceof myException) {
e = (myException) ex;
} else {
e = new myException("其他错误");
}
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg", e.getMessage()); // 封装错误信息
mv.setViewName("error"); // 跳转页面
return mv;
}
}
向Spring容器中注入异常处理器
<!--配置异常处理器-->
<bean id="sysExceptionResolver" class="cn.itcast.exception.SysExceptionResolver"/>
拦截器
拦截器的配置
SpringMVC中的拦截器只能拦截Controller中的方法,下面演示拦截器的使用
自定义拦截器需要继承HandlerInterceptor接口,该接口中定义了三个方法,都已有其默认实现:
preHandle(…): 该方法在处理器方法实际执行之前执行
postHandle(…): 该方法在处理器方法实际执行完毕以后执行
afterCompletion(…): 该方法在整个请求处理完成后执行
其中preHandle(…)方法返回一个boolean值,可以通过这个方法来决定是否继续执行处理链中的部件。当方法返回 true时,处理器链会继续执行;若方法返回 false, DispatcherServlet即认为拦截器自身已经完成了对请求的处理(比如说,已经渲染了一个合适的视图),那么其余的拦截器以及执行链中的其他处理器就不会再被执行了。
/**
* 自定义拦截器
*/
public class MyInterceptor1 implements HandlerInterceptor{
/**
* 预处理,controller方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行controller中的方法
* return false不放行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor1执行了...前1111");
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
return true;
}
/**
* 后处理方法,controller方法执行后,success.jsp执行之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1执行了...后1111");
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* success.jsp页面执行后,该方法会执行
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1执行了...最后1111");
}
}
向Spring容器中注入拦截器
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--要拦截的方法-->
<mvc:mapping path="/user/*"/>
<!--不要拦截的方法-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置拦截器的对象-->
<bean class="cn.itcast.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
</mvc:interceptors>