SpringMVC第二天
该文档是:SpringMVC框架学习...
日期:2021-01-08
1. 响应数据和结果视图
先搭建环境,创建maven项目引入依赖,配置web.xml文件等等...
1.1 返回类型为字符串
controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
(1)jsp页面:
response.jsp:
<body>
<a href="user/save">发送请求</a>
</body>
success.jsp:
<body>
<h3>跳转成功</h3>
${requestScope.user.username}
${requestScope.user.password}
</body>
(2)UserController类:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
public String save(Model model){
//模拟数据库查出数据
User user=new User();
user.setUsername("Luo");
user.setPassword("123456");
//把user对象放入request域中
model.addAttribute(user);
//return字符串相当于请求转发
//指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/pages/success.jsp
return "success";
}
}
执行结果:
1.2 返回类型为void
- 如果控制器的方法返回值编写成void,执行程序报404的异常,默认查找JSP页面没有找到。
- 默认会跳转到@RequestMapping(value="/testVoid") testVoid的页面。
- 可以使用请求转发或者重定向跳转到指定的页面
(1)UserController类:
@RequestMapping("/testVoid")
public void testVoid(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;c");
//直接进行相应
response.getWriter().write("hello");
//可以直接retrun不让代码执行下去
return;
}
1.3 返回类型为ModelAndView
ModelAndView是SpringMVC为我们提供的一个对象,该对象也可以用作控制器方法的返回值。(返回为Spring底层其实也是调用的这个)
(1)该对象中有两个方法:
(2)UserController类:
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
//创建ModelAndView对象
ModelAndView mv=new ModelAndView();
//模拟数据库查出数据
User user=new User();
user.setUsername("Luo");
user.setPassword("123456");
//把user对象存入mv对象中,也会把user对象存入到requset对象中。
mv.addObject(user);
//要跳转到哪个页面
mv.setViewName("success");
//返回这个mv对象,他会使用视图解析器然后转发到setViewName设置的success.jsp页面
return mv;
}
1.4 SpringMVC框架提供的转发和重定向(不常用)
(1)forward转发
controller方法在提供了String类型的返回值之后,默认就是请求转发。我们也可以写成:
/**
* 使用forward关键字进行请求转发
* "forward:转发的JSP路径",不走视图解析器了,所以需要编写完整的路径
* @return
* @throws Exception
*/
@RequestMapping("/delete")
public String delete() throws Exception {
System.out.println("delete方法执行了...");
// return "forward:/WEB-INF/pages/success.jsp";
return "forward:/user/findAll";
}
需要注意的是,如果用了formward:
则路径必须写成实际视图url,不能写逻辑视图。 它相当于request.getRequestDispatcher("url").forward(request,response)
。使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。
(2)redirect重定向
contrller方法提供了一个String类型返回值之后,它需要在返回值里使用:redirect:
/**
* 重定向
* @return
* @throws Exception
*/
@RequestMapping("/count")
public String count() throws Exception {
System.out.println("count方法执行了...");
//不需要写工程路径,底层已经封装好了
return "redirect:/index.jsp";
}
它相当于response.sendRedirect(url)
。需要注意的是,如果是重定向到jsp页面,则jsp页面不能写在WEB-INF目录中,否则无法找到。
1.5 @ResponseBody响应json数据
(1)DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而不能被使用。解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置:
引入js:
设置不拦截静态资源,修改spring.xml进行配置:
<!-- 设置静态资源不过滤 -->
<!--顺序一定不要弄错,mapping在前,location在后-->
<!--1. mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b-->
<!--2. location元素表示webapp目录下的包下的所有文件-->
<mvc:resources mapping="/css/**" location="/css/" /> <!-- 样式 -->
<mvc:resources mapping="/images/**" location="/images/"/> <!-- 图片 -->
<mvc:resources mapping="/js/**" location="/js/"/> <!-- javascript -->
(2)使用@RequestBody获取请求体数据:
jsp页面:
<head>
<title>Title</title>
<%--引入jquery--%>
<script type="text/javascript" src="js/jquery-1.7.2.js"></script>
<script type="text/javascript">
$(function () { //入口函数
$("#test").click(function () {
//发送ajax请求
$.ajax({
//编写json格式,设置属性和值
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
//data是发送给服务器的请求体
data:'{"username":"hehe","password":"123","age":"13"}',
dataType:"json",
type:"post",
//data就是服务器响应回来的数据
success:function (data) {
}
})
})
})
</script>
</head>
<body>
<button id="test">发送ajax请求</button>
</body>
Controller类:
@RequestMapping("/testAjax")
//@RequestBody注解可以获得整个请求体的内容
public void testAjax(@RequestBody String body){
System.out.println(body);
}
输出结果:
(3)使用@RequestBody把json数据封装到实体类中再用@ResponseBody注解实现将controller方法返回对象转换为json响应给客户端:
json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包:
<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>
jsp页面:
<head>
<title>Title</title>
<%--引入jquery--%>
<script type="text/javascript" src="js/jquery-1.7.2.js"></script>
<script type="text/javascript">
$(function () { //入口函数
$("#test").click(function () {
//发送ajax请求
$.ajax({
//编写json格式,设置属性和值
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
//data是发送给服务器的请求体
data:'{"username":"hehe","password":"123"}',
//返回的数据类型是json
dataType:"json",
type:"post",
//data就是服务器响应回来的数据
success:function (data) {
alert(data+"用户名"+data.username+"密码"+data.password+"年龄"+data.age)
}
})
})
})
</script>
</head>
<body>
<button id="test">发送ajax请求</button>
</body>
Controller类:
@RequestMapping("/testAjax")
//@RequestBody:因为导入了jackson依赖,此时会自动把json字符串封装到user对象中,需要json的键和实体类属性名对上
//@ResponseBody:因为ajax请求中要求返回的数据格式是json,会自动把user对象变成json格式响应回去
public @ResponseBody User testAjax(@RequestBody User user){
//假设从数据库 根据传过来的username和passoword查到用户年龄
user.setAge(20);
return user;
}
网页输出结果:
2. SpringMVC实现文件上传
2.1 文件上传的回顾
文件上传的必要前提:
文件上传的原理分析:
借助第三方组件实现文件上传(用这些jar包才能解析上边图片中的http协议内容):
编写文件上传的Controller控制器(不使用SpringMVC实现文件上传的情况):
/**
* 文件上传
* @throws Exception
*/
@RequestMapping(value="/fileupload")
public String fileupload(HttpServletRequest request) throws Exception {
// 先获取到要上传的文件目录
String path = request.getSession().getServletContext().getRealPath("/uploads");
// 创建File对象,一会向该路径下上传文件
File file = new File(path);
// 判断路径是否存在,如果不存在,创建该路径
if(!file.exists()) {
file.mkdirs();
}
// 创建磁盘文件项工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 解析request对象
List<FileItem> list = fileUpload.parseRequest(request);
// 遍历
for (FileItem fileItem : list) {
// 判断文件项是普通字段,还是上传的文件
if(fileItem.isFormField()) {
}else {
// 上传文件项
// 获取到上传文件的名称
String filename = fileItem.getName();
// 上传文件
fileItem.write(new File(file, filename));
// 删除临时文件
fileItem.delete();
}
}
return "success";
}
2.2 SpringMVC传统方式的文件上传
(1)分析:
(2)同样需要引入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>
(3)先在springmvc.xml中配置文件解析器:
<!--配置文件解析器,这里的id必须为multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--里边可以配置属性,maxUploadSize代表最大允许上传文件大小-->
<property name="maxUploadSize" value="10485760"></property>
</bean>
(4)jsp页面:
<form action="file/fileupload1" method="post" enctype="multipart/form-data">
文件上传:<input type="file" value="选择文件" name="upload">
<input type="submit" value="提交">
</form>
(5)编写Controller类:
SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件,要求变量名称必须和表单file标签的name属性名称相同。
@Controller
@RequestMapping("/file")
public class FileController {
@RequestMapping("fileupload1")
// 这里的MultipartFile 参数的名称必须和type="file"的input框的name一致
public String fileupload1(HttpServletRequest request, MultipartFile upload) throws IOException {
// 先获取到要上传的文件目录
String path = request.getSession().getServletContext().getRealPath("/uploads");
System.out.println(path);
// 创建File对象,一会向该路径下 上传文件
File file = new File(path);
// 判断路径是否存在,如果不存在,创建该路径(文件夹)
if(!file.exists()) {
file.mkdirs();
}
// 获取到上传的文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称唯一化(防止不同的人上传相同的文件名然后覆盖掉了)
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
filename = uuid+"_"+filename;
// 上传文件(地址为file,文件名为filename)
upload.transferTo(new File(file,filename));
return "success";
}
}
结果:
2.3 SpringMVC跨服务器方式文件上传
(1)好处:
(2)分析:
(3)搭建环节,需要再来一个tomcat充当储存图片的服务器:
用模板创建maven项目,什么都不需要改,只是存放图片用的服务器:
创建用来存放图片的文件夹:
给这个服务器配置一个tomcat:
需要修改tomcat的web.xml配置:
加入此行的含义是:接收文件的目标服务器可以支持写入操作。
(4)导入开发需要的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>
<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>
(5)jsp页面:
<form action="file/fileupload2" method="post" enctype="multipart/form-data">
文件上传:<input type="file" value="选择文件" name="upload">
<input type="submit" value="提交">
</form>
(6)编写Controller类:
@RequestMapping("/fileupload2")
// 这里的MultipartFile 参数的名称必须和type="file"的input框的name一致
public String fileupload2(MultipartFile upload) throws IOException {
// 定义图片服务器的请求路径(你要传的服务器的地址和具体位置),这里注意不能漏了http://
String path="http://localhost:9090/fileuploadserver/uploads/";
// 获取到上传的文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称唯一化(防止不同的人上传相同的文件名然后覆盖掉了)
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
filename = uuid+"_"+filename;
// 创建客户端对象
Client client = Client.create();
// 连接图片服务器(要放哪+文件名)
WebResource resource = client.resource(path + "/" + filename);
// 上传文件(需要文件的byte字节)
resource.put(upload.getBytes());
return "success";
}
测试结果:
3. SpringMVC中的异常处理
3.1 为什么要异常处理和过程
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
3.2 实现步骤
(1)jsp页面:
<body>
<a href="wrong/testException">异常处理</a>
</body>
(2)自定义异常类:
public class SysException extends Exception{
//存储提示信息的
private String message;
(3)Controller类模拟异常:
@Controller
@RequestMapping("/wrong")
public class WrongController {
@RequestMapping("testException")
public String testException() throws SysException {
System.out.println("testException执行了...");
try {
//模拟异常
int a = 10/0;
} catch (Exception e) {
//打印异常信息
e.printStackTrace();
//抛出自定义异常信息
throw new SysException("查询所有用户出现错误了...");
}
return "success";
}
}
(4)编写异常处理器类,并且在配置文件中配置它:
SysExceptionResolver异常处理器类:
/**
* 异常处理器
*/
public class SysExceptionResolver implements HandlerExceptionResolver {
/**
* 当Controller真的把异常抛过来,而且配置了这个处理器的话,它就会调用这个处理器并且会执行这里边的方法
* @param request
* @param response
* @param handler
* @param ex
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//Exception ex这个参数代表的就是Controller抛过来的异常
//获取到异常对象
SysException e=null;
//如果ex底层指向的是SysException的话
if (ex instanceof SysException){
//强转成SysException
e = (SysException) ex;
} else {
//如果不是就new一个
e=new SysException("系统正在维护...");
}
ModelAndView mv=new ModelAndView();
//把错误信息存入request域中
mv.addObject("erroMsg",e.getMessage());
//设置要往哪个友好的错误提示页面跳转
mv.setViewName("error");
//返回mv,等于说一有异常那么就会带着错误信息往 error.jsp这个页面跳转
return mv;
}
}
在springmvc.xml中进行配置:
<!--配置异常处理器-->
<bean id="sysExceptionResolver" class="com.Luo.exception.SysExceptionResolver"></bean>
(5)编写error.jsp错误页面:
<body>
<h3>${requestScope.erroMsg}</h3>
</body>
执行结果:
4. SpringMVC中的拦截器
4.1 拦截器的概述
4.2 自定义拦截器步骤
jsp页面:
<a href="lanjie/testInterceptor">测试拦截器</a>
Controller类:
public class MyInterceptor1 implements HandlerInterceptor {
/**
* 预处理,controller方法执行前,进行拦截的方法
* @return true放行
* @return false拦截
* 可以使用转发或者重定向直接跳转到指定的页面。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器预处理方法执行了...");
return true;
}
}
在springmvc.xml中配置拦截器:
<!--配置多个拦截器-->
<mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptor>
<!--要拦截的具体方法,表示拦截请求为/lanjie下的所有方法-->
<mvc:mapping path="/lanjie/*"/>
<!--这个则表示不拦截的方法,请求为/lanqiu下的方法都不拦截
<mvc:exclude-mapping path="/lanjie/*"/> -->
<!--配置自己写的拦截器对象-->
<bean class="com.Luo.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
</mvc:interceptors>
4.3 HandlerInterceptor接口中的方法
4.4 配置多个拦截器
执行顺序:
在springmvc.xml中配置拦截器:
<!--配置多个拦截器-->
<mvc:interceptors>
<!--配置第一个拦截器-->
<mvc:interceptor>
<!--要拦截的具体方法,表示拦截请求为/lanjie下的所有方法-->
<mvc:mapping path="/lanjie/*"/>
<!--这个则表示不拦截的方法,请求为/lanqiu下的方法都不拦截
<mvc:exclude-mapping path="/lanjie/*"/> -->
<!--配置自己写的拦截器对象-->
<bean class="com.Luo.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
<!--配置第二个拦截器-->
<mvc:interceptor>
<mvc:mapping path="/lanjie/*"/>
<bean class="com.Luo.interceptor.MyInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
4.5 拦截器的简单案例(验证用户是否登录)
(1)实现思路:
(2)控制器代码:
//登陆页面
@RequestMapping("/login")
public String login(Model model)throws Exception{
return "login";
}
//登陆提交
// userid:用户账号,pwd:密码
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session, String userid, String pwd)throws Exception{
//向session记录用户身份信息
session.setAttribute("activeUser", userid);
return "redirect:/main.jsp";
}
//退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
//session过期
session.invalidate();
return "redirect:index.jsp";
}
(3)拦截器代码: