栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【SpringMVC】SpringMVC学习-2022详细学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【SpringMVC】SpringMVC学习-2022详细学习笔记

SpringMVC

这是我学习尚硅谷2022版ssm框架做的笔记
欢迎访问我的个人博客

相关文章
  • 【MyBatis】MyBatis框架学习-2022详细学习笔记
  • 【Spring】Spring框架学习-2022详细学习笔记
  • 【SpringMVC】SpringMVC学习-2022详细学习笔记
  • 【SSM整合】SSM整合-2022详细学习笔记
1 SpringMVC简介 1.1 MVC

是一种架构思想,不是框架或者设计模式

1.2 SpringMVC

是Spring为表述层开发提供的一整套完备的解决方案,封装的就是servlet。

三层架构,是指的表述层、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet

1.3 SpringMVC的特点
  • Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
  • 基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
  • 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
  • 代码清新简洁,大幅度提升开发效率
  • 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
  • 性能卓著,尤其适合现代大型、超大型互联网项目要求
2 入门案例 2.1 创建Maven工程
  1. 添加web模块

这里是以另外一个模块为例

  1. 打包方式设置为war
  2. 引入依赖
 
        
        
            org.springframework
            spring-webmvc
            5.3.1
        

        
        
        
            ch.qos.logback
            logback-classic
            1.2.3
        
        
        
            javax.servlet
            javax.servlet-api
            3.1.0
            provided
        
        
        
            org.thymeleaf
            thymeleaf-spring5
            3.0.12.RELEASE
        
    

注:由于 Maven 的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。

2.2 在web.xml中配置DispatcherServlet 2.2.1 配置匹配请求路径

匹配请求时:写/,匹配浏览器向服务器发送的所有请求,但是不包括以.jsp结尾的请求

而 ModelAndView mav = new ModelAndView(); //向请求域中设置数据 mav.addObject("testRequestScope","hello,ModelAndView"); //设置视图名称 mav.setViewName("success"); return mav; } 5.2 通过Model向请求域共享数据

注意:5.2 5.3 5.4 这三个选择一个用就可以,一般选择Model

将Model作为方法的形参,之后设置域共享数据即可

@RequestMapping("/test/model")
public String testModel(Model model){
    System.out.println(model.getClass().getName());
    //org.springframework.validation.support.BindingAwareModelMap
    model.addAttribute("testRequestScope","hello,Model");
    return "success";
}
5.3 通过ModelMap向请求域共享数据
@RequestMapping("/test/modelMap")
public String testModel(ModelMap modelMap){
    System.out.println(modelMap.getClass().getName());
    //org.springframework.validation.support.BindingAwareModelMap
    modelMap.addAttribute("testRequestScope","hello,ModelMap");
    return "success";
}
5.4 通过Map向请求域共享数据
@RequestMapping("/test/map")
public String testMap(Map map){
    System.out.println(map.getClass().getName());
    //org.springframework.validation.support.BindingAwareModelMap
    map.put("testRequestScope","hello,Map");
    return "success";
}
5.5 Model和ModelMap和Map的关系

其实在底层中,这些类型的形参最终都是通过BindingAwareModelMap创建的

几个类的关系:

public class BindingAwareModelMap extends ExtendedModelMap {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class ModelMap extends LinkedHashMap {}

最终都是返回一个ModelAndView实例

5.6 共享session域和application域中的数据

使用servletAPI即可

@RequestMapping("/test/session")
public String testSession(HttpSession session){
    session.setAttribute("testSessionScope","hello,session");
    return "success";
}

@RequestMapping("/test/application")
public String testApplication(HttpSession session){
    ServletContext application = session.getServletContext();
    application.setAttribute("testApplicationScope","hello,application");
    return "success";
}
6 SpringMVC视图

SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户

SpringMVC视图的种类很多,默认有转发视图InternalResourceView和重定向视图RedirectView

当工程引入jstl的依赖,转发视图会自动转换为JstlView

若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView

6.1 Thymeleaf视图

当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转

@RequestMapping("/testHello")
public String testHello(){
    return "hello";
}

6.2 转发视图

转发和重定向:

  1. 转发浏览器发送一次请求,在服务器中处理一次,重定向浏览器发送两次请求。
  2. 转发由于只发送一次请求,地址栏地址不变。重定向改变
  3. 转发由于只发送一次request请求,可以使用request共享域中的数据,而重定向不可

SpringMVC中默认的转发视图是InternalResourceView

SpringMVC中创建转发视图的情况:

当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转。

例如"forward:/",“forward:/employee”。

@RequestMapping("/testForward")
public String testForward(){
    return "forward:/testHello";//跳转到路径为/testHello的控制器方法中,之后再执行这个方法
}

6.3 重定向视图

SpringMVC中默认的重定向视图是RedirectView

当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转

例如"redirect:/",“redirect:/employee”

@RequestMapping("/testRedirect")
public String testRedirect(){
    return "redirect:/testHello";
}

6.4 视图控制器

当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用viewcontroller标签进行表示。

注意:
当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需
要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:

7 RESTful 7.1 RESTful简介

将服务器看做由很多资源组成,一切皆资源。对于同一个资源,要求其访问路径是一样的,不论是增删改查,路径都是一样。

但是如何区别增删改查呢?使用请求方式修改。

下面是比较详细的介绍

7.2 RESTful的实现

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETe。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。

REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。

以操作用户对象为例:

@Controller
public class TestRestController {

//    @RequestMapping(value = "/user",method = RequestMethod.GET)
    @GetMapping("/user")
    public String getAllUser(){
        System.out.println("查询所有的用户信息-->/user-->get");
        return "success";
    }

//    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    @GetMapping("/user/{id}")
    public String getUserById(@PathVariable("id")Integer id){
        System.out.println("根据id查询用户信息-->/user/"+id+"-->get");
        return "success";
    }


//    @RequestMapping(value = "/user",method = RequestMethod.POST)
    @PostMapping("/user")
    public String insertUser(){
        System.out.println("添加用户信息-->/user-->post");
        return "success";
    }

//    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    @PutMapping("/user")
    public String updateUser(){
        System.out.println("修改用户信息-->/user-->put");
        return "success";
    }

//    @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
    @DeleteMapping("/user/{id}")
    public String updateUser(@PathVariable("id") Integer id){
        System.out.println("删除用户信息-->/user/"+id+"-->delete");
        return "success";
    }
}
7.3 处理PUT和DELETE请求

浏览器目前只能发送get和post请求

若要发送put和delete请求,需要在web.xml中配置一个过滤器HiddenHttpMethodFilter,配置了过滤器之后,发送的请求要满足两个条件,才能将请求方式转换为put和delete

1、当前请求的请求方式必须为post

2、当前请求必须传输请求参数_method,_method的值才是最终的请求方式

源码分析

HiddenHttpMethodFilter:

请求方式必须是post,获取参数_method的值

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

   HttpServletRequest requestToUse = request;

    //如果当前请求是post且没有异常就进入
   if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
       //得到_method参数的值,然后再根据值指定的方法创建一个对应方法的request
      String paramValue = request.getParameter(this.methodParam);
       //判断是否有值
      if (StringUtils.hasLength(paramValue)) {
         String method = paramValue.toUpperCase(Locale.ENGLISH);
         if (ALLOWED_METHODS.contains(method)) {
            requestToUse = new HttpMethodRequestWrapper(request, method);
         }
      }
   }

   filterChain.doFilter(requestToUse, response);
}


//这个内部类,传入request和method,重写了getMethod方法,之后获取method都是PUT或者DELETE
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {

   private final String method;

   public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
      super(request);
      this.method = method;
   }

   @Override
   public String getMethod() {
      return this.method;
   }
}
8 RESTful案例

这一部分就是实现一个增删改查,使用RESTful风格实现

8.1 功能
  • 查询所有的员工信息 -->/employee–>get
  • 跳转到添加页面 -->/to/add–>get
  • 新增员工信息 -->/employee–>post
  • 跳转到修改页面 -->/employee/1–>get
  • 修改员工信息 -->/employee–>put
  • 删除员工信息 -->/employee/1–>delete
8.2 处理静态资源 由css文件引入

在员工列表的页面引入css文件


DispatcherServlet会拦截除了jsp之外的所有请求,当请求静态资源时,该请求也会被DispatcherServlet处理,然而他处理不了,需要交给Tomcat处理。

DispatcherServlet和Tomcat的DefaultServlet

当前工程的web.xml配置的前端控制器DispatcherServlet的url-pattern是/

而Tomcat的web.xml配置的默认的控制器DefaultServlet的url-pattern也是/

此时,浏览器发送的请求优先会被DispatcherServlet进行处理,但是DispatcherServlet无法处理静态资源。因此需要把静态资源的处理交给默认的servlet,进行如下配置:

若配置了,此时浏览器发送的所有请求都会被DefaultServlet处理,DispatcherServlet无法进行处理

因此还需要开启mvc的注解驱动,这样浏览器发送的请求会先被DispatcherServlet处理,无法处理再交给DefaultServlet处理

如果Tomcat中的web.xml的配置和工程中的web.xml配置冲突,会以当前工程的配置为准。

8.3 功能实现

这一部分没啥的,正常的crud

控制器:

@Controller
public class EmployeeController {

    @Autowired
    private EmployeeDao employeeDao;

    @GetMapping("/employee")
    public String getAllEmployee(Model model){
        //获取所有的员工信息
        Collection employeeList = employeeDao.getAll();
        //将所有的员工信息在请求域中共享
        model.addAttribute("employeeList",employeeList);
        //跳转到列表页面
        return "employee_list";
    }

    @PostMapping("/employee")
    public String addEmployee(Employee employee){
        //保存员工信息
        employeeDao.save(employee);
        //访问列表功能,调用查询数据
        //重定向
        return "redirect:/employee";
    }

    @GetMapping("/employee/{id}")
    public String toUpdate(@PathVariable("id") Integer id,Model model){
        //根据id查询信息
        Employee employee = employeeDao.get(id);
        //将员工信息共享到请求域中
        model.addAttribute("employee",employee);
        //跳转
        return "employee_update";
    }

    @PutMapping("/employee")
    public String updateEmployee(Employee employee){
        //更新信息
        employeeDao.save(employee);
        //重定向
        return "redirect:/employee";
    }

    @DeleteMapping("/employee/{id}")
    public String deleteEmployeeById(@PathVariable("id") Integer id){
        employeeDao.delete(id);
        return "redirect:/employee";
    }

}

前端页面,就以员工列表页为例




    
    employee list
    


employee list
id lastName email gender options(add )
delete update
9 SpringMVC处理ajax请求

更详细的介绍见https://heavy_code_industry.gitee.io/code_heavy_industry/pro001-javaweb/lecture/chapter12/

9.1 ajax介绍藍 服务端渲染与ajax渲染

服务端渲染

ajax渲染(局部更新)

前后端分离

彻底舍弃服务器端渲染,数据全部通过Ajax方式以JSON格式来传递。

异步和同步

Ajax本身就是Asynchronous JavaScript And XML的缩写,直译为:异步的JavaScript和XML。在实际应用中Ajax指的是:不刷新浏览器窗口,不做页面跳转,局部更新页面内容的技术。

axios

发送请求的方式之一:(一般不使用这个,这里用这个详细介绍一下axios)

参数用params方式,参数拼接到地址栏中。参数用data方式,参数放在请求体中。

axios({
               url:"",//请求路径
               method:"",//请求方式
               //请求参数,以name=value&name=value的方式,
               //不管使用的请求方式是get或者post,请求参数都会被拼接到请求地址后
               // 此种方式可以通过request.getParameter()获取
               params:{},
               //请求参数,以json格式发送,
               // 请求参数会被保存到请求报文的请求体传输到服务器,因此不能使用get,get没有请求体
               // 需要获取请求体,之后把请求体中的json数据进行处理,会使用一些处理json的jar包处理
               data:{}
           }).then(res=>{
               console.log(res.data)
           })

发送请求的方式二:

axios.post(
    //url,要加上上下文路径
    "/SpringMVC/test/ajax?id=1001",
    //json类型的数据,会被封装在请求体中
    {username:"admin",password:"123456"}
).then(res=>{//请求成功的响应
    //服务器返回的结果封装到data属性中
    console.log(res.data)
})
9.2 @RequestBody注解

该注解将请求体中的内容和形参进行绑定。由于axios发送请求把请求参数放到了请求体中,所以需要这个注解来获取请求体中的内容。

