【Spring面试全家桶】SpringMVC处理一个请求的流程是怎样的?
  2QrAa7u9TTys 2023年11月02日 25 0

(文章目录)

概念和组件

在Spring MVC的请求处理流程中,有几个重要的概念和组件需要了解:

  1. DispatcherServlet:它是Spring MVC的入口,接收并处理所有的请求。DispatcherServlet负责查找处理请求的Handler、视图解析、渲染视图等工作,它的配置信息保存在Web.xml文件中。

  2. HandlerMapping:它负责将请求映射到具体的Handler上,并返回一个HandlerExecutionChain对象,该对象包含了所有对该请求进行预处理和后处理的拦截器。Spring MVC提供了多种HandlerMapping实现类,如RequestMappingHandlerMapping、SimpleUrlHandlerMapping等。

  3. HandlerAdapter:它负责将请求转发给Handler进行业务逻辑处理。Spring MVC提供了多种HandlerAdapter实现类,如RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter等。

  4. Handler:它是业务逻辑的核心,负责处理请求、处理业务逻辑、生成ModelAndView对象等。Handler可以是Controller、@ControllerAdvice、RestController等。

  5. ModelAndView:它包含了视图名称、数据模型等信息。Handler处理完请求后,将结果封装成ModelAndView对象返回。

  6. ViewResolver:它负责将视图名称解析成具体的视图对象。Spring MVC提供了多种ViewResolver实现类,如InternalResourceViewResolver、FreeMarkerViewResolver等。

  7. View:它负责将数据模型渲染成具体的视图,如JSP、HTML、JSON等。

  8. Interceptor:它是Spring MVC的拦截器,在Handler执行之前或之后进行预处理或后处理。Spring MVC提供了多种Interceptor实现类,如HandlerInterceptor、WebRequestInterceptor等。

  9. ExceptionHandler:它用于统一处理请求发生的异常。Spring MVC提供了多种ExceptionHandler实现类,如SimpleMappingExceptionResolver、ResponseStatusExceptionResolver等。

除了以上概念和组件,还有一些其他的机制和扩展点,如数据绑定、表单验证、文件上传、RESTful接口等,这些都是Spring MVC的特性和扩展点,可以通过配置文件或注解进行启用和定制。

总的来说,Spring MVC的请求处理流程非常复杂和灵活,它充分利用了Spring的IoC和AOP功能,提供了很多方便和优雅的方式进行请求处理和管理。熟练掌握Spring MVC的原理和机制对于Java Web开发人员来说非常重要。

步骤

Spring MVC的请求处理流程可以分为以下几个步骤:

  1. 客户端发送一个请求。

  2. DispatcherServlet作为前端控制器接收到请求后,根据请求路径查找HandlerMapping,找到对应的Handler。

  3. HandlerAdapter将请求转发给Handler进行业务逻辑处理。

  4. Handler处理完后返回一个ModelAndView对象,其中包含将要返回的数据模型和视图名称。

  5. DispatcherServlet将ModelAndView对象交给ViewResolver进行视图解析。

  6. ViewResolver根据视图名称找到对应的View,并将数据模型传递给View。

  7. View负责渲染视图,并将渲染结果返回给DispatcherServlet。

  8. DispatcherServlet将渲染结果响应给客户端。

整个请求处理流程中,从DispatcherServlet到Handler再到View的调用都是通过Spring的IoC容器完成的。

细节和补充

除了以上提到的步骤,Spring MVC的请求处理流程还有一些细节和补充:

  1. 在 Handler 处理请求前,还可以使用 Spring MVC 中的拦截器进行请求预处理,例如权限验证、日志记录等。拦截器可以对请求进行处理或直接返回响应。

  2. 在 Handler 返回 ModelAndView 对象之后,还可以使用 Spring MVC 中的拦截器进行请求后处理,例如添加数据或修改 Model 等。拦截器的执行顺序可以通过配置文件或注解进行调整。

  3. 在 View 渲染视图时,还可以使用 Spring MVC 中的视图解析器对视图进行后处理,例如添加公共头、尾或全局变量等。视图解析器可以根据视图名称的后缀或其他标识符进行选择。

  4. 在整个请求处理流程中,还可以使用 Spring MVC 中的异常处理器对发生的异常进行统一处理,例如记录日志、返回错误页面等。异常处理器可以根据异常类型进行选择,并且可以设置不同的处理方式。

  5. Spring MVC 还提供了许多其他的特性和扩展点,例如数据绑定、表单验证、文件上传、RESTful 接口等。这些特性和扩展点都可以通过配置文件或注解进行启用和定制。

