SpringMvc demo示例及源码详细分析

三层架构介绍

  我们的开发架构一般都是基于两种形式,一种C/S架构,也就是客户端/服务器,另一种是B/S架构,也就是浏览器/服务器。在JavaEE开发中,几乎全部都是基于B/S架构的开发。那么在B/S架构中,系统标准的三层架构包括:表现层、业务层、持久层。三层架构在我们的实际开发中使用的非常多。

三层职责

表现层

  也就是我们长说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,web需要接收http请求,完成http响应。

  表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。

  表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。

  表现层的设计一般都是使用mvc模型。(mvc是表现层的设计模型,和其他层没有关系)

业务层

  也就是我们常说的 service层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web层依赖业务层,但是业务层不依赖web层。

  业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)

持久层

  也就是我们常说的dao层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。

  通俗的讲,持久层就是和数据交互,对数据库表进行增删改查的。

mvc设计模式介绍

  mvc全名是Model View Controller,模型(Model)-视图(View)-控制器(Controller)的缩写,是一种用于设计创建web应用程序表现层的模式。mvc中每个部分各司其职:

Model(模型)

  模型包含业务模型和数据模型,数据模型用于封装数据,业务模型用于处理业务。

View(视图)

  通常指的就是我们的jsp或者html。作用一般就是展示数据的。

  通过视图是依据模型数据创建的。

Controller(控制器)

  是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。

SpringMVC介绍

Spring MVC是什么?

  SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring框架提供了构建Web应用程序的全功能MVC模块。使用Spring可插入的MVC架构,从而在使用Spring进行Web开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts2等。

  SpringMVC已经成为目前最主流的MVC框架之一,并随着Spring3.0的发布,全面超越Struts2,成为最优秀的MVC框架。

  它通过一套注解,让一个简单的Java类称为处理请求的控制器,而无需实现任何接口。同时它还支持RESTful编程风格的请求。

总结

  Spring MVC和Struts2一样,都是为了解决表现层问题的web框架,他们都是基于MCC设计模式的。而这些表现层框架的主要职责就是处理前端HTTP请求

 Spring MVC由来?

 Spring MVC全名叫Spring Web MVC,它是Spring家族Web模块的一个重要成员。这一点,我们可以从Spring的整体结构中看的出来:

 

 

 为什么学习SpringMVC?

   也许你会问,为什么要学习Spring MVC呢?struts2不才是主流嘛?看SSH的概念有多火?

  其实很多初学者混淆了一个概念,SSH实际上指的是Struts1.x+Spring+Hibernate。这个概念已经有十几年的历史了。在Struts1.x时代,它是当之无愧的霸主,但是在新的MVC框架涌现的时代,形式已经不是这样了,Struts2.x借助了Struts1.x的好名声,让国内开发人员认为Struts2.x是霸主继任者(其实两者在技术上无任何关系),导致国内程序员大多数学习基于Struts2.x的框架,又一个貌似很多的概念出来了S2SH(Struts2+Spring+Hibernate)整合开发。

 SpringMVC如何处理请求?

   SpringMVC是基于MVC设计模型的,MVC模式指的就是Model(业务模型)、View(视图)、Controller(控制器)。SpringMVC处理请求就是通过MVC这三个角色来实现的。

注:不要把MVC设计模式工程的三层架构混淆,三层结构指的是表现层、业务层、数据持久层。而MVC只针对表现层进行设计

  下面让我们看看处理流程吧

 

 

 第一个MVC程序

达到效果

  1. 学会如果配置前端控制器
  2. 如何开发处理器

任务需求

  访问/queryItem,返回商品列表页面,商品数据暂时使用静态数据(不从数据库查询并返回)。

 实现

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.cyb</groupId>
    <artifactId>springmvc-demo01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <!-- spring ioc组件需要的依赖包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>

        <!-- 基于AspectJ的aop依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>

        <!-- spring MVC依赖包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>

        <!-- jstl -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        
        <!-- servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- 配置Maven的JDK编译级别 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>8080</port>
                </configuration>
            </plugin>
            <!-- tomcat依赖包 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
            </plugin>
        </plugins>
    </build>
</project>

注:

