spring mvc原理深度解析(二)

spring mvc拦截处理

HandlerExceptionResolver 异常处理

该组件用于表示出现异常时spring mvc 的处理方案。
dispatcherServlet 会调用org.springframework.web.servlet.DispatcherServlet#processHandlerException() 方法,遍历 handlerExceptionResolvers 处理异常,处理完成之后返回errorView 跳转到异常视图

演示自定义异常捕捉

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <!-- jstl依赖 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
  </dependencies>

自定义异常处理类,并实现HandlerExceptionResolver 接口

package com.yemuxia.mvc01.controller;

import com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

public class MyExceptionResolver implements HandlerExceptionResolver {
    
    
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    
    
        ModelAndView modelAndView = new ModelAndView("error");
        if(ex instanceof IllegalArgumentException){
    
    
            modelAndView.addObject("errorType","参数非法");
        }else{
    
    
            modelAndView.addObject("errorType",ex.getClass().getSimpleName());
        }
        modelAndView.addObject("message",ex.getMessage());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ex.printStackTrace(new PrintStream(out,true));
        modelAndView.addObject("stack",out.toString());
        return modelAndView;
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <bean name="/hello.do" class="com.yemuxia.mvc01.controller.SimpleController"/>
    <bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/" />
        <property name="suffix" value=".jsp" />
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    </bean>
    <bean class="com.yemuxia.mvc01.controller.MyExceptionResolver"/>
</beans>

error.jsp异常页面如下

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
默认异常-内部错误:<h1>500</h1>
<h3>${message}</h3>
<h5>异常类别:${errorType}</h5>
<pre><code>${stack}</code></pre>
</body>
</html>

package com.yemuxia.mvc01.controller;



import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class SimpleController implements Controller {
    
    

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
        ModelAndView mv = new ModelAndView("userView");
        mv.addObject("name","hhh");
        if(request.getParameter("name").length()<2){
    
    
            throw new IllegalArgumentException("名称不能少于2个字符");
        }
        int i= 5/0;
        return mv;
    }
}

异常演示如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

源码分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

HandlerExceptionResolver 结构

在这里插入图片描述
在这里插入图片描述

ResponseStatusExceptionResolver(默认):用于解析带@ResponseStatus的自定义异常
DefaultHandlerExceptionResolver(默认):spring mvc 默认异常处理。
SimpleMappingExceptionResolver:异常映射,将指定异常与错误页面相对应

SimpleMappingExceptionResolver案例

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <bean name="/hello.do" class="com.yemuxia.mvc01.controller.SimpleController"/>
    <bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/" />
        <property name="suffix" value=".jsp" />
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    </bean>
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="defaultErrorView" value="error"/>
        <property name="defaultStatusCode" value="500"/>
        <property name="exceptionMappings">
            <map>
                <entry key="java.lang.RuntimeException" value="error"/>
                <entry key="java.lang.IllegalArgumentException" value="argumentError"/>
            </map>
        </property>
    </bean>
</beans>
package com.yemuxia.mvc01.controller;



import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class SimpleController implements Controller {
    
    

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
        ModelAndView mv = new ModelAndView("userView");
        mv.addObject("name","hhh");
        if(request.getParameter("name").length()<2){
    
    
            throw new IllegalArgumentException("名称不能少于2个字符");
        }
        int i= 5/0;
        return mv;
    }
}

error.jsp和argumentError.jsp内容如下

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
默认异常-内部错误:<h1>500</h1>
<h3>${exception.message}</h3>
</body>
</html>

在这里插入图片描述
在这里插入图片描述

HandlerInterceptor调用拦截

HandlerInterceptor用于对请求拦截,与原生Filter区别在于 Filter只能在业务执行前拦截,而HandlerInterceptor 可以在业务处理前、中、后进行处理。

案例

拦截器需要配置拦截范围

<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.yemuxia.mvc01.controller.SimpleHandlerInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

在这里插入图片描述
自定义拦截器

package com.yemuxia.mvc01.controller;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleHandlerInterceptor implements HandlerInterceptor {
    
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        System.out.println("afterCompletion");
    }
}

运行项目,并调用接口
在这里插入图片描述
在这里插入图片描述
其实现机制是基于 HandlerExecutionChain 分别在 doDispatch 方法中执行以下方法:

  • preHandle :业务处理前执行
  • postHandle:业务处理后(异常则不执行)
  • afterCompletion:视图处理后

dispatchServlet 初始化流程

1.创建WebApplicationContext
2.基于策略模型加载各组件。
创建WebApplicationContext 源码解析

org.springframework.web.servlet.HttpServletBean#init
org.springframework.web.servlet.FrameworkServlet#initServletBean
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
// 基于当前存在的Spring 上下文做为Root 创建Mvc上下文。
org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.context.ApplicationContext)
org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext
org.springframework.context.support.AbstractApplicationContext#refresh

基于策略模型加载各组件源码解析

RequestMapping注解的使用与原理

案例

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.yemuxia.mvc01.controller"/>
    <mvc:annotation-driven/>
    <bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/" />
        <property name="suffix" value=".jsp" />
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    </bean>

</beans>
package com.yemuxia.mvc01.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
@RequestMapping("/my/")
public class UserController {
    
    
    @RequestMapping("hello")
    public void hello(HttpServletResponse response) throws IOException {
    
    
        response.getWriter().println("hello you shi ge hao ren");
    }
}

在这里插入图片描述

为什么基于 <mvc:annotation-driven/ > 配置就能实现mvc 的整个配置了

告知Spring,我们启用注解驱动,自动向ioc 里面注册了两个BeanDefinition。分别是:RequestMappingHandlerMapping与RequestMappingHandlerAdapter。Spring会自动为我们注册上面说到的几个Bean到工厂中,来处理我们的请求。
在这里插入图片描述

RequestMapping实现的原理

在这里插入图片描述

  • 1.RequestMappingHandlerMapping :URL 映射器
  • 2.RequestMappingHandlerAdapter:执行适配器
  • 3.InvocableHandlerMethod:Control目标对象,包含了control Bean 及对应的method 对像,及调用方法
    • a. HandlerMethodArgumentResolverComposite:参数处理器
    • b. ParameterNameDiscoverer:参数名称处理器
    • c. HandlerMethodReturnValueHandlerComposite:返回结构处理器

源码解析

查找mapping源码解析

// 基于注解查找 mapping
org.springframework.web.servlet.DispatcherServlet#getHandler
在这里插入图片描述
在这里插入图片描述

org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
在这里插入图片描述

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
在这里插入图片描述

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl
在这里插入图片描述

调用执行过程源码解析

org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
在这里插入图片描述

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
在这里插入图片描述

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
在这里插入图片描述

org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
在这里插入图片描述
在这里插入图片描述

org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/yemuxiaweiliang/article/details/120344345