SpringMVC的功能
-
参数绑定:
-
通常我们在从页面拿form表单参数的时候是通过这种方式去拿的:
@RequestMapping(value = "login",method = RequestMethod.POST) public String login(String userName,String password){ // 对参数进行处理 return "tohello"; }
这种方式在当我们的参数有几十个的时候,那么将会在括号里面写一长串的参数列表了,噢,这也太不美观了,要是有一个参数写错了,那么还得挨着找,所以,SpringMVC给我们提供了很好的便利,将所有的值都封装到同一个对象里面了
-
首先我们写一个form表单:
<form action="/login" method="post"> <input type="text" placeholder="用户名" name="userName"> <input type="password" placeholder="密码" name="password"> <button type="submit"/>提交 </form>
对应的controller:
@RequestMapping(value = "login",method = RequestMethod.POST) public String login(LoginForm login){ System.out.println(userName); System.out.println(login.getPassword()); System.out.println(login.getAge()); return "tohello"; }
写一个用来封装表单元素的类:
/** * springMVC会将表单元素的值封装到这个类的对象里面, * 所以字段名要与表单元素的name一致,否则拿不到值. * 类名可以随便写,只要在对应的controller方法中引用这个类就行了 * 当请求带着参数进来时,springMVC会将对应的参数绑定到这个类的对象上面 */ @Data public class LoginForm { private String userName; private String password; private String age; }
然后我们运行,在表单中输入账号 li 密码:99999 ,提交,在控制台我们可以看到:
li 99999 null
只要表单中的name的值与表单数据处理类的字段名一样,那么springMVC就会将数据进行数据绑定,在取的时候使用对象取就行了.
-
并且springmvc还会将封装到对象里面的数据进行透传,什么意思呢,就是我们可以直接在页面上进行取值,就不需要再使用request.setAttribute()这种方法来传回数据了
在页面上我们直接取值:这里的规则是将类型名的首字母小写转换为key,我们开始定义的类叫LoginForm,所以我们取值如下: ${loginForm.userName} ${loginForm.password}
-
-
数据校验:
-
需要导入依赖:
<!-- 参数校验,hibernate-validator--> <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.0.Final</version> </dependency>
-
加注解,来限定参数的类型:需要其他限制请去查看API
@Data public class LoginForm { @NotNull(message = "账号不能为空") //不能为空,message的值可以自定义,提示语句 @NotBlank //非空并且不能为空白字符串 @Length(min = 2,max = 5) //限定参数的长度,最小为2,最大为5位 @Email //判断是否为email格式 @Pattern(regexp ="可以在这里面写正则表达式") private String userName; private String password; private String age; }
开始校验:在要校验的参数对象前加上@Valid注解,校验结果会放在BindResult里面
@RequestMapping(value = "login",method = RequestMethod.POST) public String login(@Valid LoginForm login, BindingResult bindingResult, ModelMap modelMap){ System.out.println(bindingResult); StringBuilder stringBuilder=new StringBuilder(); // 转换校验结果 if (bindingResult.hasErrors()){ // 取出所有的错误的集合 List<FieldError> fieldErrors = bindingResult.getFieldErrors(); for (FieldError error:fieldErrors) { // 得到有错误的参数名 stringBuilder.append(error.getField()+","); // 得到此错误的提示 stringBuilder.append(error.getDefaultMessage()+","); } } // ModelMap对象主要用于传递控制方法处理数据到结果页面, // 也就是说我们把结果页面上需要的数据放到ModelMap对象中即可,跟使用request传值效果一样 modelMap.put("errorMessage",stringBuilder.toString()); return "tohello"; } }
在页面上,我们使用jstl直接取值:
${errorMessage}, 最后就会将错误信息打印出来: password,长度需要在1和3之间,userName,不是一个合法的电子邮件地址,userName,长度需要在2和5之间,
-
-
类型转换
-
SpringMVC默认是不支持日期的类型自动转换的
-
使用注解,进行日期转换:@InitBinder
//日期转换,SpringMVC的自动转换不支持日期类 @InitBinder public void init(WebDataBinder binder){ binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); }
注意,这个的作用范围只在当前controller有效,所以,我们可以将其抽象成一个基类,往后所有的controller都来继承这个基类,就自然有他的效果了,我们的参数校验这个方法也需要在很多地方用到,所有都抽象出来:
/** * 将所有controller类有的功能抽象出来 * 基类,封装了日期转换和校验的方法 */ public abstract class ControllerBase { //日期转换,SpringMVC的自动转换不支持日期类 @InitBinder public void init(WebDataBinder binder){ binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); } public String extractError(BindingResult result){ //转换校验结果 StringBuilder errorString=new StringBuilder(); if(result.hasErrors()){ //将错误信息放在集合里面 List<FieldError> fieldErrors = result.getFieldErrors(); for(FieldError fieldError:fieldErrors){ errorString.append(fieldError.getField()+";"); errorString.append(fieldError.getDefaultMessage()+";"); } return errorString.toString(); } return null; } }
-
-
JSON转换:
-
导入依赖Jackson,spring默认的json转换工具
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <!-- 对jackson的依赖进行一个版本管理--> <jackson.version>2.10.0</jackson.version> </properties> <!-- jackson,转换json的支持--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency>
-
创建一个实体类:
@Data public class User { private String userName; private String password; private String sex; private int age; public User(String userName, String password, String sex, int age) { this.userName = userName; this.password = password; this.sex = sex; this.age = age; } }
-
使用:在需要转换json的controller上加@ResponseBody注解
/** * json转换 */ @Controller //@RestController //如果是加RestController注解,那么这个类下面所有的方法都会被转换为json格式,并且方法上就不用再加@ResponseBody注解了 public class JSONController extends ControllerBase { @RequestMapping("getData") @ResponseBody public User getData(){ return new User("li","99999","男",22); } }
-
结果:当我们访问getData时,就会得到一个json格式的对象了
{"userName":"li","password":"99999","sex":"男","age":22}
-
-
静态资源的处理:
-
在使用springMVC的时候,你可能会遇到访问页面的时候,发现所有的静态资源都被拦截了,那是因为在默认情况下,所有的静态资源(css,js,html,图片,音频)都会被拦截,所以在配置的时候需要我们手动的把这些静态资源放出来.
-
在子容器配置中添加:
<!-- mapping:访问路径 ,location:本地路径--> <!-- 这里的style可以自己定义,一般还是跟目录结构匹配比较方便,因为这样写的话有提示,可以避免错误--> <mvc:resources mapping="/style/**" location="/css/"/> <mvc:resources mapping="/js/**" location="/images/"/> <mvc:resources mapping="/images/**" location="/js/"/> <mvc:resources mapping="/fonts/**" location="/fonts/"/>
-
使用:在要使用静态资源的地方根据配置直接使用就好了:
<html> <head> <-- 比如我要在这里导入一个css样式,因为我上面配的是/style/**,所以我这里的路径写/style/login.css,springMVC就会帮我们匹配到WEB-INF/css路径下去寻找叫login.css文件 --> <link rel="stylesheet" href="/style/login.css"> </head>
-
-
全局异常的处理:
-
异常处理通常有两种方式:try-catch,或者是throws,当我们可以处理的异常可以使用try-catch进行处理,但是,当遇到我们不能处理的异常怎么办?比如说sql异常,有这样一个场景,用户在进行登录的时候,传的参数有问题,dao层在查询数据库的时候出现了sql异常,如果这个时候你将这个异常try-catch处理掉,当做什么都没发生,那么用户会发现自己在点了登录之后页面没有动!又或者说出现数据库抖动,这些异常我们都处理不了.
-
对于这些不好处理的异常最好的办法就是向上抛,如果出现这些异常我们就给用户返回一个错误页面,比如说服务器正在维护呀,您访问的页面走丢了呀这样的页面.
-
全局异常处理就相当于一个接盘的,我们肯定不能将错误一直往上抛,最后丢给tomcat处理,结果就是最后用户会看见一个tomcat的404页面.所以我们当然不能这么干,全局异常处理器就是你们都不处理,最后我来处理的意思.
-
使用:@ControllerAdvice注解拦截所有的controller,@ExceptionHandler注解拦截所有的异常:
/** * 全局异常处理器 * 将异常都收集到一起 */ @ControllerAdvice //全局的通知,会拦截所有的controller给他加入一些功能 public class GlobalExceptionHandler { //异常处理器 @ExceptionHandler public String handleException(Exception e, Model model){ //打印堆栈信息 e.printStackTrace(); System.out.println("出错了"+e.getMessage()); model.addAttribute("msg","您要访问的页面走丢啦~~~"); //这里我们就可以使用一个漂亮的404页面返回给用户 return "404"; } }
-
-
文件上传:
-
依赖:commons-fileupload
<!-- 文件上传的支持--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
-
配置:因为文件上传是web相关的,所以我们将其配置在子容器中:
<!-- 上传文件配置--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--最大上传大小--> <property name="maxUploadSize" value="10485760"/> <!-- 最大上传文件单个大小--> <property name="maxInMemorySize" value="1048576"/> </bean>
-
接收文件上传的controller,使用MultipartFile类来接收上传的文件
@Controller public class UploadController { @RequestMapping("upload") public String uploadFile(MultipartFile myfile){ // 得到上传的文件的名字,注意,此时的参数一定要与上传文件的name相对应,这样springMVC才会进行参数绑定,才可以拿到文件对象 String filename = myfile.getOriginalFilename(); System.out.println(filename); try { // 将上传的文件保存在本地 myfile.transferTo(new File("d://Temp//"+filename)); } catch (IOException e) { e.printStackTrace(); } return "tohello"; } }
-
页面:注意要给form表单里面添加属性enctype=“multipart/form-data”;
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <%--注意:method一定要为post,否则会报错 然后要写enctype="multipart/form-data",不然文件传不进去--%> <form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="myfile"> <input type="submit" value="上传"> </form> </body> </html>
-
结果:
我们上传了一个不超过配置中指定大小的文件,最后会在d:/Temp目录下看见,文件上传成功!
-
-
文件下载:
- 直接上代码就可以了,写一个文件下载的controller,注意,这里是从本地下载:
文件下载 @RequestMapping("download") public void downLoad(String filename, HttpServletResponse response){ BufferedInputStream bis=null; BufferedOutputStream bos=null; try { // 从d:/Temp下下载文件 bis=new BufferedInputStream(new FileInputStream("d://Temp//"+filename)); // 设置响应头,告诉浏览器是文件下载 response.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(filename,"UTF-8")); // 读取要下载的文件,保存到文件的输入流 bos=new BufferedOutputStream(response.getOutputStream()); // 创建缓冲区 byte[] bytes=new byte[1024]; int len=0; // 循环将输入流中的内容读取到缓冲区中 while ((len=bis.read(bytes))!=-1){ // 输出缓冲区的内容到浏览器,实现文件下载 bos.write(bytes,0,len); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { // 关流 try { bis.close(); bos.close(); } catch (IOException e) { e.printStackTrace(); } } }
-
拦截器:
-
拦截器与过滤器的区别:
- 拦截器
Interceptor
,过滤器是Filter
- 拦截器是springMVC的组件,过滤器是javaEE中的组件
- 拦截器是配置在Spring的配置文件中的,过滤器是配置在web.xml中的
- 拦截器是运行在
DispatcherServlet
之后、Controller
之前的,且在Controller
执行完后还会调用2个方法,而过滤器是运行在所有的Servlet
之前的; - 拦截器的配置非常灵活,可以配置多项黑名单,也可以配置多项白名单,过滤器的配置非常单一,只能配置1项过滤路径;
- 拦截器与过滤器也有很多相似之处,例如:都可拒绝掉某些访问,也可以选择放行;都可以形成链
- 相比之下,在一个使用SpringMVC框架的项目中,拦截器会比过滤器要好用一些,但是,由于执行时间节点的原因,它并不能完全取代过滤器!
- 拦截器
-
上一张图吧:
-
配置:拦截器是springMVC的组件,所以配置在子容器中:
<!-- 配置拦截器--> <mvc:interceptors> <mvc:interceptor> <!-- 要拦截的路径--> <mvc:mapping path="/**"/> <!-- 要放过的路径--> <mvc:exclude-mapping path="/css/**"/> <bean class="com.li.springmvc.controller.component.LoginIntercepter"/> </mvc:interceptor> </mvc:interceptors>
-
写一个拦截器:这里就只展示在Controller处理之前的了,如果有其他需要再使用其他两个方法,主要是需要实现HandlerInterceptor接口
/** * 登录拦截器 */ public class LoginIntercepter implements HandlerInterceptor { /** * Controller处理之前 * @param request * @param response * @param o * @return false就拦截,true就放过 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { Object userName = request.getSession().getAttribute("userName"); if (userName==null){ System.out.println("没有登录"); request.setAttribute("msg","您尚未登录,请登录"); request.getRequestDispatcher("/index.jsp").forward(request,response); return false; } return true; } /** * controller处理之后 * @param httpServletRequest * @param httpServletResponse * @param o * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } /** * 在请求处理完成之后,视图渲染结束后 * @param httpServletRequest * @param httpServletResponse * @param o * @param e * @throws Exception */ @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
-
前端:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <%-- --%> <link rel="stylesheet" href="/style/login.css"> </head> <body> ${msg} <form action="/login" method="post"> <input type="text" placeholder="用户名" name="userName"> <input type="password" placeholder="密码" name="password"> <button type="submit"/>登录 </form> </body> </html>
-
结果:注意,springMVCInterceptor不会对webapp下面的页面,想要拦截webapp下的页面解决办法有两个,将页面放在WEB-INF里面去,或者使用Filter进行拦截
当我们在浏览器输入:http://localhost:8080/hello?name=li 会发现被拦截了,并在登录页面打印出您尚未登录,请登录.
-