总之,Spring MVC 的请求处理流程是一个非常复杂和灵活的系统,它充分利用了 Spring 的 IoC 和 AOP 功能,提供了很多方便和优雅的方式进行请求处理和管理。掌握 Spring MVC 的原理和机制对于 Java Web 开发人员来说是非常重要的。

底层工作原理

SpringMVC是一个基于MVC(Model-View-Controller)设计模式的Web框架,它负责处理客户端请求并将响应发送回客户端。SpringMVC提供了一系列的组件和框架,使得Web应用程序的开发变得更加简单和高效。

在SpringMVC中,请求处理的底层工作原理主要涉及到以下几个方面:

  1. DispatcherServlet

DispatcherServlet是SpringMVC的核心组件,它负责接收所有的客户端请求,并将请求转发给相应的处理器进行处理。DispatcherServlet可以理解为是Web应用程序的入口,它会根据请求的URL路径和HTTP请求类型(GET、POST等)来查找对应的处理器。

在SpringMVC中,DispatcherServlet的初始化工作主要由WebApplicationContext完成,WebApplicationContext主要负责管理和维护SpringMVC中的所有bean以及其他配置信息。当DispatcherServlet接收到客户端请求时,它会从WebApplicationContext中获取对应的处理器并将请求转发给处理器进行处理。

  1. HandlerMapping

HandlerMapping是一个映射器,它主要负责将客户端请求映射到对应的处理器上。在SpringMVC中,HandlerMapping可以根据请求的URL路径和HTTP请求类型来查找对应的处理器。HandlerMapping通常是通过Java配置或者XML配置来进行注册的。

在SpringMVC中,HandlerMapping通常有多个实现类,其中比较常用的有RequestMappingHandlerMapping和SimpleUrlHandlerMapping。RequestMappingHandlerMapping可以根据注解@RequestMapping和@Controller来进行映射,而SimpleUrlHandlerMapping则可以根据URL路径来进行映射。

  1. HandlerAdapter

HandlerAdapter是一个适配器,它主要负责将请求转换成处理器可以处理的参数形式。在SpringMVC中,处理器的参数形式不一定与HTTP请求的参数形式相同,因此需要使用HandlerAdapter将请求参数转换为处理器可以处理的参数形式。

在SpringMVC中,HandlerAdapter可以根据请求的类型来选择对应的适配器实现。比如,如果请求是一个HTTP GET请求,那么就选择RequestMethod.GET适配器实现来转换参数;如果请求是一个HTTP POST请求,那么就选择RequestMethod.POST适配器实现来转换参数。

  1. HandlerInterceptor

HandlerInterceptor是一个拦截器,它主要负责在请求处理之前和请求处理之后对请求进行拦截和处理。在SpringMVC中,HandlerInterceptor可以根据请求的类型和URL路径来进行拦截和处理。

在SpringMVC中,HandlerInterceptor的主要作用是对请求进行安全验证、登录拦截、日志记录等处理。通常情况下,HandlerInterceptor会在请求处理之前进行拦截处理,如果拦截器返回false,则请求处理将被终止。

  1. ViewResolver

ViewResolver是一个视图解析器,它主要负责将处理器返回的模型数据和视图模板进行组装,并将组装后的视图发送给客户端。在SpringMVC中,视图可以通过JSP、HTML、JSON、XML等多种形式来进行表示。

在SpringMVC中,ViewResolver通常有多个实现类,其中比较常用的有InternalResourceViewResolver和JsonViewResolver。InternalResourceViewResolver可以将处理器返回的模型数据和JSP视图模板进行组装,而JsonViewResolver则可以将处理器返回的模型数据和JSON视图进行组装。

  1. 请求处理流程