testAjax(){
    axios.post(
        //url,要加上上下文路径
        "/SpringMVC/test/ajax?id=1001",
        //json类型的数据,会被封装在请求体中
        {username:"admin",password:"123456"}
    ).then(res=>{//请求成功的响应
        //服务器返回的结果封装到data属性中
        console.log(res.data)
    })
},
@RequestMapping("/test/ajax")
public void testAjax(Integer id, @RequestBody String requestBody, HttpServletResponse response) throws IOException {
    System.out.println("id:"+id);
    System.out.println("requestBody:"+requestBody);
    response.getWriter().write("hello.axios");
}

控制台打印:

id:1001
requestBody:{"username":"admin","password":"123456"}
9.3 @RequestBody注解将json格式的数据转为java对象

由于axios的请求体中是json格式,我们需要进行转换

步骤:

1、导入jackson的依赖

    com.fasterxml.jackson.core
    jackson-databind
    2.12.1

2、在SpringMVC配置文件中设置开启mvc的注解驱动

3、在控制器方法的形参前设置@RequestBody注解
@RequestMapping("/test/requestbody/json")
public void testRequestBody(@RequestBody User user, HttpServletResponse response) throws IOException {
    System.out.println(user);
    response.getWriter().write("hello,requestBody");
}
9.4 @ResponseBody注解

就是把所标识的方法的返回值作为响应报文的响应体响应到浏览器。

@RequestMapping("/test/responsebody")
//这个注解就是把方法的返回值作为请求的返回体返回给浏览器
@ResponseBody
public String testResponseBody(){
    return "success";
}

这里直接将内容显示到浏览器中

9.5 @ResponseBody注解响应给浏览器json格式数据 java --> json

如何将java对象响应过去?如果直接如果直接response.getWriter().write,加入java对象,是返回的内存地址或者返回了一个toString方法的返回值,再者,如果将java类型的对象返回了,那么浏览器也不能处理java类型的数据。

一般都是奖java转换为json值返回

步骤:(和上面的差不多)
  1. 导入jackson的依赖
  2. 在SpringMVC配置文件中设置开启mvc的注解驱动
  3. 将需要转换为json字符串的Java对象作为控制器方法的返回值,使用@ResponseBody注解标识控制器方法就可以将java对象直接转化为json字符串,并响应到浏览器

非常方便,省略了将返回的java对象转换为json的过程,由框架完成

//非常方便,省略了将返回的java对象转换为json的过程,由框架完成
@RequestMapping("/test/responsebody/json")
@ResponseBody
public User testResponseBodyJson(){
    User user = new User(1001,"admin","23232",20,"男");
    return user;
}

转换关系

常用的java对象转化为json的结果:

  • 实体类–>json对象
  • map–>json对象
  • List–>json数组

9.6 @RestController注解

@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了
@Controller注解,并且为其中的每个方法添加了@ResponseBody注解

10 文件上传和下载 10.1 文件下载

ResponseEntity:可以作为控制器方法的返回值,表示响应到浏览器的完整的响应报文

如果不设置返回,返回值为void,那么会默认将请求地址作为逻辑视图

下面这个,一般当成一个模板用就可以了

    @RequestMapping("/test/down")
    public ResponseEntity testResponseEntity(HttpSession session) throws
            IOException {
//获取ServletContext对象
        ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径,其实就是直接把这个路径拼接到项目的后面
        String realPath = servletContext.getRealPath("img");
        //E:code_workspacestudy_SSMspring_mvc04_ajaxtargetspring_mvc04_ajax-1.0-SNAPSHOT
        realPath = realPath + File.separator+"avatar.jpg";
//创建输入流
        InputStream is = new FileInputStream(realPath);
//创建字节数组,is.available()获取输入流所对应的文件的字节数。
//        对于流,可以设置响应的读取策略,可以适量多次读取
        byte[] bytes = new byte[is.available()];
//将流读到字节数组中
        is.read(bytes);
//创建HttpHeaders对象设置响应头信息
        MultiValueMap headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字,attachment:以附件的方式下载
        headers.add("Content-Disposition", "attachment;filename=avatar.jpg");
//设置响应状态码
        HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
        ResponseEntity responseEntity = new ResponseEntity<>(bytes, headers,
                statusCode);
//关闭输入流
        is.close();
        return responseEntity;
    }
