spring mvc的简介

spring mvc一开始就集成于spring框架中,其功能十分强大,在web项目中主要的亮点有支持RESTful风格的请求、拦截器、异常处理、国际化、数据视图解析、跨域配置等。下面讲解spring mvc与spring搭配的web项目主要用法。

一个请求的执行过程

在使用了spring mvc的项目里,所有请求都会被spring容器中的DispatcherServlet拦截处理,整体过程:

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器
  3. 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
  4. DispatcherServlet调用HandlerAdapter处理器适配器
  5. HandlerAdapter经过适配调用具体的处理器(Controller)
  6. Controller执行完成返回ModelAndView
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServletModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View
  10. DispatcherServlet根据View进行渲染视图(将模型数据填充至视图中)
  11. DispatcherServlet响应用户

主要注解以及使用方式

注解 使用方式
@ResponseBody 通常与@Controller搭配使用,将返回的对象通过转换器转换为json或xml对象
@RestController 相当于@Controller@ResponseBody的整合
@RequestMapping 注解在类或方法上,其value值指定当前控制器url
@ControllerAdvice 注解在全局异常处理类上,可将该类注入到ioc容器中
@ExceptionHandler @ExceptionHandler搭配使用,注释在方法上,参数可以指定要捕捉的异常
@Configuration 注释在配置类上,通常用于各种配置相关的内容
@CrossOrigin 前后端分离时的跨域处理,注释在控制类或其方法上
@Controller 标注一个类为控制器(Controller),让其接收所有http请求
@RequestParam 注解在控制器方法参数列表中,获取url中所带的参数

ModelAndView的使用

在前后端不分离的情况下,Controller可以通过返回一个ModelAndView来传递返回的数据与解析视图。从名称上来看就可以知道,其囊括了MVC三层架构中的model层与view层。
要让其跳转到相应的视图则要在配置文件中配置视图解析器:

<!-- 配置视图解析器(InternalResourceViewResolver) -->
<bean id= "parser" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 给所有的返回值增加前缀/views/,增加后缀.jsp -->
<property name="prefix" value="/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

这里返回的视图为success.jsp,传递的对象为student1

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
ModelAndView mv=new ModelAndView("success");
Student student=new Student(1, "zhangsan");
mv.addObject("student1", student);
return mv;
}

前后端分离通过json传递数据

现在大部分的web项目都会进行前后端分离解耦,前端跟后端不在同一个项目中,后端不需要负责前端页面的渲染,只需要接收前端请求然后响应数据即可。而这前后端交互的过程中后端通常都是把数据结果集封装成json对象返回给前端。这个过程可以通过在Controller中使用@RestController实现。

@RestController
@CrossOrigin
@RequestMapping("/queryMagnet")
public class MagnetController {
@Autowired
private MagnetService service;

//查询所有磁力链接
@RequestMapping(value = "/all",method = RequestMethod.GET)
private ResponseResult queryAllMagnet(){
List<Magnet> magnetList=service.queryAllMagnet();
return ResponseResult.ok(magnetList);
}
}

Postman请求结果

跨域请求的处理方法

如果是前端项目通过浏览器发送的请求要注意跨域的问题,这时后端要在controller上添加@CrossOrigin或通过@Configuration配置类的方式解决。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//跨域设置
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOrigins("*")
// 是否允许证书
.allowCredentials(true)
// 设置允许的方法
.allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH")
// 设置允许的header属性
.allowedHeaders("*")
// 跨域允许时间
.maxAge(3600);
}
}

全局异常处理

这个内容前几天已经发布过了,链接https://kbdog.github.io/2021/04/04/springboot-1/

拦截器的使用

拦截器的实现思想是AOP,由Spring官方文档可以知道拦截器要实现HandlerInterceptor接口。HandlerInterceptor接口中主要有三个方法postHandleafterCompletion、和preHandle。分别对应于Controller运行之后、完整的请求之后、实际处理的Controller之前。因此,preHandle是其中最主要的方法,该方法返回值为boolean,若返回true,则拦截器放行,请求继续执行,若为false则相反。通常拦截器应用在路径验证、权限验证、日志打印等方面。以下是其实现代码:

@Component
public class ApiInterceptor implements HandlerInterceptor {
//在controller前调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("经过拦截器...");
System.out.println("请求链接:"+request.getRequestURL());
return true;
}

//在controller后调用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle:访问controller完毕");
}

//整个过程结束后调用
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion:整个过程完毕");
}
}

拦截器写好后还要将其添加到配置类/文件中

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//拦截器设置
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ApiInterceptor()).addPathPatterns("/**");
}
}