1)DispatcherServlet接收请求,根据请求的URL路径和HTTP请求类型查找对应的HandlerMapping。

DispatcherServlet是SpringMVC框架的核心控制器,在web.xml配置文件中定义了DispatcherServlet的映射路径,所有的请求都会交给DispatcherServlet进行处理。当DispatcherServlet接收到请求后,首先要根据请求的URL路径和HTTP请求类型查找对应的HandlerMapping,HandlerMapping可以将请求映射到对应的处理器上。

在DispatcherServlet源码中,可以看到以下实现:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //获取处理器
    HandlerExecutionChain mappedHandler = getHandler(request);
    //...
}

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //获取处理器映射器
    HandlerMapping handlerMapping = getHandlerMapping(request);
    if (handlerMapping == null) {
        return null;
    }
    //获取处理器
    return handlerMapping.getHandler(request);
}

protected HandlerMapping getHandlerMapping(HttpServletRequest request) {
    //获取所有的处理器映射器
    List<HandlerMapping> handlerMappings = this.handlerMappings;
    if (handlerMappings != null) {
        //循环遍历所有的处理器映射器
        for (HandlerMapping mapping : handlerMappings) {
            try {
                //根据请求的URL路径和HTTP请求类型查找对应的处理器映射器
                HandlerExecutionChain handler = mapping.getHandler(request);
                //如果能够找到对应的处理器映射器,则返回
                if (handler != null) {
                    return mapping;
                }
            } catch (Exception ex) {
                //...
            }
        }
    }
    return null;
}

在以上代码中,可以看到DispatcherServlet接收请求后,首先会通过getHandlerMapping方法获取所有的处理器映射器,然后循环遍历所有的处理器映射器,根据请求的URL路径和HTTP请求类型查找对应的处理器映射器,如果能够找到对应的处理器映射器,则返回。

2)HandlerMapping将请求映射到对应的处理器上,并返回处理器对象。

HandlerMapping是处理器映射器的接口,处理器映射器是把请求和处理器建立映射关系的类。在SpringMVC中,一般有以下几种处理器映射器:

  • BeanNameUrlHandlerMapping:根据请求的URL路径和Handler的Bean名称进行映射。
  • SimpleUrlHandlerMapping:根据请求的URL路径和Handler的映射定义进行映射。
  • ControllerClassNameHandlerMapping:根据请求的URL路径和Controller的类名称进行映射。
  • AnnotationMethodHandlerAdapter:根据注解定义的URL路径和方法名称进行映射。

在HandlerMapping的实现类中,一般会重写getHandler方法,该方法会根据请求的URL路径和HTTP请求类型查找对应的Handler,并返回处理器对象。

以BeanNameUrlHandlerMapping为例,可以看到以下实现:

public class BeanNameUrlHandlerMapping extends AbstractUrlHandlerMapping {
    protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
        //获取请求的URL路径
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request, REQUEST_ATTRIBUTE_PATH);
        //根据URL路径查找处理器
        return lookupHandler(lookupPath, request);
    }
    
    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
        //根据URL路径查找处理器的Bean名称
        String beanName = lookupHandlerMapping(urlPath, request);
        if (beanName == null) {
            return null;
        }
        //根据Bean名称获取处理器对象
        return obtainApplicationContext().getBean(beanName);
    }
}

在以上代码中,可以看到BeanNameUrlHandlerMapping重写了getHandlerInternal方法,在该方法中会获取请求的URL路径,然后调用lookupHandler方法根据URL路径查找处理器的Bean名称,最后根据Bean名称获取处理器对象并返回。

3)HandlerAdapter将请求参数转换为处理器可以处理的参数形式,并调用处理器的处理方法。

HandlerAdapter是一个适配器,它的作用是将请求参数转换为处理器可以处理的参数形式,并调用处理器的处理方法。在SpringMVC中,有以下几种HandlerAdapter:

  • SimpleControllerHandlerAdapter:适配器用来适配Controller接口。
  • HttpRequestHandlerAdapter:适配器用来适配HttpRequestHandler接口。
  • AnnotationMethodHandlerAdapter:适配器用来适配使用注解方式的处理器。

