Spring MVC
1. 什么是 MVC
- MVC 是一种软件架构思想,将软件按照模型、视图、控制器来划分
- M:model,模型层,指工程中的 Java bean,作用是处理数据
- Java Bean 分两类
- 实体类 Bean:专门存储业务数据的
- 业务处理 Bean:指 service 或 Dao 对象,专门用于处理业务逻辑和数据访问
- Java Bean 分两类
- V:view,视图层,指工程中的 html 或 jsp 等页面
- 作用:与用户进行交互,展示数据
- C:controller,控制层,指工程中的 servlet
- 作用:接收请求和响应浏览器
2. 什么是 Spring MVC
Spring MVC 是 Spring 为表述层开发提供的一整套完备的解决方案
三层式架构:表述层(表示层)、业务逻辑层、数据访问层
- 表述层:前台页面 + 后台 servlet
特点:
- 无缝对接 IOC 容器
- 基于原生 servlet 实现了强大的前端控制器 DispatcherServlet,对请求和响应进行统一处理
- 全方位覆盖表述层各细分领域,提供全面解决方案
- 提升开发效率
- 内部组件化程度高,配置相应组件即可使用对应的功能
- 性能卓著,适合大型互联网项目
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5</version>
</dependency>
3. 配置前端控制器
在 web.xml 文件中配置
<!--配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--服务器启动时就加载Servlet对象-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--任何请求都会经过该Servlet,不会匹配 *.jsp 请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>url-pattern中配置 / 和 /* 的区别- /:匹配浏览器向服务器发送的所有请求(不包括 .jsp 的请求)
- /*:匹配浏览器向服务器发送的所有请求(包括 .jsp 的请求)
- 由于.jsp 的请求是需要 Tomcat 中配置的 JspServlet 去处理的,DispatcherServlet 是无法对 jsp 请求进行处理,所以在配置 DispatcherServlet 时使用 / 即可
init-param:配置加载配置文件的路径<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>load-on-startup:设置 DispatcherServlet 的初始化时机- 默认初始化是在请求第一次访问 DispatcherServlet 时初始化,会造成第一次访问的请求花费的时间比较长
- 设置为 1 表示将 DispatcherServlet 初始化时机提前到服务器启动时
4. 配置视图解析器
在 spring-mvc 配置文件中进行配置
<!--配置视图解析器内部资源-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- /jsp/xxx.jsp -->
<property name="prefix" value="/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
5. @RequestMapping 注解
@RequestMapping的作用:- 将请求和处理请求的控制器关联,建立映射关系
@RequestMapping注解可以标识的位置类上:设置映射请求的请求路径的初始信息
@Controller
@RequestMapping("/user")
public class UserController {}方法上:设置映射请求的请求路径的具体信息
//通过return进行转发和重定向
@RequestMapping(value="/quick",method = RequestMethod.GET,params = {"username"})
public String save(){
System.out.println("Controller save running....");
return "success";
}
@RequestMapping的 value 属性设置多个 value 可以映射多个请求路径
@RequestMapping({"/test1","/test2"})
@RequestMapping的 method 属性method 为数组类型,用于匹配请求的请求方式(GET、POST......)
@RequestMapping(value = "/test1",method = {RequestMethod.GET,RequestMethod.POST})请求方式不匹配时会报 405
@RequestMapping的一些派生注解- 在
@RequestMapping的基础上结合请求方式而成的注解 @GetMapping@PutMapping@PostMapping@DeleteMapping
- 在
@RequestMapping的 params 属性功能:浏览器发送的请求的请求参数必须满足 params 属性的设置(所有设置都需要满足才能请求成功)
@RequestMapping(
value = "/test1",
method = {RequestMethod.GET,RequestMethod.POST},
params = {"username","!user","age=21","sex!=1"}
)params 不匹配报 400
@RequestMapping的 headers 属性匹配请求头信息
用法与 params 一致
headers 不匹配报 404,即找不到资源(请求头不匹配)
@RequestMapping注解可以使用 ant 风格的路径在注解的 value 属性中设置路径时可以往路径中加入一些特殊字符
?:表示任意单个字符(不包括?)
@RequestMapping(
value = "/a?b/test1",
method = {RequestMethod.GET,RequestMethod.POST},
params = {"username","!user","age=21","sex!=1"}
)
2. *:表示任意个数的任意字符(不包括?和/)
```java
@RequestMapping(
value = "/a*b/test1",
method = {RequestMethod.GET,RequestMethod.POST},
params = {"username","!user","age=21","sex!=1"}
)
```
3. **:表示任意层数的任意目录
```java
@RequestMapping(
value = "/**/test1",
method = {RequestMethod.GET,RequestMethod.POST},
params = {"username","!user","age=21","sex!=1"}
)
```
spring MVC 路径中的占位符
@PathVariable注解进行占位符的匹配获取工作/**
* 获得Restful风格的参数
* Restful风格:http://localhost:8080/Spring_mvc/user/quick16/lhk
* 在业务方法中使用@PathVariable注解进行占位符的匹配获取工作
* @param username
* @throws IOException
*/
@RequestMapping(value="/quick16/{username}")
@ResponseBody //该注解告知SpringMVC框架,不进行视图跳转,直接进行数据响应
public void save16(@PathVariable(value = "username") String username) throws IOException {
System.out.println(username);
}
spring MVC 获取请求参数
通过 servlet API 获取请求参数
在控制器方法中设置
HttpServlet相关的形参,即可使用 request、response 和 session 相关对象/**
* SpringMVC中获取Servlet的相关API
* @param request
* @param response
* @param session
* @throws IOException
*/
@RequestMapping(value="quick18")
@ResponseBody //该注解告知SpringMVC框架,不进行视图跳转,直接进行数据响应
public void save18(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
通过控制器的形参获取请求参数
Controller 中的业务方法参数名称与请求参数的名称一致,参数值会自动映射匹配
/**
* 获得基本类型参数
* @param username
* @param age
* @throws IOException
*/
//Controller中的业务方法参数名称与请求参数的name一致,参数值会自动映射匹配
//http://localhost:8080/Spring_mvc/user/quick10?username=lhk&age=21
@RequestMapping(value="/quick10")
@ResponseBody //该注解告知SpringMVC框架,不进行视图跳转,直接进行数据响应
public void save10(String username,int age) throws IOException {
System.out.println(username);
System.out.println(age);
}当请求的参数名称与 Controller 的业务方法参数名称不一致时,可以使用
@RequestParam注解将请求参数和方法中的形参显示的进行绑定- value 设置请求参数的名称
- required = true 表示必须传一个请求参数,否则报 400 的错误
- defaultValue 设置形参的默认值
/**
* 当请求的参数名称与Controller的业务方法参数名称不一致时,
* 就需要使用@RequestParam注解显示的绑定
* @param username
* @throws IOException
*/
@RequestMapping(value="/quick15")
@ResponseBody //该注解告知SpringMVC框架,不进行视图跳转,直接进行数据响应
public void save15(@RequestParam(value = "name" , required = false,defaultValue = "lhk") String username) throws IOException {
System.out.println(username);
}@RequestHeader:将请求头信息和控制器方法的形参进行绑定/**
* 获取请求头的信息
* @param user_agent
* @throws IOException
*/
@RequestMapping(value="quick19")
@ResponseBody //该注解告知SpringMVC框架,不进行视图跳转,直接进行数据响应
public void save19(@RequestHeader(value = "User-Agent") String user_agent) throws IOException {
System.out.println(user_agent);
}@CookieValue:将 cookie 数据和控制器方法的形参进行绑定/**
* 通过注解@CookieValue获取cookie的值
* @param jsessionid
* @throws IOException
*/
@RequestMapping(value="quick20")
@ResponseBody //该注解告知SpringMVC框架,不进行视图跳转,直接进行数据响应
public void save20(@CookieValue(value = "JSESSIONID") String jsessionid) throws IOException {
System.out.println(jsessionid);
}通过 pojo 获取请求参数
- 控制器方法的实体类类型形参的属性名和请求参数名保持一致,即可将请求参数映射到实体类类型的形参中
/**
* 获得pojo类型参数
* @param user
* @throws IOException
*/
//Controller中的业务方法中的pojo参数属性名称与请求参数的name一致,参数值会自动映射匹配
//http://localhost:8080/Spring_mvc/user/quick11?name=lhk&age=21
@RequestMapping(value="/quick11")
@ResponseBody //该注解告知SpringMVC框架,不进行视图跳转,直接进行数据响应
public void save11(User user) throws IOException {
System.out.println(user);
}
解决获取请求参数乱码问题
在设置编码执行之前,不能获取任何的请求参数,如果先获取了请求参数,再设置编码将不会生效
spring MVC 处理编码的过滤器一定要设置在其他过滤器之前,否则无效
在 web.xml 文件中配置全局过滤器
<!--配置全局过滤的filter 解决post请求乱码问题-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 设置请求的编码 -->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<!-- 设置响应的编码 -->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
6. 往域对象中共享数据
使用 servletAPI 往 request 域对象中共享数据
@RequestMapping(value="/quick5")
public String save5(HttpServletRequest request){
request.setAttribute("username","大时代");
return "success";
}使用 modelAndView 往 request 域对象中共享数据
model:向请求域中共享数据
view:设置逻辑视图
//方法中传入SpringMVC提供的ModelAndView对象参数,进行转发和重定向
@RequestMapping(value="/quick3")
public ModelAndView save3(ModelAndView modelAndView){
//设置模型数据
modelAndView.addObject("username","LHK");
//设置视图名称(ViewName)
modelAndView.setViewName("success");
return modelAndView;
}
//通过返回ModelAndView对象进行转发和重定向
@RequestMapping(value="/quick2")
public ModelAndView save2(){
ModelAndView modelAndView = new ModelAndView();
//设置模型数据
modelAndView.addObject("username","TheMetents");
//设置视图名称(ViewName)
modelAndView.setViewName("success");
return modelAndView;
}使用 model 往 request 域中共享数据
@RequestMapping(value="/quick4")
public String save4(Model model){
model.addAttribute("username","lhk1008611");
return "success";
}使用 ModelMap 往 request 域中共享数据
// modelMap 的使用
@RequestMapping(value="/modelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("username","lhk1008611");
return "success";
}使用 map 往 request 域中共享数据
// map 的使用
@RequestMapping(value="/map")
public String testModelMap(Map<String,Object> map){
map.put("username","lhk1008611");
return "success";
}model 、ModelMap 、map 的关系
底层都是都是通过
BindingAwareModelMap创建,本质上都是BindingAwareModelMap类型public class BindingAwareModelMap extends ExtendedModelMap {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class ModelMap extends LinkedHashMap<String, Object> {}
往 session 与中共享数据
使用 servletAPI 往 session 域中共享数据
//往session域中共享数据
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("key","value");
return null;
}
往 application 域中共享数据
//往应用域中共享数据
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("key","value");
return null;
}
7. springMVC 的视图
springMVC 中的视图是 View 接口
- 视图的作用是渲染数据,将 model 的数据展示给用户
- springMVC 中默认的转发视图(internalResourceView)和重定向视图(redirectView)
- 当工程中引入 jstl 的依赖,转发视图会自动转换为 jstlView
- 若在springMVC的配置文件中配置了 Thymeleaf 的视图解析器,则此视图解析后的视图为 ThymeleafView
ThymeleafView
配置 Thymeleaf 的视图解析器
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>配置了 ThymeleafViewResolver 后
- 在 controller 中直接
return "视图名称"会解析成 ThymeleafView 进行视图渲染 - 在 controller 中直接
return "forward:/xxx/xxx"会解析成 internalResourceView 实现路由转发 - 在 controller 中直接
return "redirect:/xxx/xxx"会解析成 redirectView实现重定向- 转发和重定向的区别:
- 转发直接跳转到页面不会改变页面 url
- 重定向直接跳转到页面会将页面 url 变为跳转后页面的 url
- 转发和重定向的区别:
- 在 controller 中直接
视图控制器
配置视图控制器
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>开启注解驱动
若不开启 mvc 的注解驱动,则只会处理视图控制器所对应的请求,其他请求 404;
开启 mvc 的注解驱动后可以处理视图控制器和 controller 中的所有请求
<mvc:annotation-driven /">
8. RESTFul 风格
REST : Representational State Transfer,表现层资源状态转移
HTTP method restful 动作 获取url中的参数的注解 占位符url get /user/1 查询 @PathVariable/url/{id} post /user 新增 put /user/1 修改 @PathVariable/url/{id} delete /user/1 删除 @PathVariable/url/{id} 通过超链接发送的是 get 请求
浏览器只能发送 get 或 post 请求
默认情况下 form 表单只能发送 get 和 post 请求
后端通过配置处理 http method 的过滤器可以实现发送 put、delete 请求的效果
<filter>
<filter-name >HiddenHttpMethodFilter</filter-name>
<filter-class >org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>前端 form 表单需要将 method 属性设置为 post,并在表单传送一个 _method 参数到 controller 方法
<form action="/Spring_mvc/user/quick13" method="post">
<input type="hidden" name="_method" value="put">
.......
<input type="submit" value="提交">
</form>
通过查看 HiddenHttpMethodFilter 这个过滤器源码中的 doFilterInternal 方法可以了解底层是如何对 http method 进行处理
9. springMVC 处理 ajax 请求
/**
* 使用ajax提交时,可以指定contentType为json形式
* 那么在 controller 方法参数位置使用 @RequestBody 可以直接接收集合数据而无需对集合进行pojo包装
* @param userlist
* @throws IOException
*/
@RequestMapping(value="/quick14")
@ResponseBody //该注解告知SpringMVC框架,不进行视图跳转,直接进行数据响应
public void save14(@RequestBody List<User> userlist) throws IOException {
System.out.println(userlist);
}
@RequestBody注解:获取请求体信息- 导入 jackson 包配合
@RequestBody注解可将请求体数据解析为 Java 类型数据
- 导入 jackson 包配合
@ResponseBody注解:将 controller 方法返回数据以响应报文的响应体形式返回给客户端- 导入 jackson 包配合
@ResponseBody注解可将 controller 方法返回的 Java 类型数据解析为 json 格式数据以响应报文的响应体形式返回给客户端
- 导入 jackson 包配合
需要开启 springmvc 的注解驱动
<mvc:annotation-driven /">@RestController=@Controller+@ResponseBody- 该注解一般标识在 controller 类的前面
10. 文件上传和下载
文件上传:将文件从浏览器复制到服务器
文件下载:将文件从服务器复制到浏览器
Java 原生文件上传下载实现:通过 IO 流实现文件上传下载,输入流读取文件,输出流见文件输出到指定位置
spring MVC 中的文件上传下载
文件下载
使用
ResponseEntity响应报文对象作为 controller 方法的返回值类型,可以实现文件下载功能/**
* 文件下载
* @param httpSession
* @return ResponseEntity
* @throws IOException
*/
@RequestMapping("/download")
public ResponseEntity<byte[]> download(
HttpSession httpSession
) throws IOException {
//1. 获取需要下载的文件路径
ServletContext servletContext = httpSession.getServletContext();
String imagePath = servletContext.getRealPath("image");
String filePath = imagePath + File.separator + "appreciate_afdian.png";
//2. 创建输入流读取文件字节
FileInputStream fileInputStream = new FileInputStream(filePath);
byte[] bytes = new byte[fileInputStream.available()];
//将文件流读入字节数组
fileInputStream.read(bytes);
//3. 创建 HttpHeaders 对象,设置头信息
MultiValueMap<String,String> httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Disposition","attachment;filename=appreciate_afdian.png");
//4. 返回 ResponseEntity 对象,作为响应报文响应到浏览器
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes,httpHeaders,HttpStatus.OK);
fileInputStream.close();
return responseEntity;
}
文件上传
使用
MultipartFile对象接收文件上传参数,调用transferTo方法实现文件上传<h2>
<span>文件上传</span>
</h2>
<form action="${pageContext.request.contextPath}/user/upload" method="post" enctype="multipart/form-data">
文件1:<input type="file" name="uploadFile"><br>
文件2:<input type="file" name="uploadFile"><br>
<input type="submit" value="上传"><br>
</form>/**
* 多文件上传,表单中文件名不同
* 单文件上传,传入一个MultipartFile参数即可
* @param uploadFile
* @throws IOException
*/
@RequestMapping(value="upload")
public String uploadFile(
MultipartFile[] uploadFile,
HttpSession httpSession
) throws IOException {
ServletContext servletContext = httpSession.getServletContext();
String imagePath = servletContext.getRealPath("image");
File file = new File(imagePath);
System.out.println(uploadFile.length);
if(!file.exists()){
file.mkdir();
}
//将文件保存到磁盘
for (MultipartFile multipartFile : uploadFile) {
if (!multipartFile.isEmpty()){
multipartFile.transferTo(new File(imagePath+File.separator+multipartFile.getOriginalFilename()));
}
}
return "success";
}需要配置文件上传解析器
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="500000"></property>
</bean>需要导入文件上传包
<!--文件上传包-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>问题: 上传同名文件会将文件的原内容覆盖
- 解决:将文件名设置为不重复的文件名即可
- 文件名添加 uuid 或使用 uuid 作为文件名
- 文件名添加
时间戳或使用时间戳作为文件名
- 解决:将文件名设置为不重复的文件名即可
11. 拦截器
filter 与 拦截器的区别
filter:会在请求达到 dispatchSeverlet 之前执行,先对请求进行过滤
拦截器:是在 dispatchSeverlet 匹配到对应的处理器(controller 或 handler)后,在该处理器前后对请求进行拦截
三个方法
@Override
//preHandle()方法是 在处理器方法执行前 执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
@Override
//postHandle()方法是 在处理器方法执行后 视图返回之前 执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
//afterCompletion()方法是在 整个访问资源流程执行完毕后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
拦截器的使用
创建拦截器类,实现接口
HandlerInterceptorpublic class MyInterceptor1 implements HandlerInterceptor {
@Override
//preHandle()方法是 在目标方法执行前 执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle().......");
String param = request.getParameter("param");
if ("yes".equals(param)){
return true;//返回true代表放行,返回false则不会执行目标方法
}else {
request.getRequestDispatcher("/error.jsp").forward(request,response);
return false;
}
}
@Override
//postHandle()方法是 在目标方法执行后 视图返回之前 执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle()............");
}
//afterCompletion()方法是在 整个访问资源流程执行完毕后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion().....");
}
}配置拦截器,使拦截器生效
/**表示对所有请求都使用该拦截器进行拦截配置方式1
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--配置需要进行拦截的资源-->
<mvc:mapping path="/**"/>
<bean class="com.lhk.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!--配置需要进行拦截的资源-->
<mvc:mapping path="/**"/>
<bean class="com.lhk.interceptor.MyInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>使用
@Component注解和注解扫描配置拦截器<!--组件扫描-->
<context:component-scan base-package="com.lhk.interceptor"></context:component-scan>
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--配置需要进行拦截的请求-->
<mvc:mapping path="/**"/>
<bean class="com.lhk.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!--配置需要进行拦截的请求-->
<mvc:mapping path="/**"/>
<ref bean="myInterceptor2"></ref>
</mvc:interceptor>
</mvc:interceptors>@Component
public class MyInterceptor2 implements HandlerInterceptor {
//....
}
多个拦截器的执行顺序
preHandle().......111111
preHandle().......222222
controller 执行....
postHandle()........222222
postHandle().......111111
afterCompletion().....222222
afterCompletion().....111111preHandle方法按拦截器配置的顺序依次执行,postHandle和afterCompletion按拦截器配置的逆序依次执行
12.异常解析器
HandlerExceptionResolver接口DefaultHandlerExceptionResolver:默认的异常解析器,内部实现了对异常的默认处理方式SimpleMappingExceptionResolver:可以配置异常的处理方式,可以配置异常出现时跳转至哪个页面<!--配置SpringMVC的异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error"></property>
<property name="exceptionMappings">
<map>
<entry key="java.lang.ClassCastException" value="error1"></entry>
<entry key="com.lhk.exception.MyException" value="error2"></entry>
</map>
</property>
</bean>自定义异常解析器,可实现异常处理的定制化
创建异常解析类实现
HandlerExceptionResolver接口,即可public class MyExceptionResolver implements HandlerExceptionResolver {
/**
*
* @param httpServletRequest
* @param httpServletResponse
* @param e 异常对象
* @return ModelAndView 跳转到错误视图信息页面
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView = new ModelAndView();
if (e instanceof ClassCastException){
modelAndView.addObject("info","类转换异常");
}else if (e instanceof MyException){
modelAndView.addObject("info","自定义异常");
}
modelAndView.setViewName("error");
return modelAndView;
}
}<!--配置自定义的异常处理器-->
<bean class="com.lhk.resolver.MyExceptionResolver"></bean>
自定义异常
创建异常类继承
Exception,即可得到自定义异常public class MyException extends Exception{
}
注解实现自定义异常解析器
@ControllerAdvice注解可将类标识为异常解析器组件@ExceptionHandler注解表示遇到哪些异常时需要进行异常解析@ControllerAdvice
public class DemoController_1 {
@Autowired
private DemoService demoService;
@ExceptionHandler(ClassCastException.class)
public String Show(Throwable ex, Model model) {
System.out.println("遇到异常时进行一些处理.....");
demoService.show1();
model.addAttribute("ex", ex);//将异常信息存入请求域
return "error";//出现 ClassCastException 异常时跳转到 error 页面
}
}
13、注解配置 Spring MVC
使用配置类和注解来替代 web.xml 和 SpringMVC 的配置文件
配置类替代 web.xml
/**
* 代替 web.xml 文件的配置类
*/
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
//设置配置类代替 Spring 的配置文件
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
//设置配置类代替 SpringMVC 的配置文件
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
//设置 SpringMVC 的前端控制器 DispatcherServlet 的url-pattern
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
//设置过滤器
protected Filter[] getServletFilters() {
//编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("utf-8");
characterEncodingFilter.setForceEncoding(true);
//处理请求方式过滤器
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter};
}
}配置类代替 Spring 配置文件
- 使用
@Configuration注解将类标识为配置类
- 使用
配置类代替 Spring 配置文件
使用
@Configuration注解将类标识为配置类实现
WebMvcConfigurer类配置 组件扫描、视图解析器、默认的 Servlet 处理静态资源 、mvc 注解驱动、视图控制器、文件上传解析器、拦截器、异常解析器
/**
* 配置类代替 springMVC 配置文件
*/
@Configuration
@ComponentScan("com.lhk.controller") //组件扫描
@EnableWebMvc //开启 mvc 的注解驱动
public class WebConfig implements WebMvcConfigurer {
@Override
//配置默认 Servlet 处理静态资源
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
//配置视图控制器
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
@Bean //可以将方法的返回值作为 bean 交给容器进行管理,bean 的 id 为注解标识的方法名
//配置文件上传解析器
public CommonsMultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
@Override
//配置拦截器
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").excludePathPatterns("/");
}
@Override
//配置异常解析器
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.ClassCastException", "error");
simpleMappingExceptionResolver.setExceptionMappings(properties);
simpleMappingExceptionResolver.setExceptionAttribute("ex");
}
//以下为配置视图解析器
//1.配置生成模板解析器
@Bean
public ITemplateResolver templateResolver() {
WebApplicationContext currentWebApplicationContext = ContextLoader.getCurrentWebApplicationContext();
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(currentWebApplicationContext.getServletContext());
templateResolver.setPrefix("/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("utf-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
//2.配置生成模板引擎,并注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
springTemplateEngine.setTemplateResolver(templateResolver);
return springTemplateEngine;
}
//3.配置生成视图解析器,并注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
thymeleafViewResolver.setCharacterEncoding("UTF-8");
thymeleafViewResolver.setTemplateEngine(templateEngine);
return thymeleafViewResolver;
}
}
14、spring MVC 的整个流程
1. 常用组件
DispatcherServlet:前端控制器,由框架提供- 统一处理请求和响应
HandlerMapping:处理器映射器,由框架提供- 根据请求的 url、method 等信息映射到对应的处理器(controller 方法)
- `
Handler:处理器(Controller),需要工程师开发- 在
DispatcherServlet的控制下,Handler 对请求进行具体的处理
- 在
HandlerAdapter:处理器适配器,由框架提供- 通过
HandlerAdapter执行处理器(controller 方法)
- 通过
ViewResolver:视图解析器,由框架提供- 进行视图解析,得到相应的视图,如:
ThymeleafView,internalResourceView
- 进行视图解析,得到相应的视图,如:
View:视图- 将模型数据通过页面展示给用户
2. 具体执行流程
- 浏览器发送请求
- 检查请求地址是否与 web.xml 文件中设置的
url-pattern相匹配,匹配成功则将该请求交由 DispatcherServlet 进行处理 - 前端控制器 DispatcherServlet 读取 SpringMvc 的核心配置文件,通过组件扫描找到 controller (控制器),检查请求地址是否与控制器中的
@RequestMapping注解中的 value 值相匹配,匹配成功则该注解所标识的 controller (控制器)方法会对该请求进行处理 - controller 方法需要返回一个字符串的类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,并对该视图对应的页面进行渲染,最终将请求转发到视图所对应的页面
15. 整合SSM
springMVC 的 IOC 容器是在初始化
dispatcherServlet时进行的创建的,由于 controller 层会依赖 service 层的组件,故 spring 的 IOC 容器应该在 springMVC IOC 容器创建之前进行创建- 故:在整合 spring 和 springMVC 的时候,项目启动时应先读取 spring 的配置文件创建 spring 的 IOC 容器,再读取SpringMVC的配置文件,创建它的 IOC 容器
在 Tomcat 中,listener、filter、servlet 的执行顺序是
listener——>filter——>servlet- 故:可以在监听器(listener)执行时加载 spring 配置文件,创建 spring 的 IOC 容器,即可实现 spring 与 springMVC 的整合
监听器(
listener)ServletContextListener:用于监听 Web 应用程序的启动和关闭事件,即 Servlet 上下文的创建和销毁事件ServletRequestListener:用于监听 HTTP 请求的开始和结束事件,即 Servlet 请求的创建和销毁事件HttpSessionListener:用于监听 HTTP 会话的创建和销毁事件,即会话的创建和销毁事件。ServletContextAttributeListener:用于监听 Servlet 上下文属性的添加、移除和替换事件。ServletRequestAttributeListener:用于监听 Servlet 请求属性的添加、移除和替换事件。HttpSessionAttributeListener:用于监听会话属性的添加、移除和替换事件。
这些监听器可以通过在 Web 应用程序的配置文件(web.xml)中进行注册,或通过注解的方式进行配置
在 spring中提供了
ContextLoaderListener实现了ServletContextListener,用于在 Web 应用程序启动时加载和初始化 Spring 应用程序上下文该类会在 Web 服务器启动时,读取 spring 的配置文件,创建 spring 的 IOC 容器
在 web.xml 中配置
<!--Spring的监听器 默认读取 WEB-INF/application.xml-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--全局的初始化参数,可自定义 spring 配置文件的位置和名称-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
在 pom.xml 文件中可以在
properties标签中统一管理依赖版本号<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>
<spring.version>5.0.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>有 mybatis 为何还需导入
spring-jdbc- 因为
spring-jdbc中有事务管理器,目的是为了导入事务管理器,更好的进行实物相关操作
- 因为
在 web.xml 中配置 spring 的编码过滤器、配置处理请求方式的过滤器、配置 spring 的前端控制器
dispatcherServlet、配置 spring 的监听器用于加载 spring 的配置文件、配置 spring 配置文件的自定义位置和名称- 在 springMVC.xml 中配置组件扫描(controller)、视图解析器、默认 servlet 处理静态资源、mvc 注解驱动、视图控制器、文件上传解析器,拦截器和异常处理器按需配置即可
在 spring.xml 中配置 组件扫描(除 controller),配置数据源
整合 mybatis ,可以将一些卸载 mybatis.xml 文件中的配置写在 spring.xml 中
需要配置
SqlSessionFactory交给 IOC 容器管理在 spring,xml 中配置 mybatis 的
SqlSessionFactory<!--
配置mybatis的SqlSessionFactory
其中SqlSessionFactoryBean是mybatis-spring包提供的SqlSessionFactory接口的一个实现
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property><!--加载数据源-->
<property name="configLocation" value="classpath:mybatis-config-spring.xml"></property><!--加载mybatis核心配置文件-->
<property name="mapperLocations" value="classpath:com.lhk.mapper/*.xml"></property><!--加载映射文件-->
</bean>在 spring,xml 中配置 mybatis 的
SqlSessionFactoryBean,可以直接在 spring 的 IOC 容器中获取SqlSessionFactory<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property><!--加载数据源-->
<property name="configLocation" value="classpath:mybatis-config-spring.xml"></property><!--加载mybatis核心配置文件-->
<property name="mapperLocations" value="classpath:com.lhk.mapper/*.xml"></property><!--加载映射文件,只有 mapper 接口的包和映射文件的包不一致时需要设置-->
</bean>
在 spring,xml 中配置 mapper 接口扫描,可以将指定包下所有的 mapper 接口通过
SqlSession创建代理实现类对象,并交给 IOC 容器管理,这样可以直接在 service 层中注入 mapper 组件<!--扫描mapper相关接口所在的包 为mapper创建实现类-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lhk.mapper"></property>
</bean>配置事务管理器
<!--声明式事式控制-->
<!--平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property><!--注入数据元-->
</bean>
<!-- 开启事务的注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>