详解SpringMVC从基础到源码

以下是关于“详解SpringMVC从基础到源码”的完整攻略,其中包含两个示例。

详解SpringMVC从基础到源码

SpringMVC是一个基于MVC模式的Web框架,它提供了一种灵活、高效的方式来开发Web应用程序。在本攻略中,我们将从基础概念到源码实现,全面讲解SpringMVC的工作原理和实现细节。

SpringMVC基础概念

MVC模式

MVC模式是一种软件设计模式,它将应用程序分为三个部分:模型、视图和控制器。模型表示应用程序的数据和业务逻辑,视图表示应用程序的用户界面,控制器负责协调模型和视图之间的交互。

在SpringMVC中,模型、视图和控制器分别对应以下组件:

  • 模型:业务逻辑组件和数据访问组件。
  • 视图:JSP、Thymeleaf等模板引擎。
  • 控制器:DispatcherServlet。

DispatcherServlet

DispatcherServlet是SpringMVC的核心组件,它负责接收所有的请求,并将请求分发给对应的控制器进行处理。在SpringMVC中,我们需要在web.xml文件中配置DispatcherServlet。

以下是一个web.xml文件的示例:

<web-app>
    <servlet>
        <servlet-name>Servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url>
    </servlet-mapping>
</web-app>

在本示例中,我们定义了一个名为dispatcherServlet的Servlet,并将其映射到根路径"/"。我们使用元素指定了DispatcherServlet的类名,并使用元素指定了DispatcherServlet的配置文件路径。

HandlerMapping

HandlerMapping是SpringMVC的组件之一,它负责将请求映射到对应的控制器进行处理。在SpringMVC中,我们可以使用多种方式来配置HandlerMapping,例如注解、XML配置等。

以下是一个使用注解配置HandlerMapping的示例:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class AppConfig implements WebMvcConfigurer {
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("/static/");
    }
}