以AnnotationMethodHandlerAdapter为例,可以看到以下实现:

public class AnnotationMethodHandlerAdapter extends AbstractHandlerMethodAdapter {
    public boolean supports(Object handler) {
        //判断处理器是否为带有@RequestMapping注解的方法
        return (handler instanceof HandlerMethod &&
                ((HandlerMethod) handler).hasMethodAnnotation(RequestMapping.class));
    }
    
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取HandlerMethod对象
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //获取请求参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        //创建调用参数列表
        Object[] args = handlerMethod.getMethodArgumentValues(request, response, parameterMap);
        //调用处理器方法
        Object returnValue = handlerMethod.invokeForRequest(request, response, args);
        //...
        return mav;
    }
}

在以上代码中,可以看到AnnotationMethodHandlerAdapter实现了supports和handle方法,supports方法用来判断处理器是否为带有@RequestMapping注解的方法,handle方法则用来处理请求,其中会先获取HandlerMethod对象,然后获取请求参数,创建调用参数列表,调用处理器方法,最后将处理器返回的模型数据和视图进行组装,并返回最终的视图。

4)处理器处理请求并返回处理结果(模型数据)。

处理器是真正处理请求的类,它会接收到请求后进行处理,并返回处理结果,通常处理的结果为模型数据。在SpringMVC中,处理器可以是一个Controller、一个普通Java对象或者一个方法。

以Controller为例,可以看到以下实现:

@Controller
public class HomeController {
    @RequestMapping("/")
    public String index() {
        return "index";
    }
}

在以上代码中,HomeController是一个Controller,其中的index方法用来处理请求,并返回一个字符串,该字符串为视图的名称,在后续的流程中会将该视图的名称和模型数据进行组装生成最终的视图。

5)ViewResolver将处理器返回的模型数据和视图模板进行组装,并返回最终的视图。

ViewResolver是视图解析器的接口,视图解析器的作用是将处理器返回的模型数据和视图模板进行组装,生成最终的视图。在SpringMVC中,有以下几种视图解析器:

  • InternalResourceViewResolver:使用JSP作为视图模板。
  • FreeMarkerViewResolver:使用FreeMarker作为视图模板。
  • VelocityViewResolver:使用Velocity作为视图模板。
  • ThymeleafViewResolver:使用Thymeleaf作为视图模板。

以InternalResourceViewResolver为例,可以看到以下实现:

public class InternalResourceViewResolver extends UrlBasedViewResolver {
    protected View createView(String viewName, Locale locale) throws Exception {
        //获取视图的URL
        String url = getPrefix() + viewName + getSuffix();
        //创建InternalResourceView对象
        InternalResourceView view = new InternalResourceView(url);
        //设置视图的请求上下文属性
        view.setRequestContextAttribute(getRequestContextAttribute());
        //...
        return view;
    }
}

在以上代码中,可以看到InternalResourceViewResolver重写了createView方法,在该方法中会根据视图名称生成视图的URL,并创建InternalResourceView对象,最终返回该视图对象。

6)DispatcherServlet将最终的视图发送给客户端。

在SpringMVC中,DispatcherServlet负责将视图发送给客户端,以完成请求处理的整个流程。DispatcherServlet会使用HttpServletResponse对象的getOutputStream()方法获取一个输出流,并将渲染后的视图内容写入流中。最后,DispatcherServlet会调用HttpServletResponse对象的flush()方法来刷新该流并发送视图内容到客户端。

以下是Java代码示例:

@Controller
public class MyController {
    
    @RequestMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "Hello World!");
        return "hello"; // 返回逻辑视图名
    }
}

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example")
public class AppConfig implements WebMvcConfigurer {

    // 配置视图解析器
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
    
    // 配置DispatcherServlet
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }
    
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", dispatcherServlet());
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}

// Web应用的入口
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] {AppConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }
}

上述示例中,MyController定义了一个处理/hello请求的控制器方法,该方法向模型添加一个message属性,最后返回逻辑视图名hello

