SpringMVC

文章目录

  • [1. MVC 架构](#1. MVC 架构)
  • [2. 基于 Servlet 的 MVC 模式](#2. 基于 Servlet 的 MVC 模式)
  • [3. Model 1模式 与 Model 2模式](#3. Model 1模式 与 Model 2模式)
      • [Model 1 模式:](#Model 1 模式:)
      • [Model 2 模式:](#Model 2 模式:)
  • [4. MVVM 架构](#4. MVVM 架构)
  • [5. Spring MVC](#5. Spring MVC)
  • [6. SpringMVC 实现步骤](#6. SpringMVC 实现步骤)
  • [8. Controller 的两种实现方式](#8. Controller 的两种实现方式)
  • [9. RestFul风格](#9. RestFul风格)
  • [10. @ResponseBody](#10. @ResponseBody)
  • [11. 乱码解决](#11. 乱码解决)
  • [12. 过滤器 VS 拦截器](#12. 过滤器 VS 拦截器)
  • 参考

1. MVC 架构

MVC 是 Model、View 和 Controller 的缩写,分别代表 Web 应用程序中的 3 种职责。

  • (Model)模型:用于存储数据以及处理用户请求的业务逻辑。

  • (View)视图:向控制器提交数据,显示模型中的数据。

  • (Controller)控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示。

2. 基于 Servlet 的 MVC 模式

模型:一个或多个 JavaBean 对象,用于存储数据(实体模型,由 JavaBean 类创建)和处理业务逻辑(业务模型,由一般的 Java 类创建)。

视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据。

控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示。

最典型的MVC就是JSP + servlet + javabean的模式。

3. Model 1模式 与 Model 2模式

Model 1 模式:

  • 特点

    • 在 Model 1 模式中,整个应用程序的业务逻辑、数据处理和表示层都在同一个组件中。
    • 通常使用 JSP(JavaServer Pages)作为视图层,Java Servlet 用于处理用户请求并在需要时调用 JavaBean(数据模型)来处理业务逻辑。
    • 没有明确的分层结构,所有的逻辑都混合在一起,导致代码难以维护和扩展。
  • 优点

    • 简单,适用于小型应用程序或快速原型开发。
  • 缺点

    • 缺乏分离关注点,使得代码难以理解、测试和维护。
    • 不利于团队合作和代码重用。
    • 可扩展性较差,难以在应用程序规模增大时进行维护。

Model 2 模式:

  • 特点

    • Model 2 模式引入了更严格的分层结构,将应用程序分为模型、视图和控制器三个独立的组件。
    • 通常使用 Servlet 作为控制器层,负责接收用户请求并调用适当的模型进行处理,然后将结果传递给视图层进行呈现。
    • JSP 通常被用作视图层,但与 Model 1 不同的是,JSP 只负责显示数据,不包含业务逻辑。
  • 优点

    • 更好的分离关注点,使代码更易于理解、测试和维护。
    • 支持团队合作和代码重用,因为不同的组件可以独立开发和测试。
    • 更好的可扩展性,允许应用程序在不同的部分进行修改和扩展,而不影响其他部分。
  • 缺点

    • 相对于 Model 1,Model 2 模式需要更多的代码和配置,因此可能增加开发时间和成本。
    • 对于小型应用程序或快速原型开发来说,可能会感到过度复杂。

4. MVVM 架构

把View和Contrller都放在了View层(相当于把Controller一部分逻辑抽离了出来),Model层依然是服务端返回的数据模型。

而ViewModel充当了一个UI适配器的角色,也就是说View中每个UI元素都应该在ViewModel找到与之对应的属性。除此之外,从Controller抽离出来的与UI有关的逻辑都放在了ViewModel中,这样就减轻了Controller的负担。

  • View层:视图展示。包含UIView以及UIViewController,View层是可以持有ViewModel的。

  • ViewModel层:视图适配器。暴露属性与View元素显示内容或者元素状态一一对应。一般情况下ViewModel暴露的属性建议是readOnly的,至于为什么,我们在实战中会去解释。还有一点,ViewModel层是可以持有Model的。

  • Model层:数据模型与持久化抽象模型。数据模型很好理解,就是从服务器拉回来的JSON数据。而持久化抽象模型暂时放在Model层,是因为MVVM诞生之初就没有对这块进行很细致的描述。按照经验,我们通常把数据库、文件操作封装成Model,并对外提供操作接口。(有些公司把数据存取操作单拎出来一层,称之为DataAdapter层,所以在业内会有很多MVVM的变种,但其本质上都是MVVM)。

  • Binder:MVVM的灵魂。可惜在MVVM这几个英文单词中并没有它的一席之地,它的最主要作用是在View和ViewModel之间做了双向数据绑定。如果MVVM没有Binder,那么它与MVC的差异不是很大。

例子

  1. 创建模型(Model) :在这个示例中,我们创建了一个简单的Person类,用于表示人员的数据结构。它有两个属性:firstNamelastName
java 复制代码
public class Person {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}
  1. 创建视图(View) :我们使用JavaFX创建了一个简单的用户界面。MainView类继承了JavaFX的Application类,并实现了start()方法。在start()方法中,我们创建了两个文本框(TextField)用于输入firstNamelastName,以及一个按钮(Button)用于保存数据。这些UI组件被添加到一个垂直布局(VBox)中,并且展示在舞台(Stage)上。
java 复制代码
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class MainView extends Application {
    private TextField firstNameField;
    private TextField lastNameField;
    private Button saveButton;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("MVVM Example");

        firstNameField = new TextField();
        lastNameField = new TextField();
        saveButton = new Button("Save");

        VBox root = new VBox();
        root.getChildren().addAll(firstNameField, lastNameField, saveButton);

        Scene scene = new Scene(root, 300, 200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}
  1. 创建视图模型(ViewModel)PersonViewModel类负责连接视图和模型。它包含了firstNamelastName的字符串属性,并且维护了一个保存按钮(saveButton)。在构造函数中,我们将保存按钮传递进来,并且设置了一个监听器,以便在firstNamelastName字段不为空时激活保存按钮。
java 复制代码
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.Button;

public class PersonViewModel {
    private StringProperty firstName = new SimpleStringProperty();
    private StringProperty lastName = new SimpleStringProperty();
    private Button saveButton;

    public PersonViewModel(Button saveButton) {
        this.saveButton = saveButton;
        // 添加监听器,当firstName和lastName不为空时,激活保存按钮
        saveButton.disableProperty().bind(firstName.isEmpty().or(lastName.isEmpty()));
    }

    public StringProperty firstNameProperty() {
        return firstName;
    }

    public StringProperty lastNameProperty() {
        return lastName;
    }
}
  1. 连接视图和视图模型 :在MainView类中,我们需要实例化视图模型,并将视图和视图模型连接起来。为了实现这一点,我们需要在start()方法中创建一个PersonViewModel对象,并且在按钮点击事件中设置监听器,以便在点击按钮时保存数据。
java 复制代码
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class MainView extends Application {
    private TextField firstNameField;
    private TextField lastNameField;
    private Button saveButton;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("MVVM Example");

        // 创建视图模型
        PersonViewModel viewModel = new PersonViewModel(saveButton);

        firstNameField = new TextField();
        lastNameField = new TextField();
        saveButton = new Button("Save");

        // 绑定视图和视图模型的属性
        firstNameField.textProperty().bindBidirectional(viewModel.firstNameProperty());
        lastNameField.textProperty().bindBidirectional(viewModel.lastNameProperty());

        VBox root = new VBox();
        root.getChildren().addAll(firstNameField, lastNameField, saveButton);

        // 设置保存按钮点击事件
        saveButton.setOnAction(event -> {
            // 在此处可以执行保存操作,例如将数据传递给模型进行持久化存储
            System.out.println("Saving: " + viewModel.getFirstName() + " " + viewModel.getLastName());
        });

        Scene scene = new Scene(root, 300, 200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

5. Spring MVC

图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。

执行流程

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

假设请求的url为 : http://localhost:8080/SpringMVC/hello

如上url拆分成三部分:

http://localhost:8080:服务器域名

SpringMVC:部署在服务器上的web站点

hello:控制器

通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

  1. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。

  2. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。

  3. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。

  4. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。

  5. Handler让具体的Controller执行。

  6. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。

  7. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

  8. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。

  9. 视图解析器将解析的逻辑视图名传给DispatcherServlet。

  10. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。

  11. 最终视图呈现给用户。

6. SpringMVC 实现步骤

实现步骤:

  1. 新建一个web项目

  2. 导入相关jar包

  3. 编写web.xml , 注册DispatcherServlet

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--1.注册servlet-->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!-- 启动顺序,数字越小,启动越早 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--所有请求都会被springmvc拦截 -->
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

/*
注意/ 和 /* 的区别:
< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;
即:.jsp 不会进入spring的 DispatcherServlet类 。

< url-pattern > /* </ url-pattern > 会匹配 *.jsp,
会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,
导致找不到对应的controller所以报404错。
  1. 编写springmvc配置文件(在resource目录下添加springmvc-servlet.xml配置文件, 配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:)
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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="com.github.subei.controller"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler />
    <!--
    支持mvc注解驱动
        在spring中一般采用@RequestMapping注解来完成映射关系
        要想使@RequestMapping注解生效
        必须向上下文中注册DefaultAnnotationHandlerMapping
        和一个AnnotationMethodHandlerAdapter实例
        这两个实例分别在类级别和方法级别处理。
        而annotation-driven配置帮助我们自动完成上述两个实例的注入。
     -->
    <mvc:annotation-driven />

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

</beans>
  1. 接下来就是去创建对应的控制类 , controller
java 复制代码
package com.github.subei.controller;

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

@Controller
@RequestMapping("/HelloController")
public class HelloController {

    // 真实访问地址 : 项目名/HelloController/hello
    @RequestMapping("/hello")
    public String sayHello(Model model){
        // 向模型中添加属性msg与值,可以在JSP页面中取出并渲染
        model.addAttribute("msg","hello,SpringMVC");
        // web-inf/jsp/hello.jsp
        return "hello";
    }
}
  1. 最后完善前端视图和controller之间的对应, jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>SpringMVC</title>
</head>
<body>
${msg}
</body>
</html>
  1. 测试运行调试.

使用springMVC必须配置的三大件:

  • 处理器映射器、处理器适配器、视图解析器
  • 通常,只需要手动配置视图解析器 ,而处理器映射器处理器适配器 只需要开启注解驱动即可,而省去了大段的xml配置

8. Controller 的两种实现方式

  1. 实现 Controller 接口并重写handleRequest() 方法,并在springmvc-servlet.xml 添加 该控制器为bean,以及其访问路径;

  2. 使用注解,@Controller, @RequestMapping("/path")等

9. RestFul风格

在类上直接使用 @RestController ,这样子,里面所有的方法都只会返回 json 字符串了,不用再每一个都添加@ResponseBody !

Restful风格是一种基于标准HTTP方法来设计Web服务的架构风格。它强调使用HTTP协议的各种特性来实现资源的增删改查(CRUD操作),以及对资源的状态转换。下面是Restful风格的一些主要特点:

  1. 资源(Resources): 在Restful架构中,一切皆资源。资源可以是任何事物,例如用户、商品、订单等等。每个资源都有一个唯一的标识符(URI)来进行访问。

  2. HTTP方法(HTTP Methods): ++Restful风格使用HTTP方法来定义操作资源的行为。常用的HTTP方法包括GET(获取资源)、POST(创建资源)、PUT(更新资源)、DELETE(删除资源)等。++

使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!

  1. 表述(Representation): 资源的表述是指客户端与服务器之间交换的数据格式。通常使用JSON或XML格式来表示资源的状态。@ResponseBody

  2. 无状态(Stateless): Restful架构是无状态的,即服务器不会保存客户端的状态信息。每个请求都包含足够的信息,使服务器能够理解并处理请求。

  3. 统一接口(Uniform Interface): Restful架构通过使用统一的接口来简化系统架构,并提高系统的可见性和可伸缩性。这包括使用标准的HTTP方法、URI和媒体类型。

  4. 资源链接(Resource Linking): Restful架构允许在资源之间建立链接关系,以支持导航和发现。

  5. 无连接(Stateless): Restful架构是无连接的,即客户端的每个请求都包含足够的信息,使服务器能够理解并处理请求,而不需要依赖之前的请求。

10. @ResponseBody

由于@ResponseBody注解,(return str;)这里会将str转成json格式返回;十分方便

注意:使用json记得处理乱码问题

在类上直接使用 @RestController ,这样子,里面所有的方法都只会返回 json 字符串了,不用再每一个都添加@ResponseBody !

11. 乱码解决

  1. 在 web.xml 配置字符编码过滤器,以确保所有请求和响应都使用正确的字符编码
xml 复制代码
<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 自定义过滤器类实现 Filter 接口,重写doFilter() 方法
java 复制代码
import javax.servlet.*;
import java.io.IOException;

public class EncodingFilter implements Filter {

    private String encoding;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        encoding = filterConfig.getInitParameter("encoding");
        if (encoding == null) {
            encoding = "UTF-8"; // 默认使用UTF-8编码
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 过滤器销毁时的处理
    }
}

12. 过滤器 VS 拦截器

区别:

过滤器(Filter)和拦截器(Interceptor)是在 Java Web 开发中用于实现对请求和响应进行处理的两种不同机制。

  1. 作用层次不同
  • ++过滤器是基于 Servlet 规范++的,工作在 Servlet 层面,可以在请求进入 Servlet 之前或响应离开 Servlet 之后进行处理。
  • ++拦截器是 Spring 框架提供的++,工作在 Spring MVC 层面,用于对控制器的请求进行预处理和后处理。
  1. 所属技术框架不同
  • 过滤器是 Servlet 规范中的一部分,属于 Java EE 的一部分,而且不依赖于任何特定的框架。
  • 拦截器是 Spring MVC 框架提供的,用于在 Spring MVC 控制器中进行请求处理的一个机制。
  1. 调用方式不同
  • 过滤器通过 Servlet 容器的 doFilter() 方法调用,直接在 Servlet 的请求处理链中执行。
  • 拦截器则是由 Spring MVC 框架控制器所管理的,通过AOP(面向切面编程)的思想,与控制器方法进行交互。
  1. 灵活性不同
  • 过滤器的灵活性相对较低,++无法直接访问 Spring 托管的 Bean++,通常用于请求的预处理和后处理。

    过滤器的设计初衷是为了处理 Servlet 请求和响应,它们并不属于 Spring 容器的管理范围,因此在过滤器中无法直接通过 Spring 上下文来访问 Spring 托管的 Bean。

  • 拦截器则++可以方便地访问 Spring 托管的 Bean++,可以在请求处理前后执行更加灵活的逻辑,例如验证用户权限、记录日志等。

  1. 作用范围不同
  • ++过滤器作用于 Servlet 的请求处理链中++,可以对所有的请求进行拦截和处理。
  • ++拦截器则主要用于对 Spring MVC 控制器的请求++进行处理,因此仅作用于经过 Spring MVC 控制器的请求。

参考

Spring MVC入门学习目录 - subeiLY - 博客园

相关推荐
淡泊if2 小时前
RESTful API设计标准:单体 vs 微服务的最佳实践
后端·微服务·restful
小二·3 小时前
Go 语言系统编程与云原生开发实战(第3篇):企业级 RESTful API 开发 —— 中间件、验证、文档与权限控制
云原生·golang·restful
秃头续命码农人6 小时前
谈谈对Spring、Spring MVC、SpringBoot、SpringCloud,Mybatis框架的理解
java·spring boot·spring·mvc·maven·mybatis
树码小子7 小时前
SpringMVC(2)传入请求参数
spring·mvc
树码小子8 小时前
SpringMVC(1)初识MVC
spring·mvc
风景的人生1 天前
请求参数相关注解
spring·mvc
空空kkk2 天前
Spring、Spring MVC、SpringBoot的欢迎页配置
spring boot·spring·mvc
yangminlei2 天前
Spring MVC 响应机制综合实践:页面、数据、JSON 与响应配置
spring·json·mvc
码农幻想梦2 天前
实验九 Restful和ajax实现
后端·ajax·restful
哪里不会点哪里.3 天前
Spring MVC 核心原理解析:从请求到响应到底发生了什么?
java·spring·mvc