10.2 文件上传

要求:

  1. form表单的请求方式必须是post
  2. form表单必须设置属性enctype=“multipart/form-data”,保证以二进制方式上传

步骤:

  1. 添加依赖

    
    
        commons-fileupload
        commons-fileupload
        1.3.1
    
    
  2. 配置bean

    注意设置id,id名固定

    
    
        
    
    
  3. 控制器方法

    注意点:使用MultipartFile类型参数接收,注意把他当成一个普通参数就可,需要匹配请求的参数

    @RequestMapping("/test/up")
    //注意:这里的MultipartFile不是由框架自动填充(就像request和session那样赋值),
    // 而是像普通参数一样从请求参数中获取
    public String testUp(@RequestParam("photo") MultipartFile photo, HttpSession session) throws IOException {
        //获取上传的文件的文件名
        String filename = photo.getOriginalFilename();
        //处理文件重名问题
        //获取上传的文件的后缀名
        String fileType = filename.substring(filename.lastIndexOf("."));
        filename = UUID.randomUUID().toString() + fileType;
        System.out.println(filename);
        //获取ServletContext对象
        ServletContext servletContext = session.getServletContext();
    
        //获取当前工程下photo目录的真实路径
        String photoPath = servletContext.getRealPath("photo");
    
        //创建photoPath所对应的File对象
        File file = new File(photoPath);
        //判断file对应的目录是否存在
        if(!file.exists()){
            file.mkdir();
        }
    
        String finalPath = photoPath + File.separator + filename;
        //文件上传
        photo.transferTo(new File(finalPath));
        return "success";
    }
    
10.3 文件上传的重命名问题

使用UUID命名,UUID加上文件后缀名即可。

//处理文件重名问题
//获取上传的文件的后缀名
String fileType = filename.substring(filename.lastIndexOf("."));
filename = UUID.randomUUID().toString() + fileType;
System.out.println(filename);

下面这些知识点用的不多,了解即可

11 拦截器

请求经过过滤器,过滤器放行之后DispatcherServlet处理请求,而拦截器用于拦截控制器方法的执行

11.1 拦截器的配置

SpringMVC中的拦截器用于拦截控制器方法的执行

SpringMVC中的拦截器需要实现HandlerInterceptor接口

SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:


		
	






    
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceRequestEncoding(true);
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
    }
}
2、创建SpringConfig配置类,代替spring的配置文件
@Configuration
public class SpringConfig {
	//ssm整合之后,spring的配置信息写在此类中
}
3、创建WebConfig配置类,代替SpringMVC的配置文件
//将当前类表示为一个配置类
@Configuration
//1、扫描组件
@ComponentScan("com.atguigu.mvc")
//5、mvc注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {


    //4、default-servlet-handler
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    //8、拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TestInterceptor())
                .addPathPatterns("
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
           	// 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
			
            // 调用拦截器的preHandle()
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler.
            // 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            // 调用拦截器的postHandle()
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 后续处理:处理模型数据和渲染视图
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                               new NestedServletException("Handler processing failed", err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}
d>processDispatchResult()
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {

    boolean errorView = false;

    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        // 处理模型数据和渲染视图
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
        // Exception (if any) is already handled..
        // 调用拦截器的afterCompletion()
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}
4、SpringMVC的执行流程
  1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。

  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:

a) 不存在

i. 再判断是否配置了mvc:default-servlet-handler

ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误

iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误

b) 存在则执行下面的流程

  1. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。

  2. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。

  3. 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】

  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

  1. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。

  2. 此时将开始执行拦截器的postHandle(…)方法【逆向】。

  3. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。

  4. 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。

  5. 将渲染结果返回给客户端。

感谢你的阅读
本篇文章是我学习尚硅谷2022版ssm框架做的笔记
欢迎访问我的个人博客

相关文章
  • 【MyBatis】MyBatis框架学习-2022详细学习笔记
  • 【Spring】Spring框架学习-2022详细学习笔记
  • 【SpringMVC】SpringMVC学习-2022详细学习笔记
  • 【SSM整合】SSM整合-2022详细学习笔记
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1035828.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号