在本示例中,我们使用@Configuration注解标识AppConfig类为配置类,并使用@EnableWebMvc注解启用SpringMVC。我们使用@ComponentScan注解指定了控制器所在的包路径。在configureDefaultServletHandling()方法中,我们启用了默认的Servlet处理。在addViewControllers()方法中,我们添加了一个视图控制器,将根路径"/"映射到index视图。在addResourceHandlers()方法中,我们添加了一个资源处理器,将/static/路径下的静态资源映射到/static/**路径。

HandlerAdapter

HandlerAdapter是SpringMVC的组件之一,它负责将请求分发给对应的控制器进行处理,并将处理结果封装成ModelAndView对象返回给DispatcherServlet。在SpringMVC中,我们可以使用多种方式来配置HandlerAdapter,例如注解、XML配置等。

以下是一个使用注解配置HandlerAdapter的示例:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class AppConfig implements WebMvcConfigurer {
    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
        adapter.setArgumentResolvers(Collections.singletonList(new RequestParamMethodArgumentResolver()));
        adapter.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
        return adapter;
    }
}

在本示例中,我们使用@Bean注解定义了一个名为requestMappingHandlerAdapter的HandlerAdapter,并使用setArgumentResolvers()方法和setMessageConverters()方法分别配置了请求参数解析器和消息转换器。

SpringMVC源码实现

DispatcherServlet源码实现

DispatcherServlet的源码实现位于org.springframework.web.servlet包中。以下是DispatcherServlet的核心方法doDispatch()的源码实现:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    ModelAndView mv = null;
    Exception dispatchException = null;

    try {
        processedRequest = checkMultipart(request);
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
        }

        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    }
    catch (Exception ex) {
        dispatchException = ex;
    }
    catch (Throwable err) {
        dispatchException = new NestedServletException("Handler dispatch failed", err);
    }

    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

在本方法中,我们首先调用checkMultipart()方法对请求进行处理,然后调用getHandler()方法获取对应的HandlerExecutionChain对象。如果获取不到HandlerExecutionChain对象,则调用noHandlerFound()方法进行处理。如果获取到HandlerExecutionChain对象,则调用getHandlerAdapter()方法获取对应的HandlerAdapter对象,并调用handle()方法进行处理。最后,我们调用processDispatchResult()方法对处理结果进行处理。

HandlerMapping源码实现

HandlerMapping的源码实现位于org.springframework.web.servlet包中。以下是RequestMappingHandlerMapping的源码实现:

public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
    @Override
    protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
        super.registerHandlerMethod(handler, method, mapping);
    }

    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
        }
        return info;
    }

    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestMapping requestMapping = AnnotationUtils.getAnnotation(element, RequestMapping.class);
        if (requestMapping != null) {
            return RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
                    .methods(requestMapping.method())
                    .params(requestMapping.params())
                    .headers(requestMapping.headers())
                    .consumes(requestMapping.consumes())
                    .produces(requestMapping.produces())
                    .mappingName(requestMapping.name())
                    .options(this.config)
                    .build();
        }
        return null;
    }
}

在本示例中,我们继承了AbstractHandlerMethodMapping类,并实现了registerHandlerMethod()方法和getMappingForMethod()方法。在registerHandlerMethod()方法中,我们调用了父类的registerHandlerMethod()方法。在getMappingForMethod()方法中,我们调用了createRequestMappingInfo()方法创建RequestMappingInfo对象,并使用combine()方法将RequestMappingInfo对象合并。

HandlerAdapter源码实现

HandlerAdapter的源码实现位于org.springframework.web.servlet包中。以下是RequestMappingHandlerAdapter的源码实现:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter<RequestMappingInfo> {
    @Override
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }

    @Override
    protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, handlerMethod);

        Object result = handlerMethod.invokeForRequest(webRequest, mavContainer);
        if (result == null) {
            if (isRequestNotModified(request, handlerMethod.getMethod())) {
                return null;
            }
            handleResponseBody(null, mavContainer, webRequest);
        }
        else if (StringUtils.hasText(mavContainer.getViewName())) {
            handleResponseBody(result, mavContainer, webRequest);
        }
        else {
            mavContainer.setRequestHandled(true);
        }

        return getModelAndView(mavContainer);
    }
}

在本示例中,我们继承了AbstractHandlerMethodAdapter类,并实现了supportsInternal()方法和handleInternal()方法。在supportsInternal()方法中,我们返回true表示支持所有的HandlerMethod对象。在handleInternal()方法中,我们首先创建了ServletWebRequest对象和ModelAndViewContainer对象,并调用initModel()方法初始化ModelAndViewContainer对象。然后,我们调用invokeForRequest()方法执行HandlerMethod对象,并获取处理结果。如果处理结果为null,则调用handleResponseBody()方法处理响应体。如果处理结果不为null且ModelAndViewContainer对象中包含视图名称,则调用handleResponseBody()方法处理响应体。否则,我们将ModelAndViewContainer对象的requestHandled属性设置为true。最后,我们调用getModelAndView()方法获取ModelAndView对象并返回。

示例1

以下是一个使用SpringMVC的示例:

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public ModelAndView list() {
        List<User> userList = userService.getUserList();
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("userList", userList);
        modelAndView.setViewName("userList");
        return modelAndView;
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public ModelAndView detail(@PathVariable("id") Long userId) {
        User user = userService.getUserById(userId);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("user", user);
        modelAndView.setViewName("userDetail");
        return modelAndView;
    }
}

在本示例中,我们使用@Controller注解标识UserController类为控制器,并使用@RequestMapping注解指定了请求路径、请求方法、请求参数等信息。在list()方法中,我们调用了UserService的getUserList()方法获取用户列表,并将用户列表添加到ModelAndView对象中。在detail()方法中,我们调用了UserService的getUserById()方法获取指定ID的用户,并将用户信息添加到ModelAndView对象中。

示例2

以下是一个使用Thymeleaf模板引擎的示例:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>User Detail</title>
</head>
<body>
    <h1>User Detail</h1>
    <table>
        <tr>
            <td>ID:</td>
            <td th:text="${user.id}"></td>
        </tr>
        <tr>
            <td>Name:</td>
            <td th:text="${user.name}"></td>
        </tr>
        <tr>
            <td>Age:</td>
            <td th:text="${user.age}"></td>
        </tr>
    </table>
</body>
</html>

在本示例中,我们使用Thymeleaf模板引擎来渲染视图。我们使用th:text指令来显示用户的ID、姓名和年龄。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解SpringMVC从基础到源码 - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • IDEA项目如何实现打jar包

    下面就详细讲解一下如何在 IDEA 中将项目导出为 jar 包的完整攻略。 第一步:导入项目 首先需要将开发好的项目导入到 IDEA 中,可以直接使用 File → Open Project,或者使用 Import Project 选项,等待 IDEA 自动加载项目。 第二步:配置项目信息 接下来需要配置项目信息,将项目打包并导出。 配置 pom.xml 文…

    Java 2023年5月26日
    00
  • JAVA大作业之图书管理系统实现全解

    JAVA大作业之图书管理系统实现全解攻略 一、需求分析 在进行任何项目之前,首先需要明确项目需求,即明确项目所需要实现的功能。图书管理系统需要包括以下基本功能:1. 图书的录入、修改、删除和查询2. 读者的录入、修改、删除和查询3. 借阅、归还和续借图书4. 生成借阅记录和逾期记录5. 管理员的登陆和注销 二、技术选型 对于图书管理系统的开发,需要选择适合的…

    Java 2023年5月23日
    00
  • 跨站脚本攻击XSS原理与防范实例分析

    跨站脚本攻击XSS原理与防范实例分析 XSS攻击原理 跨站脚本攻击(XSS)是通过在web应用程序中注入恶意脚本来攻击用户的一种常见安全漏洞。攻击者可将攻击代码注入到正常的web页面中,一旦被用户浏览器执行,就能够窃取用户的敏感信息或者利用用户的身份进行恶意操作。 XSS攻击通常分为以下三种类型: 存储型攻击:攻击者将恶意脚本注入到web应用程序中的数据库中…

    Java 2023年6月16日
    00
  • MySql修改数据库编码为UTF8避免造成乱码问题

    以下是MySql修改数据库编码为UTF8的攻略,具体步骤如下: 步骤一:备份数据库 在进行数据库编码修改之前,为了防止意外情况导致数据丢失,应该先备份好原有的数据库。备份有多种方法,常见的有使用phpMyAdmin或通过mysqldump命令备份。 示例一:使用phpMyAdmin备份数据库 打开phpMyAdmin,选择要备份的数据库。 点击“导出”选项卡…

    Java 2023年5月20日
    00
  • Bootstrap 下拉多选框插件Bootstrap Multiselect

    Bootstrap Multiselect 是一种基于 Bootstrap 框架的下拉多选框插件,可以帮助开发人员快速创建具有多选能力的下拉菜单控件。 安装 Bootstrap Multiselect Bootstrap Multiselect 可以通过以下几种方式进行安装: 1. 使用 CDN 在 HTML 文件中引入以下两个脚本即可: <scrip…

    Java 2023年6月16日
    00
  • Kotlin与Java的主客观对比分析

    Kotlin与Java的主客观对比分析 引言 Kotlin是一种针对Android开发的编程语言,它可以直接将Kotlin代码转换为Java字节码,因此可以与Java进行良好的兼容。本文将深入剖析Kotlin与Java在多个方面的对比分析。 语法 Kotlin相对于Java来说有更加简洁、直观的语法。Kotlin支持Lambda表达式、扩展函数、空安全等特性…

    Java 2023年6月1日
    00
  • Java实现跳跃表的示例详解

    让我来为您详细讲解“Java实现跳跃表的示例详解”的完整攻略。 什么是跳跃表 跳跃表是一种特殊的数据结构,它能快速地在有序链表中进行查找、插入和删除等操作,其效率甚至可以比拟红黑树。 跳跃表通过概率分布来随机地确定新节点的层数,这样就可以在一定程度上减少查找时需要比较的节点数目,从而提高查找效率。同时,跳跃表还可以通过动态调整层数来保证其平衡性。 如何实现跳…

    Java 2023年5月18日
    00
  • SpringBoot2零基础到精通之配置文件与web开发

    配置文件 Spring Boot 项目中的配置文件一般有 application.properties 和 application.yml 两种,它们用于对应不同的属性配置和格式化方式,常见的属性配置包括数据库连接、端口号、日志级别等。其中,application.yml 文件的格式化方式相对更灵活,可以嵌套、缩进和列表项等等。 下面以 MySQL 数据库连…

    Java 2023年5月15日
    00
合作推广
合作推广
分享本页
返回顶部