一、有关SpringMVC的响应问题
1.响应之返回值是字符串
这种响应就像我们前面一篇博客说的没有什么特殊之处。在这里仅把代码放在这里
@RequestMapping("/stringResponse")
public String stringResponse(Model model){
System.out.println("testResponse执行了");
User user = new User();
user.setUsername("美美");
user.setAge(23);
user.setMoney(100.0);
model.addAttribute("user",user);
return "success";
}
2.没有返回值
对于没有返回值的响应,其大致也有三种形式,一种是转发,一种是重定向,还有一种就是直接输出,和我们原生servlet中是一模一样的。
@RequestMapping("/voidResponse")
public void voidResponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//请求转发
/*request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
//重定向
response.sendRedirect(request.getContextPath()+"index.jsp");*/
//直接进行响应
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().println("hello");
return;
}
3.返回值是ModelAndView类型的
@RequestMapping("/modelAndView")
public ModelAndView testModelAndView(){
ModelAndView modelAndView = new ModelAndView();
User user = new User();
user.setUsername("张三");
user.setAge(24);
user.setMoney(99.0);
System.out.println("testModelAndView方法执行了");
modelAndView.addObject("user",user);
//跳转到哪个页面
modelAndView.setViewName("success");
return modelAndView;
}
主要就是涉及两个方法,一个是addObject方法将对象添加到域中,还有一种是setViewName用于设置跳转的页面。
4.响应json数据
这个时候请注意,我们前端需要使用Jquery了,所以需要把jquery.min.js给配置进去
<script src="js/jquery.min.js"></script>
你配置之后发现根本就用不了,这是因为在配置前端控制器的时候,设置了任何资源都要拦截,然后就把静态资源给拦截了,所以在这里访问不到你添加进去的.js文件,所以需要在springmvc.xml中设置对静态资源不进行拦截:
<!-- 前端控制器,哪些静态资源不拦截 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/><!-- 前端控制器,哪些静态资源不拦截 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
这一切配置完之后我们就可以写我们的代码了,前端基本上还是和我们JAVA web时是一致的:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Response</title>
<script src="js/jquery.min.js"></script>
<script>
//页面加载,绑定单击事件
$(function(){
$("#btn").click(function(){
// alert("hello btn");
// 发送ajax请求
$.ajax({
// 编写json格式,设置属性和值
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
data:'{"username":"hehe","age":"123","money":30.0}',
dataType:"json",
type:"post",
success:function(data){
// data服务器端响应的json的数据,进行解析
alert(data);
alert(data.username);
}
});
});
});
</script>
</head>
<body>
<button id="btn">发送ajax请求</button>
</body>
</html>
在后端我们使用到了RequestBody标签,他相当于一个json数据的解析器,只要在变量前加上@RequestBody,就能够将其从json解析为单一的对象或者将对象封装成json数据类型。
@RequestMapping("/testAjax")
public @ResponseBody User testAjax(@RequestBody User user){
System.out.println("testAjax执行了");
System.out.println(user);
user.setUsername("张三");
user.setAge(33);
user.setMoney(100.0);
return user;
}
二、文件上传
1.传统的文件上传步骤
1)导入文件上传所需要的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>
2)编写文件上传所需要的jsp页面
<form action="/user/fileupload2" method="post" enctype="multipart/form-data">
选择需要上传的文件:
<input type="file" name="upload"/><br/>
<input type="submit" value="上传"/>
</form>
3)文件上传所需要的后端代码
@RequestMapping("/fileupload1")
public String fileUpload1(HttpServletRequest request) throws Exception {
System.out.println("文件上传");
//使用fileupload组件完成文件上传
//上传位置
String path = request.getSession().getServletContext().getRealPath("/upload/");
//判断,该路径是否存在
File file = new File(path);
if(!file.exists()){
//创建该文件夹
file.mkdir();
}
//解析request对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
//解析request
List<FileItem> items = servletFileUpload.parseRequest(request);
//遍历
for (FileItem item : items) {
//进行判断,当前item对象是否是上传文件项
if(item.isFormField()){
//说明普通表单项
}else{
String filename = item.getName();
//把文件的名字设置为唯一
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
//完成文件上传
item.write(new File(path,filename));
//删除临时文件
item.delete();
}
}
return "success";
}
可以看到整体过程是十分繁琐的,特别是在request的解析以及文件的上传部分,而springMVC可以大大简化代码。在springMVC中request只为获得文件上传的路径,而文件的上传由MuitipartFile这个对象来完成。
2.SpringMVC文件上传步骤
注意这个MultipartFile对象的变量名必须与表单中file的变量名是一致的
@RequestMapping("/fileupload2")
public String fileupload2(HttpServletRequest request, MultipartFile upload) throws IOException {
System.out.println("文件上传");
//使用fileupload组件完成文件上传
//上传位置
String path = request.getSession().getServletContext().getRealPath("/upload/");
//判断,该路径是否存在
File file = new File(path);
if(!file.exists()){
//创建该文件夹
file.mkdir();
}
//解析request对象,获取上传文件项
String filename = upload.getOriginalFilename();
//把文件的名字设置为唯一
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
//完成文件上传
upload.transferTo(new File(path,filename));
return "success";
}
在这些都写完之后,还需要配置在springmvc.xml中配置一个文件解析对象:
<!-- 配置文件解析对象 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760" />
</bean>
3.跨服务器的文件上传
以后会有各个功能不同的服务器,我们想着最后图片或者文件都保存在一个服务器上,以后我们访问也只需要和那个服务器进行连接,所以我们需要一个跨服务器文件上传的代码
1)导入项目所需要的jar包
<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>
2)配置好图片服务器的环境,在这里就不多说了,项目名我们定位upload,配完之后需要在target/upload/下新建一个文件夹叫fileuploads,用于接收传过来的文件
3)编写后端代码
@RequestMapping("/fileupload3")
public String fileupload3(MultipartFile upload) throws IOException {
System.out.println("文件上传");
String path = "http://localhost:9090/fileuploads/";
//解析request对象,获取上传文件项
String filename = upload.getOriginalFilename();
//把文件的名字设置为唯一
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
//创建客户端对象
Client client = Client.create();
//和图片服务器进行连接
WebResource resource = client.resource(path + filename);
//上传文件
resource.put(upload.getBytes());
return "success";
}
需要注意的是,这里的path是指项目下的fileuploads的文件夹。
三、异常处理
1.我们需要自定义一个异常类
public class SysException extends Exception {
//存储错误信息
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
//构造方法,以后只要你创建新的对象,就一定要给我传入提示信息
public SysException(String message) {
this.message = message;
}
}
2.我们先在controller类中写一个异常,然后将其抛出,请注意这里我们抛出的就是我们定义的那种异常,你也可以抛Exception等其他异常
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testException")
public String testException() throws SysException {
//对错误信息进行捕获
try {
int a = 1/0;
} catch (Exception e) {
e.printStackTrace();
//抛出自定义异常信息
throw new SysException("查询所有用户出现错误了。。。");
}
return "success";
}
}
3.自定义异常处理类,请注意这里的e就是我们cotroller中抛出的异常,在类中用了instanceof进行判断,判断完了之后还是需要向下转型,转成我们自己定义的SysException,然后用我们前一天学习过的第三种响应方式,即返回ModelAndView对象
public class SysExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
//获取到异常对象
SysException sysException = null;
if(e instanceof SysException){
sysException = (SysException)e;
}else{
sysException = new SysException("系统正在维护...");
}
//创建ModelView对象
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg",sysException.getMessage());
mv.setViewName("error");
return mv;
}
}
4.配置异常处理器
<!-- 配置异常处理器 -->
<bean id="sysExceptionResolver" class="com.itheima.exception.SysExceptionResolver"/>
四、拦截器
1.拦截器的概述
1. SpringMVC 框架中的拦截器用于对处理器进行预处理和后处理的技术。2. 可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。3. 拦截器和过滤器的功能比较类似,有区别3.1. 过滤器是 Servlet 规范的一部分,任何框架都可以使用过滤器技术。3.2. 拦截器是 SpringMVC 框架独有的。3.3. 过滤器配置了 /* ,可以拦截任何资源。3.4. 拦截器只会对控制器中的方法进行拦截。4. 拦截器也是 AOP 思想的一种实现方式5. 想要自定义拦截器,需要实现 HandlerInterceptor 接口。
2.自定义拦截器的步骤
1)创建类,实现HandlerInterceptor接口,重写需要的方法,通过前面的类型转换器、异常处理器,我们总结出一条组件配置的基本步骤,首先就是要创建一个组件类,然后实现相应的接口,重写里面的方法,最后再去springmvc.xml中进行相应的配置。为了展现执行的顺序,我们创建了两个拦截器。
package com.itheima.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1执行了...后");
}
/*** controller方法执行前,进行拦截的方法 * return true放行 * return false拦截 * 可以使用转发或者重定向直接跳转到指定的页面。 */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor1执行了...前");
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1执行了...最终");
}
}
package com.itheima.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor2执行了...前");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor2执行了...后");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor2执行了...最终");
}
}
2)在springmvc.xml中配置拦截器类
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 要拦截的方法 -->
<mvc:mapping path="/user/*"/>
<!-- 不要拦截的方法-->
<!-- <mvc:exclude-mapping path=""/>-->
<bean class="com.itheima.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- 要拦截的方法 -->
<mvc:mapping path="/user/*"/>
<bean class="com.itheima.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
最后我们输出的顺序为:
注意事项:
1. preHandle 方法是 controller 方法执行前拦截的方法1.1 可以使用 request 或者 response 跳转到指定的页面1.2 return true 放行,执行下一个拦截器,如果没有拦截器,执行 controller 中的方法。1.3 return false 不放行,不会执行 controller 中的方法。2. postHandle 是 controller 方法执行后执行的方法,在 JSP 视图执行前。2.1 可以使用 request 或者 response 跳转到指定的页面2.2 如果指定了跳转的页面,那么 controller 方法跳转的页面将不会显示。3. postHandle 方法是在 JSP 执行后执行3.1 request 或者 response 不能再跳转页面了