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 - 博客园

相关推荐
△曉風殘月〆33 分钟前
WPF MVVM入门系列教程(二、依赖属性)
c#·wpf·mvvm
九鼎科技-Leo8 小时前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net
希忘auto13 小时前
详解Java之Spring MVC篇二
java·spring·mvc
LilKevinRay17 小时前
【SpringMVC】记录一次Bug——mvc:resources设置静态资源不过滤导致WEB-INF下的资源无法访问
java·笔记·mvc·bug
鹿屿二向箔21 小时前
基于 JAVASSM(Java + Spring + Spring MVC + MyBatis)框架开发一个九宫格日志系统
java·spring·mvc
Ares-Wang1 天前
ASP.NET Core 路由规则,自定义特性路由 ,IActionConstraint 路由约束 总结 mvc
后端·asp.net·mvc
技术拾荒者2 天前
.net core mvc 控制器中页面跳转
后端·c#·asp.net·mvc·.netcore
吴冰_hogan2 天前
spring-mvc源码
java·spring·mvc
鹿屿二向箔2 天前
基于 JAVASSM(Java + Spring + Spring MVC + MyBatis)框架开发一个医院挂号系统
java·spring·mvc
V+zmm101343 天前
校园综合服务小程序ssm+论文源码调试讲解
java·小程序·毕业设计·mvc·课程设计·1024程序员节