AppConfig中配置了视图解析器和DispatcherServlet,并将其注册到Servlet容器中。AppInitializer是Web应用的入口类,它继承了AbstractAnnotationConfigDispatcherServletInitializer类,用于注册DispatcherServlet和相关配置。

7)如果需要进行拦截处理,HandlerInterceptor会在请求处理之前和请求处理之后对请求进行拦截和处理。

HandlerInterceptor是SpringMVC框架中的一个拦截器组件,它可以在请求处理前、请求处理后以及请求处理完成后对请求进行拦截和处理。在SpringMVC中,可以通过注册拦截器来实现请求的拦截和处理,以实现诸如日志记录、权限控制、性能监控等功能。

代码示例:

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

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

public class MyInterceptor implements HandlerInterceptor {

    // 在请求处理前进行拦截和处理
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 在此处编写拦截处理的业务逻辑
        return true; // 返回true表示继续执行请求处理,返回false表示中断请求处理
    }

    // 在请求处理后进行拦截和处理
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // 在此处编写拦截处理的业务逻辑
    }

    // 在请求处理完成后进行拦截和处理
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // 在此处编写拦截处理的业务逻辑
    }

}

以上代码实现了HandlerInterceptor接口,并覆盖了其中三个方法,分别对应请求处理前、请求处理后和请求处理完成后的拦截和处理逻辑。在每个方法内部,可以编写拦截处理的具体业务逻辑。其中,preHandle方法返回true表示继续执行请求处理,返回false表示中断请求处理。在使用时,需要将该拦截器组件注册到SpringMVC框架中。

实战中SpringMVC处理一个请求的问题与解决方案

SpringMVC是一个MVC框架,它将请求映射到控制器方法,并将结果渲染为视图。在处理一个请求时,可能会遇到以下问题:

  1. 请求无法映射到控制器方法:这可能是因为请求路径不正确或没有正确的请求参数。可以检查@RequestMapping注解和请求参数的匹配情况。

  2. 控制器方法返回不正确的结果:控制器方法应该返回一个ModelAndView对象或者一个String类型的视图名。如果返回值类型不正确,可以检查请求路径和控制器方法的注解。

  3. 视图无法正确渲染:这可能是因为视图文件不存在或者视图文件中有错误。可以检查视图的路径和文件名。

解决方案通常包括以下步骤:

  1. 确认请求路径、请求参数和控制器方法的注解匹配。

  2. 确认控制器方法返回值类型正确。

  3. 检查视图文件路径、文件名和内容。

  4. 如果以上步骤都正常,可以通过调试和日志记录来定位问题。

这是一个理解和解决SpringMVC中常见问题的说明,下面给出Java代码示例:

  1. 请求无法映射到控制器方法:
@Controller
public class UserController {

    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
    public ModelAndView getUser(@PathVariable("id") int id) {
        // ...
    }
}

在上述代码中,@RequestMapping注解中的value值为"/user/{id}",其中{id}为路径变量,可以通过@PathVariable注解获取。如果请求路径不为"/user/{id}",则无法映射到该控制器方法。

  1. 控制器方法返回不正确的结果:
@Controller
public class UserController {

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public User getUser() {
        return new User(1, "John");
    }
}

在上述代码中,控制器方法返回一个User对象,而不是一个ModelAndView对象或String类型的视图名。如果要返回视图,可以使用以下方式:

@Controller
public class UserController {

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String getUser(Model model) {
        User user = new User(1, "John");
        model.addAttribute("user", user);
        return "user";
    }
}

在上述代码中,使用了Model对象将User对象添加到模型中,并返回了一个名为"user"的视图。

  1. 视图无法正确渲染:
@Controller
public class UserController {

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String getUser(Model model) {
        User user = new User(1, "John");
        model.addAttribute("user", user);
        return "user-view";
    }
}

在上述代码中,返回的视图名为"user-view",如果该视图文件不存在或者视图文件中有错误,将无法正确渲染。可以检查视图文件路径、文件名和内容是否正确。

以上是解决SpringMVC中常见问题的一些步骤和示例代码。如果以上步骤都正常,可以通过调试和日志记录来定位问题。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

2QrAa7u9TTys