目录标题
Spring MVC框架
Spring Framework 包含了 IOC, AOP, DT, Spring Test, Spring MVC ··· ···
M
------ 指的是 Model, 模型,数据的处理层,包含 Entity/pojo/bean, dao, servcie, …V
------ 指的是 View,视图, 做数据呈现,包含:HTML/CSS/JS, AJAX, JSP, Freemaker, Velocity, Thymeleaf, …C
------ 指的是 Controller, 控制器,做请求和响应的处理,包含: Servlet, Controller, Action, …
Spring MVC框架的功能
- 基于Spring Framework 的生态,完美与Spring IOC, AOP , DT ,Spring-test 集成
- 提供了非侵入式的控制器,它通过配置来完成
- 提供了视图解析器,使得视图技术与解析技术分离,这样,Spring MVC可以支持多种不同的视图技术,如:JSP, FreeMarker, Velocity, thymeleaf ··· ···
- 提供了ModelAndView, 可以把视图与模型整合在一起,交给核心控制器进行解析。
- 提供了异常处理机制。
- 提供了表单数据的后端验证
- 支持国际化[i18n]
- …
Spring MVC项目的开发步骤
- 准备maven项目
- 在pom.xml 中导入依赖 spring-webmvc :
<!-- springmvc 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
- 在web.xml中要配置Spring mvc框架的入口【要启动Spring mvc】,也就是配置前置控制器 DispatherServlet :
<!-- spring mvc 主控制器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.hc.config.WebMvcConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 编写基于注解的Web配置类,名叫:WebMvcConfig.java , 在此类中 启动MVC模式,扫描 controller 包等
package com.hc.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/*************
* 代表的 ACWAC,也就是WEB应用相关的上下文
*/
@Configuration //表示这是一个Spring的注解配置类
@ComponentScan({"com.hc.controller"}) //扫描控制层的包
@EnableWebMvc //启动 WebMvc
//@Import(value = {AppConfig.class}) //加载IOC容器应用上下文
//这就表明,此应用将有两个ApplicationContext
public class WebMvcConfig implements WebMvcConfigurer {
//nothing!
}
- 开发你自己的控制器【想象成 Servlet 】
- 做测试
Spring MVC 工作流程示意图:
Spring MVC框架下控制器的开发
框架本身提供了一个前置控制器,也就是 DispatcherServlet, 我们也把它叫 大C, 而我们自己开发的控制器叫 小C, 大C 接收客户端的请求后,负责把这个请求分发给目标 小C, 它是如何确定分发给哪一个 小C 呢?
“小C” 的开发
在Spring MVC框架下,我们开发小C,不需要实现任何的接口或继承任何抽象类,就是一个普通的JavaBean,所以说,Spring MVC框架是非侵入式的。
package com.hc.controller;
import com.hc.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
@RequestMapping("/stu")
public class StudnetController {
@RequestMapping("/toadd")
public String toAddStudent(HttpServletRequest request, HttpServletResponse response) {
return "addStudent";
}
@RequestMapping(value = "/add",produces = "application/json")
@ResponseBody
public Student addStudent(Student student) {
System.out.println(student);
student.setId(100);
return student;
}
}
请求参数的处理
处理响应
跳转的处理
Spring MVC的核心功能
Spring MVC的操作
- 搭建项目环境
- 开发小C
- ①方法的返回值
* String —> 表示逻辑视图名【注: 如果不想经过视图解析器话,在String前面加 forward:或redirect: 前缀。
* JAVA数据类型 + @ResponseBody --> 表示直接以返回值做为响应的内容,建议内容是 application/json 格式 【用在异步请求中】
* void -> 不建议使用
* ModelAndView -> 把模型和视图封装在一起返回给大C, 由大C进一步解析。【这种做法是老版本的做法】
- ②方法的参数
* Spring MVC内置的参数
* request, response, httpSession, … 【在Servlet时代中存在的】
* Model, MultipartFile, …
* 由客户端传递给服务端的请求参数
* 可以是非表单中的请求参数【以queryString的方法传递过来】
* 可以是表单中的请求参数【一般以实体类做为参数,可以有多个】
- ③有关 @RequestMapping 注解
* value 属性,指定此方法接收的请求URI【Uniform Resource Identifier, 统一资源定位符】,
* produces 属性,会配合@ResponseBody使用, 指定此方法的返回内容以什么格式写到客户端,大多数时候,是写 application/json
* method属性,指定此方法只能接收哪种类型【GET, POST, DELETE, PUT, PATCH】的请求。
- 单元测试
Spring MVC中常用的功能
一、文件上传和下载
- 在
WebMvcConfig.java
中进行配置对 Multipart 文件的配置, 如下:
@Bean public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); //设置 一些属性 multipartResolver.setDefaultEncoding("UTF-8"); multipartResolver.setMaxUploadSizePerFile(2*1024*1024); //每个文件大小 multipartResolver.setMaxUploadSize(20*1024*1024); //总大小 // return multipartResolver; }
表明你的大C有能力处理 MultipartFile 类型的数据,有能力处理文件上传
- 在前端页面中的表单中,要指定两个东西:
- method=“post”
- enctype=“multipart/form-data”
<form name="xxxForm" action="/upload" method="POST" enctype="multipart/form-data"> .... <input type="file" name="uploadFile"> </form>
- 在小C中,就可以注入 MultipartFile 对象,这个对象就封装了文件上传的所有操作, 如下:
public class FileController { @RequestMapping(value = "/upload") public String upload(MultipartFile uploadFile) { //.... return ""; } }
- 下载时的注意事项:
- 要告诉浏览器,响应客户内容的类型,也就是要调用:
response.setContentType("application/x-download;charset=utf-8")
- 其次,在响应头中,告诉浏览器该如何处理这个文件? 是直接打开还是弹出对话框让用户保存或打开。
response.setHeader("Content-Disposition", "attachment;filename=xxxx.png")
二、Bean Validation
由于前端的验证很容易就被“跳过”, 也就是说数据传递到后端还存在很大的不安全性,为了保证这些数据在传递到数据库之前是合法的,我们在后端需要对前端传递过来的数据做进一步的验证。这个就是后端的Bean Validation, 也叫 JSR303 规范
后端的数据验证方式
- 手动编写一个工具类,利用正则表达式就可以来验证各种数据,比如:Email, phone, birth, age, …
- 通过配置方式来完成验证,无需程序员编写验证逻辑代码【我们要这种】
- 注:
Spring MVC框架完全支持JSR303规范,所以,我们在使用spring mvc框架就可以使用 bean validation 技术。
- 注:
JSR303规范,有一个很好的实现者就是 Hibernate-validator, 【不是Hibernate框架】,而Spring MVC本着不重复发明车轮的理念,并没有重新去实现 JSR303规范,而是直接使用 hibernate-validator 框架。
如何使用 Bean Validation?
- 在 pom.xml中,导入 hibernate-validator 的组件
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
- 配置 Spring MVC中内置的 BeanValidator 【验证器】,在
WebMvcConfig.java
中, 重写 getValidator 方法。
/****************************
* 重写父类的方法,不再返回NULL,而是要返回一个有效的BeanValidator
* @return
*/
@Override
public Validator getValidator() {
LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
//设置属性 【可选】
validatorFactoryBean.setValidationMessageSource(messageSource());
//
return validatorFactoryBean;
}
/***
* 利用消息文件,可以做国际化[i18n] [可选]
* @return
*/
private MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
//设置属性
messageSource.setDefaultEncoding("UTF-8");
messageSource.setBasename("validateMessage");
//
return messageSource;
}
- 在你的需要被验证的实体类中,使用JSR303的注解来修饰,如下:
import javax.validation.constraints.*;
public class Student {
private Integer id;
@NotNull(message = "{error.name.notnull}")
@Size(min = 6, max=12, message = "{error.name.size}")
private String name;
@NotNull(message = "{error.stuno.notnull}")
@Pattern(regexp = "[A-Z][0-9]+",message = "{error.stuno.pattern}")
private String stuNo;
private Gender gender;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Past(message = "{error.birth.future}")
private Date birth;
//....
}
- 在小C的方法中,利用 @Valid 注解和 BindingResult 类来处理验证失败后的提示信息
@RequestMapping(value = "/add",method = RequestMethod.POST)
public String addStudent(@Valid Student student, BindingResult result, Model model) {
//要验证
if(result.hasErrors()) {
// 说明验证失败, 取出每一个属性对应的message
List<FieldError> fieldErrors = result.getFieldErrors();
/*for(FieldError fe : fieldErrors) {
System.out.printf("%s -> %s\n", fe.getField(), fe.getDefaultMessage());
//绑定到model中去,以便在目标资源可以显示出来
//model.addAttribute(fe.getField(), fe.getDefaultMessage());
}*/
//转换成map,绑定到前台
model.addAttribute("ERROR_MAP",convertMap(fieldErrors));
return "student/add";
} else {
//调用业方法
studentService.add(student);
model.addAttribute("STUDENT_KEY",student);
//跳转|重定向 到 /list 请求中
return "redirect:/stu/list"; //如果带上 forward或redirect 关键后,就表示不再经过视图解析器
}
}
//把List转换成map
private Object convertMap(List<FieldError> fieldErrors) {
Map<String, String> map = new HashMap<>();
for(FieldError fieldError : fieldErrors) {
map.put(fieldError.getField(), fieldError.getDefaultMessage());
}
return map;
}
注:
如果要做国际化的话,则应该把错误提示信息,单独存储在一个 xxxx.properties 文件中,这个文件可以做成国际化文件,后面带语言代号和国家代码,如: xxx_zh_cn.properties
如:
error.name.notnull=学号不能为
error.name.size=学员名长度要在6至12个字符之间
error.stuno.notnull=学号不能为
error.stuno.pattern=学号必需以大写字母开头
error.birth.future=生日时间不能大于当前时
- 注:上面的中文一定要转成 unicode 码,网上有很多的在线工具可以使用帮助。
三、拦截器[Interceptor]
在 Servlet学习中,我们已经了解过一个请求拦截的组件叫 Filter, 它是在 WEB Container层面进行请求的拦截,面我们在这里学习的 Interceptor ,是针对 控制器请求的拦截。
开发拦截器
- 写一个类实现 HandlerInterceptor 接口, 并重写你感兴趣的方法,一般是 preHandle方法
- 配置这个拦截器, 支持两种配置方式
- 通过xml配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/stu/*"/>
<bean class="com.hc.interceptors.HelloInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
```
然后,要把这个xml让我们的注解配置去读取,如下 :
@Configuration //表示这是一个Spring的注解配置类
@ComponentScan({"com.hc.controller"}) //扫描控制层的包
@EnableWebMvc //启动 WebMvc
@Import(value = {AppConfig.class}) //加载IOC容器应用上下文
//读到xml文档
@ImportResource(value = "classpath:applicationContext.xml") //读取
public class WebMvcConfig implements WebMvcConfigurer {
//....
}
- 通过代码配置,直接在
WebMvcConfig.java
类中,重写父类的 addInterceptors方法即可,如下:
/**
* 通过代码的方式来添加拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("--- 代码注册拦截器....");
HandlerInterceptor helloInterceptor = new HelloInterceptor();
//
registry.addInterceptor(helloInterceptor).addPathPatterns("/stu/*");
}
四、控制器的测试
Spring MVC中的异常处理
支持两种,一种是局部针对某一个控制器中的异常处理,另一种是全局的异常处理
单个控制器的异常处理【基于注解】
- @ExceptionHandler 注解
在需要做异常处理的控制器中,写一个异常处理方法,在此方法上面打上 @ExceptionHandler注解,如下 :
@ExceptionHandler(value = {RuntimeException.class,SQLException.class}) public String handlerException(Throwable t, Model model) { // System.out.println("出现异常了:"); //日志记录: log.debug(t); if(t instanceof RuntimeException) { System.out.println("运行时异常"); //跳到某个页面 } else if(t instanceof SQLException) { System.out.println("SQL异常"); //跳到某个页面 } // model.addAttribute("EXCEPTION",t); // return "500"; }
注:这种方式只能处理当前的控制器出现的异常。
全局异常处理器【基于注解】
- 写一个类实现 HandlerExceptionResolver 接口,并重写它的方法
- 配置这个全局异常处理类型,有两种方式
-
基于注解的配置
-
在此类上面使用 @Component 注解
-
在 WebMvcConfig.java中,在@ComponentScan注解中,把此异常处理类所在的包添加进去,如下 :
@Configuration //表示这是一个Spring的注解配置类 @ComponentScan({"com.hc.controller","com.hc.exhandler"}) //扫描控制层及异常处理层的包 @EnableWebMvc //启动 WebMvc @Import(value = {AppConfig.class}) //加载IOC容器应用上下文 //读到xml文档 //@ImportResource(value = "classpath:applicationContext.xml") //这就表明,此应用将有两个ApplicationContext public class WebMvcConfig implements WebMvcConfigurer { //... }
-
-
基于代码的配置
-
直接在 WebMvcConfig中,重写父类的一个方法: configureHandlerExceptionResolvers ,如下:
@Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { System.out.println("-- 代码注册全局异常处理器..."); System.out.printf("-- 已经有的异常处理器个数:%d\n",resolvers.size()); HandlerExceptionResolver her = new GlobalExceptionHandler(); resolvers.add(her); }
-
Note:
欢迎点赞,留言,转载请在文章页面明显位置给出原文链接
知者,感谢您在茫茫人海中阅读了我的文章
没有个性 哪来的签名!
详情请关注点我
持续更新中
© 2020 09 - Guyu.com | 【版权所有 侵权必究】 |