1、依赖添加完之后,项目上右键->maven->Update Maven Project

2、项目上右键->Java EE Tools->Generate Deployment Descriptor Stub

 web.xml

路径:src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <!-- 学习前置条件 -->
    <!-- 问题1:web.xml中servelet、filter、listener、context-param加载顺序 -->
    <!-- 问题2:load-on-startup标签的作用,影响了Servlet对象创建的时机 -->
    <!-- 问题3:url-pattern:标签的配置方式有四种:/dispatcherServlet、/servlet/*、*.do、/ 以上四种配置-->
    <!-- 问题4:url-pattern标签的配置为什么配置/就不拦截jsp请求,而配置/*,就会拦截jsp请求 -->
    <!-- 问题4原因:标签配置为/*报错,因为它拦截了jsp请求,但是又不能处理jsp请求。 -->
    <!-- 问题5:配置了springmvc去读取spring配置文件之后,就产生了spring父子容器的问题 -->
    
    <!-- 配置前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 设置spring配置文件路径 -->
        <!-- 如果不设置初始化参数,那么DispatcherServlet会读取默认路径下的配置文件 -->
        <!-- 默认配置文件路径:/WEB-INF/springmvc-servlet.xml -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 指定初始化时机,设置为2,表示Tomcat启动时,它会跟随着启动,DispatcherServlet会跟随着初始化 -->
        <!-- 如果没有指定初始化时机,DispatcherServlet就会在第一次被请求的时候,才会初始化,而且只会被初始化一次(单例模式) -->
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!-- url-pattern的设置 -->
        <!-- 不要配置为/*,否则报错 -->
        <!-- 通俗解释:会拦截整个项目中的资源访问,包含JSP和静态资源的访问,对于JS的访问,springmvc提供了默认Handler处理器 -->
        <!-- 但是对于JSP来讲,springmvc没有提供默认的处理器,我们也没有手动编写对应的处理器,此时按照springmvc的处理流程分析得知,它down了 -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

springmvc.xml

路径:src/main/resources/springmvc.xml

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 处理器类的扫描 -->
    <context:component-scan
        base-package="com.cyb.springmvc.controller"></context:component-scan>
    <!-- 注解映射器 @Controller和@RequestMapping组合这种方式的注解映射的解析 -->
    <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean> -->
    <!-- 注解适配器 -->
    <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean> -->
    <!-- 配置注释的适配器和映射器,同时还注入其他很多的bean -->
    <!-- <mvc:annotation-driven></mvc:annotation-driven> -->
    <!-- 显示配置视图解析器 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

ItemController.java

路径:/src/main/java/com/cyb/springmvc/controller/ItemController.java

package com.cyb.springmvc.controller;

import java.util.ArrayList;
import java.util.List;

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

import com.cyb.springmvc.po.item;

/**
 * 处理器的开发方式有多种,比如实现HttpRequestHandler接口、Controller接口的方式、还有注解的方式 企业中使用的一般都是注解的方式
 * 注解的注意事项
 *  1、类上加上@Controller注解(必须是Controller,可以通过源码找到答案)
 *  2、类上或者方法上面要加上@RequestMapping(必须)
 * 
 * @author apple
 *
 */
@Controller
public class ItemController {
    //@RequestMapping此时填写的是url
    //ModelAndView:Model标识的是数据类型,View就是最终要展示给用户的视图
    @RequestMapping("queryItem")
    public ModelAndView queryItem() {
        //用静态数据模型
        List<item> itemList=new ArrayList<item>();
        
        item item_1=new item();
        item_1.setName("苹果手机");
        item_1.setPrice(5000);
        item_1.setDetail("iphoneX苹果手机!");
        itemList.add(item_1);
        
        item item_2=new item();
        item_2.setName("华为手机");
        item_2.setPrice(6000);
        item_2.setDetail("华为5G网速就是快!");
        itemList.add(item_2);
        ModelAndView mvAndView=new ModelAndView();
        //设置数据模型,相当于request的setAttribute方法,实质上,底层确实也是转成了request()
        //先将k/v数据放入map中,最终根据视图对象不同,再进行后续处理
        mvAndView.addObject("itemList",itemList);
        //设置view视图
        mvAndView.setViewName("/WEB-INF/jsp/item/item-list.jsp");
        return mvAndView;
    }
}

item.java

路径:src/main/java/com/cyb/springmvc/po/item.java

package com.cyb.springmvc.po;

public class item {
    private String name;
    private double price;
    private String detail;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }
}

item-list.jsp

 路径:src/webapp/WEB-INF/jsp/item/item-list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>查询商品列表</title>
</head>
<body>
    <form action="${pageContext.request.contextPath }/itemList.do"
        method="post">
        查询条件:
        <table width="100%" border=1>
            <tr>
                <td><input type="submit" value="查询" /></td>
            </tr>
        </table>
        商品列表:
        <table width="100%" border=1>
            <tr>
                <td>商品名称</td>
                <td>商品价格</td>
                <td>商品描述</td>
                <td>操作</td>
            </tr>
            <c:forEach items="${itemList }" var="item">
                <tr>
                    <td>${item.name }</td>
                    <td>${item.price }</td>
                    <td>${item.detail }</td>
                    <td><a
                        href="${pageContext.request.contextPath }/itemEdit.do?id=${item.name}">修改</a></td>
                </tr>
            </c:forEach>

        </table>
    </form>
</body>

</html>

 项目结构图

 运行

 完整项目

直接下载

 SpringMVC 框架源码分析

 框架结构

 程序入口

一、初始化Servlet

二、处理器映射,渲染页面

 注:标记的方法体,跟踪进去读源码就好啦!~~

默认配置文件

 

 

 

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
    org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
    org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

 

架构流程

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

 组件说明

 DispatcherServlet:前端控制器

   用户请求到达前端控制器,它就相当于mvc模式中的C,DispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。

HandlerMapping:处理器映射器

   HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

Handler:处理器

  Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下,Handler对具体的用户请求进行处理。

  由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。

HandlerAdapter:处理器适配器

  通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

 View Resolver:视图解析器

  View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

View:视图

  springmvc框架提供了很多View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。

  一般情况下需要通过页面标签或页面模板技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

说明

  再springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。需要用户开发的组件有:处理器、视图

三大组件配置(注解方式)

注解映射器和适配器

通过bean标签配置

RequestMappingHandlerMapping:注解式处理器映射器

  对类中标记@ResquestMapping的方式进行映射,根据ResquestMapping定义的url匹配ResquestMapping标记的方法,匹配成功返回HandlerMethod对象给前端控制器,HandlerMethod对象中封装url对应的方法Method。

配置如下:

<!--注解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

通过mvc标签配置(推荐)

<mvc:annotation-drivern />

  mvc:annotation-drivern标签的作用,详见AnnotationDrivenBeanDefinitionParser类的parse方法。分析源码可知:mvc:annotation-drivern往spring容器中注册以下的一些BeanDefinition

  • ContentNegotiationManagerFactoryBean
  • RequestMappingHandlerMapping
  • ConfigurableWebBindingInitializer
  • RequestMappingHandlerAdapter
  • CompositeUriComponentsContributorFactoryBean
  • ConversionServiceExposingInterceptor
  • MappedInterceptor
  • ExceptionHandlerExceptionResolver
  • ResponseStatusExceptionResolver
  • DefaultHandlerExceptionResolver
  • BeanNameUrlHandlerMapping
  • HttpRequestHandlerAdapter
  • SimpleControllerHandlerAdapter
  • HandlerMappingIntrospector

视图解析器

再springmvc.xml文件配置如下:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 该视图解析器,默认的视图类就是JstlView,可以不写 -->
        <property name="viewClass"
            value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
  • InternalResourceViewResolver:默认支持JSP视图解析
  •  viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,所以classpath中必须包含jstl的相关jar 包。此属性可以不设置,默认为JstlView
  • prefix suffix:查找视图页面的前缀和后缀,最终视图的址为:前缀+逻辑视图名+后缀,逻辑视图名需要在controller中返回的ModelAndView指定,比如逻辑视图名为hello,则最终返回的jsp视图地址 “WEB-INF/jsp/hello.jsp”
posted @ 2019-11-27 19:00  陈彦斌  阅读(3554)  评论(0编辑  收藏  举报