购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第1章
电瓶车品牌信息管理系统
——Vue.js+Spring Boot+MySQL

电瓶车又被称作电动自行车或电动助力车,是一种结合了传统自行车和电动车技术的交通工具。电瓶车作为一种环保、节能、便捷的交通工具,不仅适合短途出行,而且可以作为公共交通的接驳工具,可以减少人们对汽车的依赖,缓解城市交通拥堵和空气污染问题。本章将开发一个简单的全栈项目——电瓶车品牌信息管理系统,用于管理电瓶车的品牌信息。其中,本项目的前端将使用Vue.js予以实现,后端将使用Spring Boot予以实现。此外,本项目用于存储数据的工具是MySQL数据库。

本项目的核心功能及实现技术如下:

1.1 开发背景

随着科学技术的进步,人们的环保意识不断提高,电瓶车市场不断扩大,不断有新的制造商加入。同时,为了更好地满足消费者的需求,扩大市场占有率,各品牌纷纷推出新款车型,市场竞争日趋激烈。为了在市场竞争中脱颖而出,电瓶车制造商需要不断加大研发投入,不断推出更多符合市场需求的产品,不断提升产品质量和服务水平。例如,雅迪、爱玛等传统品牌通过提升产品性能和智能化水平来巩固市场地位;小牛电动、九号等新兴品牌通过推出符合年轻人口味的产品来聚焦高端市场。在这样的背景下,本章将开发一个全栈项目,即电瓶车品牌信息管理系统。该系统是基于对电瓶车的品牌信息进行管理的网络平台。前端负责把由后端实现的、用于查看、新增、删除电瓶车品牌信息等功能呈现在用户的浏览器上,进而达到与用户进行交互的目的;后端则负责处理由前端发送的请求(例如查看、新增或者删除电瓶车品牌信息),并根据这个请求执行相应的业务逻辑,最终把处理结果返回前端。

电瓶车品牌信息管理系统将实现以下目标:

页面简洁、功能明确、操作方便;

用户可以查看各个电瓶车的品牌信息(如品牌名称、品牌评分、好评率和品牌介绍等);

用户可以新增电瓶车的品牌信息;

用户可以删除某个电瓶车的品牌信息。

1.2 系统设计

1.2.1 开发环境

本项目的开发及运行环境如下:

操作系统:推荐Windows 10、11及以上版本,兼容Windows7(SP1)。

开发工具:IntelliJ IDEA。

前端实现技术:HTML5、CSS3、JavaScript、Vue.js。

后端实现技术:Java EE、Spring Boot。

数据库:MySQL 8.0。

Web服务器:Tomcat 9.0及以上版本。

1.2.2 业务流程

启动项目后,打开浏览器,访问http://localhost:8080/pages/bikes.html,即可看到电瓶车品牌信息管理系统的主页面。

在主页面上,程序将分页显示10条数据,共分两页,每一页最多显示7条数据,用户通过分页导航可以随意切换并访问这两个分页的数据。

用户在单击“新增电瓶车品牌信息”按钮后,程序将弹出一个窗口,用户在这个窗口中依次输入电瓶车的品牌名称、品牌评分、好评率和品牌介绍等信息,单击“确定”按钮,即可完成新增电瓶车品牌信息的操作。

在主页面上显示的每一条数据的后面,都有一个“删除”按钮,用户先单击某一个“删除”按钮,再单击删除提示窗口中的“确定”按钮,程序将删除对应的电瓶车品牌信息。

电瓶车品牌信息管理系统的业务流程如图1.1所示。

1.2.3 功能结构

本项目的功能结构已经在章首页中给出,作为基于对电瓶车的品牌信息进行管理的网络平台,本项目实现的具体功能如下:

分页插件:用于显示数据总数、分页数、与某个页面对应的数据和分页导航等信息。

查询电瓶车的品牌信息:查询数据库中所有电瓶车的品牌名称、品牌评分、好评率和品牌介绍等信息。

图1.1 电瓶车品牌信息管理系统的业务流程图

新增电瓶车的品牌信息:向数据库添加新的电瓶车品牌信息。

删除电瓶车的品牌信息:从数据库删除某一条电瓶车品牌信息。

1.3 前端技术准备

在实际开发中,Spring Boot和Vue.js的结合被广泛应用于构建Web应用程序。Spring Boot和Vue.js的结合不仅使得前、后端能够并行开发以缩短开发周期,而且使得前、后端能够分离设计以方便维护。此外,Spring Boot和Vue.js都具有丰富的API,便于程序开发人员解决在程序开发过程中遇到的问题。本节将先简单介绍本项目所使用的前端核心技术Vue.js,下一节再介绍本项目所使用的后端核心技术Spring Boot。

对于一个全栈项目而言,前端指的是Web应用程序中与用户进行交互的部分,它主要负责把由后端提供的数据和实现的功能呈现在用户的浏览器上。前端通常由HTML、CSS和JavaScript予以构建。Vue.js(简称为Vue)是一个开源的、非常受欢迎的JavaScript框架,是一套用于构建用户界面的渐进式框架。与其他重量级框架不同的是,它只关注视图层,采用自底向上增量开发的设计。Vue.js的核心目标之一,是通过尽可能简单的API实现响应的数据绑定和可组合的视图组件。它不仅容易上手,还非常容易与其他库或已有项目进行整合。Vue.js实际上是一个用于开发Web前端界面的库,其本身具有响应式编程和组件化的特点。所谓响应式编程,即保持状态和视图的同步。响应式编程允许将相关模型的变化自动反映到视图上,反之亦然。和其他前端框架一样,Vue.js同样采用“一切都是组件”的理念,即将一个网页分割成多个可复用的组件。下面将对本项目中用到的Vue.js中的重点知识进行必要介绍,以确保读者可以顺利完成本项目。

1.3.1 应用程序实例及选项

每个Vue.js的应用都需要创建一个应用程序的实例对象并挂载到指定DOM上。在Vue.js 3.0中,创建一个应用程序实例的语法格式如下:

    Vue.createApp(App)

createApp()是一个全局API,它接收一个根组件选项对象作为参数。选项对象中包括数据、方法、生命周期钩子函数等。创建应用程序实例后,可以调用实例的mount()方法,将应用程序实例的根组件挂载到指定的DOM元素上。这样,该DOM元素中的所有数据变化都会被Vue.js所监控,从而实现数据的双向绑定。例如,要绑定的DOM元素的id属性值为app,创建一个应用程序实例并绑定到该DOM元素的代码如下:

    Vue.createApp(App).mount('#app')

下面分别对组件选项对象中的几个常用选项进行介绍。

1.数据

在组件选项对象中有一个data选项,该选项是一个函数,Vue.js在创建组件实例时会调用该函数。data()函数可以返回一个数据对象,应用程序实例本身会代理数据对象中的所有数据。例如,创建一个根组件实例vm,在实例的data选项中定义一个数据,代码如下:

上述代码中,将创建的根组件实例赋值给变量vm,在实际开发中并不要求一定要将根组件实例赋值给某个变量。

2.方法

在创建的应用程序实例中,通过methods选项可以定义方法。应用程序实例本身也会代理methods选项中的所有方法,因此也可以像访问data数据那样来调用方法。例如,在根组件实例的methods选项中定义一个showInfo()方法,代码如下:

3.生命周期钩子

每个应用程序实例在创建时都有一系列的初始化步骤。例如,创建数据绑定、编译模板、将实例挂载到DOM并在数据变化时触发DOM更新、销毁实例等。在这个过程中会运行一些叫作生命周期钩子的函数,通过这些钩子函数可以定义业务逻辑。应用程序实例中几个主要的生命周期钩子函数说明如下:

beforeCreate:在实例初始化之后,数据观测和事件/监听器配置之前调用。

created:在实例创建之后进行调用,此时尚未开始DOM编译。在需要初始化处理一些数据时会比较有用。

beforeMount:在挂载开始之前进行调用,此时DOM还无法操作。

mounted:在DOM文档渲染完毕之后进行调用。相当于JavaScript中的window.onload()方法。

beforeUpdate:在数据更新时进行调用,适合在更新之前访问现有的DOM,例如手动移除已添加的事件监听器。

updated:在数据更改导致的虚拟DOM被重新渲染时进行调用。

beforeDestroy:在销毁实例前进行调用,此时实例仍然有效,可以解绑一些使用addEventListener监听的事件等。

destroyed:在实例被销毁之后进行调用。

这里通过一个示例来了解Vue.js内部的运行机制,代码如下:

在浏览器控制台中运行上述代码,页面渲染完成后,结果如图1.2所示。

经过两秒钟后调用setTimeout()方法,修改text的内容,触发beforeUpdate和updated钩子函数,结果如图1.3所示。

图1.2 页面渲染后的效果

图1.3 页面最终效果

1.3.2 常用指令

在Vue.js中,为了实现渲染视图的功能,指令是必不可少的。例如,在视图中经常需要通过条件判断控制DOM的显示状态,这时就需要使用v-if、v-else、v-else-if等指令。下面将对Vue.js中的常用指令进行介绍。

1.v-if指令

v-if指令可以根据表达式的值来判断是否输出DOM元素及其包含的子元素。如果表达式的值为true,则输出DOM元素及其包含的子元素;否则,将DOM元素及其包含的子元素移除。

v-if是一个指令,必须将它添加到一个元素上,根据表达式的结果判断是否输出该元素。如果需要对一组元素进行判断,需要使用<template>元素作为包装元素,并在该元素上使用v-if,最后的渲染结果里不会包含<template>元素。

例如,根据表达式的结果判断是否输出一组单选按钮,代码如下:

2.v-else指令

v-else指令的作用相当于JavaScript中的else语句,可以将该指令配合v-if指令一起使用。

例如,输出用户的年龄,并判断该年龄是否小于18。如果是,则输出用户未成年;否则输出用户已成年。代码如下:

3.v-else-if指令

v-else-if指令的作用相当于JavaScript中的else if语句,使用该指令可以进行更多的条件判断,不同的条件对应不同的输出结果。

例如,输出数据对象中的属性m和n的值,并根据比较两个属性的值,输出比较的结果。代码如下:

4.v-show指令

v-show指令是根据表达式的值判断是否显示或隐藏DOM元素。当表达式的值为true时,元素将被显示;当表达式的值为false时,元素将被隐藏,此时为元素添加了一个内联样式style="display:none"。与v-if指令不同,使用v-show指令的元素,无论表达式的值为true还是false,该元素都始终会被渲染并保留在DOM中。绑定值的改变只是简单地切换元素的CSS属性display。

注意

v-show指令不支持<template>元素,也不支持v-else指令。

5.v-for指令

Vue.js提供了列表渲染的功能,可将数组或对象中的数据循环渲染到DOM中。在Vue.js中,列表渲染使用的是v-for指令,其效果类似于JavaScript中的遍历操作。

v-for指令将根据接收到的数组中的数据重复渲染相应的DOM元素。该指令需要使用item in items形式的语法。其中,items为数据对象中的数组名称,item为数组元素的别名,通过别名可以获取当前数组遍历的每个元素。

例如,可以使用v-for指令将<li>标签循环渲染,输出数组中存储的职位名称,代码如下:

在使用v-for指令遍历数组时,还可以指定一个参数作为当前数组元素的索引,语法格式为(item, index)in items。其中,items为数组名称,item为数组元素的别名,index为数组元素的索引。

例如,可以使用v-for指令将<li>标签循环渲染,输出数组中存储的职位名称和相应的索引,代码如下:

1.4 后端技术准备

对于一个全栈项目而言,后端主要由服务器端和数据库组成,它负责处理由前端发送的请求,与数据库进行交互并执行相应的业务逻辑,把处理结果返回前端。Spring Boot是当下非常流行的一个后端框架,它是在Spring的基础上发展而来的、全新的、开源的框架。开发Spring Boot的主要动机是简化部署Web项目的配置过程。那么,Spring Boot是如何以更简单的、更灵活的方式开发Web项目的呢?Spring Boot通过自动配置机制简化了Web开发流程,程序开发人员只需要通过依赖注入即可获取所需对象,无须手动管理工厂类。它采用约定优先于配置的原则,程序开发人员只需要在配置文件中定义必要的参数,其余配置均采用合理的默认值。即使不进行任何显式配置,项目也能基于内置的默认设置正常启动。Spring Boot自带Tomcat服务器,在项目启动的过程中可以自动完成所有资源的部署操作。Spring Boot项目启动的速度很快,即使包含庞大的依赖库,也能够在几秒钟内完成部署和启动。下面将对本项目用到的Spring Boot中的重点知识进行必要介绍,以确保读者可以顺利完成本项目。

1.4.1 pom.xml文件

pom.xml文件是Maven构建项目的核心配置文件,程序开发人员可以在此文件中为项目添加新的依赖,添加在<dependencies>标签内部,作为其子标签,格式如下:

注意

<dependency>是<dependencies>的子标签,dependencies是dependency的复数形式。

例如,Spring Boot项目自带的Web依赖和JUnit单元测试依赖,其在pom.xml文件中的代码如下:

程序开发人员只需要仿照这种格式在<dependencies>标签内部添加其他依赖,而后保存pom.xml文件,Maven就会自动下载依赖中的JAR文件并自动将其引入项目中。

1.4.2 配置文件的格式

程序开发人员在配置Spring Boot项目的过程中,会在配置文件中配置该项目所需的数据信息。这些数据信息被称作“配置信息”。那么,配置信息都包含哪些内容呢?在实际开发中,配置信息的内容非常丰富,这里仅举例予以说明。

Tomcat服务器。

数据库的连接信息,即用于连接数据库的用户名和密码。

Spring Boot项目的启动端口。

第三方系统或者接口的调用密钥信息。

打印用于发现和定位问题的日志。

Spring Boot支持多种格式的配置文件,最常用的是properties格式(默认格式)和比较新颖的yml格式。下面将分别介绍这两种格式的特点。

1.properties格式

properties格式是经典的键值对文本格式。也就是说,如果某一个配置文件的格式是properties格式,那么这个配置文件的文本格式为键值对。键值对的语法非常简单,具体如下:

    key=value

在上述格式中,“=”左侧为键(key),“=”右侧为值(value)。在配置文件中,每个键独占一行。如果多个键之间存在层级关系,就需要使用“父键.子键”的格式予以表示。例如,在配置文件中,为一个有三层关系的键赋值的语法如下:

    key1.key2.key3=value

例如,启动Spring Boot项目的Tomcat端口号为8080,那么在这个项目的application.properties文件中就能够找到如下内容:

    server.port=8080

启动这个项目后,即可在控制台看到如下一行日志:

    Tomcat started on port(s): 8080 (http) with context path ''

这行日志表明Tomcat根据配置开启的是8080端口。

在application.properties文件中,“#”被称作注释符号,用于向其中添加注释信息。例如:

    # Tomcat 端口
    server.port=8080

application.properties文件不支持中文。如果程序开发人员在application.properties文件中编写中文,IntelliJ IDEA会自动将其转化为Unicode码,将鼠标指针悬停在Unicode码处可以看到对应的中文。

2.yml格式

yml是YAML的缩写,它是一种可读性高、用于表达数据序列化的文本格式。对于yml格式的配置文件,其文本格式也是键值对。只不过,键值对的语法与Python语言中的键值对的语法非常相似,具体如下:

    key: value
注意

英文格式的“:”与值之间必须有至少一个空格。

在上述格式中,英文格式的“:”左侧为键(key),英文格式的“:”右侧为值(value)。需要注意的是,英文格式的“:”与值之间只能用空格缩进,不能用tab缩进;空格数量表示各层的层级关系。例如,在配置文件中,为一个有三层关系的键赋值的语法如下:

    key1:
     key2:
      key3: value

在properties格式的配置文件,即使父键相同,在为每一个父键的子键赋值时也要单独占一行,还要把父键写完整,例如:

    com.mr.strudent.name=tom
    com.mr.strudent.age=21

但是在yml格式的配置文件中,只需要编写一次父键,并保证两个子键缩进关系相同即可。例如,把上述properties格式的键值对修改为yml格式的键值对的语句如下:

    com:
     mr:
      student:
       name: Tom
       age: 21

对于Spring Boot项目的配置文件,不论是采用properties格式,还是采用yml格式,都由程序开发人员自行决定。但是,在同一个Spring Boot项目中,应尽量只使用一种格式的配置文件。否则,这个Spring Boot项目中的yml格式的配置文件将被忽略。

1.4.3 注解

在给出注解的概念之前,须明确什么是元数据。所谓元数据,指的是用于描述数据的数据。下面结合某个配置文件里的一行信息,举例说明什么是元数据。

    <string name="app_name">AnnotionProject</string>

上述信息中的数据app_name用于描述数据AnnotionProject,数据app_name就是元数据。

那么,什么是注解呢?注解又被称作标注,是一种被加入源码的、具有特殊语法的元数据。需要特别说明的是:

注解仅是元数据,和业务逻辑无关。

虽然注解不属于程序本身,但是可以对程序作出解释。

应用程序中的类、方法、变量、参数、包等程序元素都可以被注解。

在理解了“什么是注解”后,再来了解一下在应用程序中注解的应用体现在哪些方面。

在编译时进行格式检查。例如,如果被@Override标记的方法不是父类的某个方法,编译器就会报错。

减少配置。依据代码的依赖性,使用注解替代配置文件。

减少重复工作。在程序开发的过程中,通过注解减少对某个方法的调用次数。

Spring Boot是一个支持海量注解的框架,其自带的常用注解如表1.1所示。

表1.1 Spring Boot 的常用注解

这些注解的编码位置是非常灵活的。当注解用于标注类、成员变量和方法时,注解的编码位置可以在成员变量的上边,例如:

    @Autowired
    private String name;

也可以在成员变量的左边,例如:

    @Autowired private String name;

在Spring Boot常用注解中,需要特别说明的是,使用@RequestParam能够标注方法中的参数。例如:

    @RequestMapping("/user")
    @ResponseBody
    public String getUser(@RequestParam Integer id) {
        return "success";
    }

1.4.4 启动类

使用注解能够启动一个Spring Boot项目,这是因为在每一个Spring Boot项目中都有一个启动类,并且启动类必须被@SpringBootApplication注解标注,进而能够调用用于启动一个Spring Boot项目的SpringApplication.run()方法。

在本项目中,com.mr包下的RunApplication类就是启动类。代码如下:

@SpringBootApplication注解虽然重要,但使用起来非常简单,因为这个注解是由多个功能强大的注解整合而成的。打开@SpringBootApplication注解的源码可以看到它被很多其他注解标注,其中最核心的3个注解如下:

@SpringBootConfiguration注解,让项目采用基于Java注解的配置方式,而不是传统的XML文件配置。当然,如果程序开发人员编写了传统的XML配置文件,Spring Boot也是能够读取这些XML文件并识别里面的内容的。

@EnableAutoConfiguration注解,开启自动配置。这样Spring Boot在启动的时候就可以自动加载所有配置文件和配置类了。

@ComponentScan注解,启用组件扫描器。这样项目才能自动发现并创建各个组件的Bean,包括Web控制器(@Controller)、服务(@Service)、配置类(@Configuration)和其他组件(@Component)。

注意

一个项目可以有多个启动类,但这样的代码毫无意义。一个项目应该只使用一次@SpringBootApplication注解。

1.4.5 处理HTTP请求

在开发Spring Boot项目的过程中,Spring Boot的典型应用是处理HTTP请求。所谓处理HTTP请求,就是Spring Boot把用户通过URL地址发送的请求交给不同的业务代码进行处理的过程。

Spring Boot提供了用于声明控制器类的@Controller注解。也就是说,在Spring Boot项目中,把被@Controller注解标注的类称作控制器类。控制器类在Spring Boot项目中发挥的作用是处理用户发送的HTTP请求。Spring Boot会把不同的用户请求交给不同的控制器进行处理,而控制器则会把处理后得到的结果反馈给用户。

说明

控制器(Controller)定义了应用程序的行为,它负责对用户发送的请求进行解释,并把这些请求映射成相应的行为。

因为@Controller注解本身被@Component注解标注,所以控制器类属于组件。这说明在启动Spring Boot项目时,控制器类会被扫描器自动扫描。这样,程序开发人员就可以在控制器类中注入Bean。例如,在控制器中注入Environment环境组件,代码如下:

    @Controller
    public class TestController {
        @Autowired
        Environment env;
    }

Spring Boot提供了用于映射URL地址的@RequestMapping注解。@RequestMapping注解可以标注类和方法。如果一个类或者方法被@RequestMapping注解标注,那么这个类或者方法就能够处理用户通过@RequestMapping注解映射的URL地址发送的请求。

注意

@Controller注解要结合@RequestMapping注解一起使用。

@RequestMapping有几个常用属性,下面主要对value属性进行介绍。

value属性是@RequestMapping注解的默认属性,用于指定映射的URL地址。在单独使用value属性时,value属性可以被隐式调用。调用value属性的语法如下:

    @RequestMapping("test")
    @RequestMapping("/test")
    @RequestMapping(value= "/test")
    @RequestMapping(value={"/test"})

上面这4种语法所映射的URL地址均为“域名/test”。其中,域名指的是当前Spring Boot项目所在的域。如果在IntelliJ IDEA中启动一个Spring Boot项目,那么域名就是127.0.0.1:8080。

@RequestMapping注解映射的URL地址可以是多层的。例如:

    @RequestMapping("/shop/books/computer")

上述代码映射的完整的URL地址是http://127.0.0.1:8080/shop/books/computer。需要特别注意的是,这个URL地址中的任何一层都是不可或缺的,否则将引发404错误。

@RequestMapping注解允许一个方法同时映射多个URL地址。其语法如下:

    @RequestMapping(value={ "/address1", "/address2", "/address3", ....... })

1.4.6 Service层

Spring Boot中的Service层是业务逻辑层,其作用是处理业务需求,封装业务方法,执行DAO层中用于访问、处理数据的操作。Service层通常由一个接口和这个接口的实现类组成。其中,Service层的接口可以在Controller层中被调用,用于实现数据的传递和处理;Service层的实现类须使用@Service注解予以标注。

在Spring Boot中,把被@Service注解标注的类称作服务类。@Service注解属于Component组件,可以被Spring Boot的组件扫描器扫描到。当启动Spring Boot项目时,服务类的对象会被自动地创建,并被注册成Bean。

Service层的实现过程如图1.4所示,具体实现过程如下:

图1.4 Service层的实现过程

(1)定义一个Service层的接口,在这个接口中定义用于传递和处理数据的方法。例如,定义一个Service层的接口ProductService,代码如下:

    public interface ProductService {
        …                                                 //省略用于传递和处理数据的方法
    }

(2)定义一个Service层的接口的实现类,使用@Service注解予以标注。这个实现类的作用有两个:一个作用是实现Service层的接口中的业务方法;另一个作用是执行DAO层中用于访问、处理数据的操作。例如,使用@Service注解标注实现ProductService接口的ProductServiceImpl类,代码如下:

    @Service
    public class ProductServiceImpl implements ProductService {
        …                                             //省略用于实现接口的业务方法和用于执行访问处理数据的操作的代码
    }

(3)在服务类的对象被自动地创建并被注册成Bean之后,其他Component组件即可直接注入这个Bean。

1.5 数据库设计

数据库是一个用于存储数据的仓库。通过数据库管理系统,可以有效地存储、组织和管理数据。本项目使用的是MySQL数据库。MySQL数据库是一个中小型、关系型数据库管理系统,由于其体积小、速度快、总体成本低,尤其是开放源码这一特点,许多大中小型网站都为了降低网站运营成本而选择MySQL作为后端数据库。此外,MySQL可以称得上是目前运行速度最快的SQL语言数据库管理系统。SQL语言,即结构化查询语言,它是世界上最流行的、标准化的数据库语言。在实际开发中,MySQL不仅为多种编程语言提供了API,而且支持多线程,同时还优化了SQL查询算法,极大地提高了性能、可扩展性、可用性,从而满足用户进行业务访问和业务处理的需求。本项目在MySQL数据库中创建一个名为db_e-bike的库,在这个库中创建与电瓶车品牌信息对应的表。

电瓶车品牌信息表的名称为bike,主要用于存储电瓶车的品牌编号、品牌名称、品牌评分、好评率、品牌介绍等,其结构如表1.2所示。

表1.2 bike表结构

1.6 后端依赖配置和公共模块设计

在开发Spring Boot项目的过程中,程序开发人员不仅需要为当前项目手动添加依赖,而且需要为当前项目手动添加配置信息,还需要为当前项目设计公共模块。

依赖是指当前项目所需的外部库或者模块,通常以jar包的形式存在。一个项目中可以包含多个依赖,这些依赖通过Maven等工具进行管理。

配置信息是用于为当前项目设置各种参数的信息,它通常被存储在配置文件中,以便在启动当前项目时读取并应用这些参数。

公共模块是一组预先构建的代码模块。这些模块可以被共享和复用,因此它们有助于提高开发效率,减少重复工作,并且使得系统的维护和扩展变得更加容易。

下面将依次对本项目后端的依赖配置和公共模块进行介绍。

1.6.1 添加依赖和配置信息

1.在pom.xml文件中添加依赖

因为本项目把Maven作为项目构建工具,而pom.xml文件是Maven构建项目的核心配置文件,所以需要在pom.xml文件中为本项目添加依赖,这些依赖会被添加到pom.xml文件中的<dependencies>标签内部。代码如下:

2.在application.yml文件中添加配置信息

本项目采用的是yml格式的配置文件,并在application.yml文件中添加如下配置信息:

1.6.2 工具类设计

将一些反复调用的代码封装成工具类,不仅可以提高开发效率,还可以提高代码的可读性。本项目具有两个工具类,分别是全局异常处理类和通用返回类。下面将分别介绍这两个类。

1.全局异常处理类

当一个Spring Boot项目没有对用户触发的异常进行拦截时,用户触发的异常就会触发最底层异常。在实际开发中,程序开发人员必须对最底层异常进行拦截。

拦截全局最底层异常的方式非常简单,只需在全局异常处理类中单独写一个“兜底”的、用于处理异常的方法,并使用@ExceptionHandler(Exception.class)注解予以标注。

本项目的全局异常处理类是com.mr.controller.util工具包下的ProjectExceptionAdvice类,该类的代码如下:

2.通用返回类

在实际开发过程中,需要编写很多个控制器。虽然在这些控制器中的方法各不相同,但是这些控制器的作用都是先让后端处理由前端发送的请求,再把由后端返回的结果传递给前端。程序开发人员习惯把由后端返回的所有结果都统一封装成一个类,并把这个类称作“通用返回类”,同时定义这个类为R类,这样由后端传递给前端的结果的类型就都是R类型了。也就是说,在控制器中,R类不仅接收了由后端处理的结果,而且被传递给前端,进而统一了返回的类型。

在本项目的R类中,包含了3个私有的属性,它们分别是表示Boolean型对象的bool、表示实体类对象的obj和表示字符串信息的str。为了方便外部类访问这3个私有的属性,需要为它们添加Getter/Setter方法。

此外,在R类中还包含了1个无参构造方法和4个有参构造方法,这4个有参构造方法分别为只含有Boolean型对象的bool的构造方法、含有Boolean型对象的bool和实体类对象的obj的构造方法、含有Boolean型对象的bool和字符串信息的str的构造方法、只含有字符串信息的str的构造方法。com.mr.controller.util工具包下的R类的代码如下:

1.6.3 实体类设计

实体类又称数据模型类。顾名思义,实体类是一种专门用于保存数据模型的类。每一个实体类都对应着一种数据模型,通常会将类的属性与数据表的字段相对应。虽然实体类的属性都是私有的,但是通过每一个属性的Getter/Setter方法,外部类就能够获取或修改实体类的某一个属性值。实体类通常都会提供无参构造方法,并根据具体情况确定是否提供有参构造方法。

本项目只有一个实体类,这个实体类对应的是com.mr.pojo包下的Bike.java文件,表示电瓶车类。电瓶车类中的品牌编号、品牌名称、品牌评分、好评率、品牌介绍这5个属性,与db_e-bike库的bike表中的5个字段相对应。为了方便外部类访问这5个私有的属性,需要为它们添加Getter/Setter方法。com.mr.pojo包下的Bike类代码如下:

1.6.4 DAO层设计

本项目的DAO层使用了MyBatisPlus。MyBatisPlus简称MP,是一个MyBatis的增强工具。MyBatisPlus在MyBatis的基础上只做增强不做改变,专为简化开发、提高开发效率而生。

何以体现MyBatisPlus能够简化开发、提高开发效率呢?当使用MyBatis时,在编写Mapper接口后,不仅需要手动编写对数据执行增、删、改、查等操作的方法,还需要手动编写与每个方法对应的SQL语句。例如,使用MyBatis读取t_people表中的数据,并把读取的数据封装在实体对象中。为此,创建PeopleMapper接口作为映射器,在映射器中实现以下3个业务:

向t_people表添加一个新人员,该人员的数据如下:小丽,女性,20岁;

将小丽的年龄修改为19岁;

删除小丽的所有数据。

PeopleMapper接口的代码如下:

当使用MyBatisPlus时,只需要创建Mapper接口并继承BaseMapper接口,此时当前的Mapper接口就会获得由BaseMapper接口提供的对数据执行增、删、改、查等操作的方法。也就是说,在创建Mapper接口后,既不需要手动编写对数据执行增、删、改、查等操作的方法,也不需要手动编写与每个方法对应的SQL语句,从而实现简化开发、提高开发效率的目的。在使用MyBatisPlus的情况下,可以将上述代码修改如下:

简而言之,当使用MyBatisPlus时,创建的Mapper接口是一个空接口。

本项目的BikeDao接口就是DAO层。因为BikeDao接口继承了BaseMapper接口,所以BikeDao接口是一个空接口。BikeDao接口的代码如下:

为了让读者能够更深入地理解BaseMapper接口,这里给出BaseMapper接口的相关代码:

1.7 分页插件模块设计

在主页面上,本项目通过分页插件显示数据总数、分页数、与某个页面对应的数据和分页导航等信息。程序分页显示10条数据,共分两页,每一页最多显示7条数据,用户通过分页导航可以随意切换并访问这两个分页的数据,如图1.5所示。下面将介绍分页插件模块的实现过程。

图1.5 分页插件的效果图

1.7.1 前端设计

本项目中的bikes.html即为主页面。在初始化主页面时,主页面还没有来得及从数据库中获取数据,此时数据总数为0,并且电瓶车的品牌名称、品牌评分、好评率和品牌介绍的值均为空的字符串。因此,在bikes.html中初始化分页插件相关数据的代码如下:

1.7.2 后端设计

com.mr.config包下的PageConfig类为分页插件配置类,这个类可以为本项目配置分页插件,进而分页显示与每个页面对应的数据。下面将介绍分页插件的出处及其配置过程。

在介绍分页插件的出处之前,首先了解一下MyBatis插件机制。所谓MyBatis插件机制,指的是MyBatis插件会拦截Executor、StatementHandler、ParameterHandler和ResultSetHandler这4个接口的方法,为了执行自定义的拦截逻辑,需要先利用JDK动态代理机制为这些接口的实现类创建代理对象,再执行代理对象的方法。

Executor:MyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过ResultSetHandler予以自动映射。

StatementHandler:MyBatis直接让数据库执行SQL脚本的对象。

ParameterHandler:MyBatis为了实现SQL带入参数而设置的对象。

ResultSetHandler:MyBatis把ResultSet集合映射成POJO的接口对象。

MyBatisPlus依据MyBatis插件机制,为程序开发人员提供了PaginationInnerInterceptor、BlockAttackInnerInterceptor、OptimisticLockerInnerInterceptor等常用的插件,以便在实际开发中使用。不难发现,这些插件都实现了InnerInterceptor接口。

PaginationInnerInterceptor:用于实现自动分页的插件。

BlockAttackInnerInterceptor:用于防止全表更新与删除的插件

OptimisticLockerInnerInterceptor:用于实现乐观锁的插件。

在明确分页插件的出处后,下面将介绍分页插件的配置过程。因为PageConfig类是分页插件配置类,所以须使用@Configuration注解标注PageConfig类。在PageConfig类中,有一个用于返回MybatisPlus插件对象的mybatisPlusInterceptor()方法。在这个方法中,首先创建一个MybatisPlus插件对象,然后让这个MybatisPlus插件对象实现自动分页的功能。com.mr.config包下的PageConfig类的代码如下:

1.8 查询电瓶车品牌信息模块设计

在db_e-bike库的bike表中一共有10条电瓶车的品牌信息,这些信息将被分页显示在主页面上。其中,主页面上的第1个分页显示了7条数据,如图1.6所示;主页面上的第2个分页显示了3条数据,如图1.7所示。下面将介绍查询电瓶车品牌信息模块的实现过程。

1.8.1 前端设计

如图1.6和图1.7所示,电瓶车的品牌信息都显示在了一个表格模型中,这个表格模型的表头分别为编号、品牌名称、品牌评分、好评率、品牌介绍和操作。在分页插件的作用下,这些信息会被分页显示在主页面上。在第1.7.1节中,分页插件的相关数据(如当前页码、每页显示的记录数、总记录数等)虽然已经被初始化,但是需要以电瓶车的品牌信息为依据予以重置。代码如下:

图1.6 主页面第1个分页的效果图

图1.7 主页面第2个分页的效果图

Vue.js在创建组件实例时会调用getAll()方法和handleCurrentChange()方法。getAll()方法用于分页查询,通过对db_e-bike库的bike表执行查询操作,获取其中所有的电瓶车品牌信息(10条),并以此为依据确定分页插件的“当前页码”“每页显示的记录数”和“总记录数”,即“当前页码”为1、“每页显示的记录数”为7、“总记录数”为10。因此,10条电瓶车品牌信息将被分页插件分为2页,第1个分页有7条数据,第2个分页有3条数据。handleCurrentChange()方法用于切换页面,既可以从第1个分页切换至第2个分页,也可以从第2个分页切换至第1个分页。代码如下:

1.8.2 后端设计

查询模块的后端设计主要包括查询模块的控制器类设计和服务类设计。下面将分别介绍如何设计查询模块的控制器类和服务类。

1.控制器类设计

本项目的BikeController类为控制器类,被@RestController注解标注。在BikeController类中,定义了两个方法,它们分别是getAll()方法和getPage()方法。其中,getAll()方法用于查询所有的电瓶车品牌信息;getPage()方法用于获取显示数据的某个分页。getAll()方法和getPage()方法的代码分别如下:

@GetMapping用于处理get请求,通常在查询数据时使用,@GetMapping的语法如下:

    @GetMapping("path")

@GetMapping等价于处理get请求的@RequestMapping,@RequestMapping的语法如下:

    @RequestMapping(value="path" , method=RequestMethod.GET)
2.服务类设计

本项目的BikeService接口为服务接口。在BikeService接口中,定义了一个用于获取显示数据的某个分页的getPage()方法。在getPage()方法中,有3个参数,它们分别为int类型的表示“当前页码”的currentPage、int类型的表示“每页显示的记录数”的pageSize、Bike类型的表示“电瓶车对象(内含电瓶车品牌信息)”的bike。此外,该方法具有返回值,返回值是显示“电瓶车对象(内含电瓶车品牌信息)”的某个分页。getPage()方法的代码如下:

    IPage<Bike> getPage(int currentPage, int pageSize, Bike bike);  //获取用于显示数据的某个分页

本项目的BikeServiceImpl类是BikeService接口的实现类,被@Service注解标注,即服务类。在BikeServiceImpl类中,重写了BikeService接口中的getPage()方法。该方法的主要作用是调用DAO层(数据访问对象)执行数据库操作。重写后的getPage()方法的代码如下:

在上述代码中,LambdaQueryWrapper是MyBatisPlus中的一个功能类,用于构建lambda表达式风格的查询条件,它提供了类型安全的条件构造器,可以减少编写字段名称的错误。

1.9 新增电瓶车品牌信息模块设计

如图1.8所示,在主页面的头部有一个“新增电瓶车品牌信息”按钮。用户单击这个按钮,程序将弹出“新增电瓶车品牌信息”窗口,如图1.9所示。在这个窗口中,用户依次输入电瓶车的品牌名称、品牌评分、好评率和品牌介绍等信息,单击“确定”按钮,完成新增电瓶车品牌信息的操作。下面将介绍新增电瓶车品牌信息模块的实现过程。

图1.8 主页面头部的效果图

图1.9 “新增电瓶车品牌信息”窗口的效果图

1.9.1 前端设计

bikes.html是本项目的主页面,因此在bikes.html的头部添加“新增电瓶车品牌信息”按钮,代码如下:

    <div class="filter-container">
        <el-button type="primary" class="butT" @click="handleCreate()">新增电瓶车品牌信息</el-button>
    </div>

用户单击“新增电瓶车品牌信息”按钮,程序将弹出“新增电瓶车品牌信息”窗口。在这个窗口中,包含4个标签、3个文本框、1个文本域、1个“取消”按钮和1个“确定”按钮。代码如下:

Vue.js在创建组件实例时会分别调用handleCreate()方法、resetForm()方法、handleAdd()方法和cancel()方法。其中,handleCreate()方法用于显示“新增电瓶车品牌信息”窗口;resetForm()方法用于重置表格模型中的数据;handleAdd()方法的作用是,用户在“新增电瓶车品牌信息”窗口中依次输入电瓶车的品牌名称、品牌评分、好评率和品牌介绍等信息,单击“确定”按钮,如果操作成功,那么表格模型中的数据将被重置,进而新增的电瓶车品牌信息会显示在第2个分页上;cancel()方法的作用是,如果用户单击窗口中的“取消”按钮,那么窗口将被关闭,并弹出“当前操作取消”的信息。代码如下:

1.9.2 后端设计

新增模块的后端设计主要包括新增模块的控制器类设计和服务类设计。下面将分别介绍如何设计新增模块的控制器类和服务类。

1.控制器类设计

在BikeController类(控制器类)中,定义了一个insert()方法,该方法用于判断是否成功地执行了新增电瓶车品牌信息的操作。如果操作成功,就返回“添加成功”信息;否则,就返回“添加失败”信息。insert()方法的代码如下:

@PostMapping用于处理post请求,通常在新增数据时使用,@PostMapping的语法如下:

    ?@PostMapping("path")

@PostMapping等价于处理post请求的@RequestMapping,@RequestMapping的语法如下:

    @RequestMapping(value="path", method=RequestMethod.POST)
2.服务类设计

在BikeService接口(服务接口)中,定义了一个insertBike()方法,该方法用于判断是否成功地执行了新增电瓶车品牌信息的操作。在insertBike()方法中,有1个参数,即Bike类型的表示“电瓶车对象(内含电瓶车品牌信息)”的bike。此外,该方法具有返回值,返回值是一个布尔值。insertBike()方法的代码如下:

    boolean insertBike(Bike bike);  //是否执行新增电瓶车品牌信息的操作

在BikeServiceImpl类(服务类)中,重写了BikeService接口中的insertBike()方法。该方法的主要作用是调用DAO层(数据访问对象)执行数据库操作。重写后的insertBike()方法的代码如下:

1.10 删除电瓶车品牌信息模块设计

如图1.6和图1.7所示,在主页面上显示的每一条数据的后面,都有一个“删除”按钮,用户先单击某一个“删除”按钮,再单击删除提示窗口中的“确定”按钮(如图1.10所示),程序将删除与这个“删除”按钮对应的电瓶车品牌信息,以完成删除电瓶车品牌信息的操作。下面将介绍删除电瓶车品牌信息模块的实现过程。

图1.10 删除提示窗口的效果图

1.10.1 前端设计

在第1.8.1节中,已经设计完成了表格模型中的表头。在表头操作中,已经添加了“删除”按钮。为了让“删除”按钮发挥作用,Vue.js在创建组件实例时会调用handleDelete()方法:用户在单击“删除”按钮后,如果继续单击删除提示窗口中的“确定”按钮,程序将删除与这个“删除”按钮对应的电瓶车品牌信息,并且表格模型中的数据会被重置;如果单击删除提示窗口中的“取消”按钮,那么窗口将被关闭,并弹出“取消操作”的信息。代码如下:

1.10.2 后端设计

删除模块的后端设计主要包括删除模块的控制器类设计和服务类设计。下面将分别介绍如何设计删除模块的控制器类和服务类。

1.控制器类设计

在BikeController类(控制器类)中,定义了一个用于根据电瓶车的品牌编号删除电瓶车品牌信息的delete()方法。delete()方法的代码如下:

@DeleteMapping用于处理delete请求,通常在删除数据时使用,@DeleteMapping的语法如下:

    @DeleteMapping("path")

@GetMapping等价于处理delete请求的@RequestMapping,@RequestMapping的语法如下:

    @RequestMapping(value="path",method=RequestMethod.DELETE)
2.服务类设计

在BikeService接口(服务接口)中,定义了一个deleteBike()方法,该方法用于判断是否成功地执行了删除电瓶车品牌信息的操作。在insertBike()方法中,有1个参数,即Integer类型的表示“电瓶车的品牌编号”的id。此外,该方法具有返回值,返回值是一个布尔值。deleteBike()方法的代码如下:

    boolean deleteBike(Integer id);                                    //是否执行删除电瓶车品牌信息的操作

在BikeServiceImpl类(服务类)中,重写了BikeService接口中的deleteBike()方法。该方法的主要作用是调用DAO层(数据访问对象)来执行数据库操作。重写后的deleteBike()方法的代码如下:

1.11 项目运行

通过前述步骤,我们设计并完成了“电瓶车品牌信息管理系统”项目的开发。下面运行本项目,检验一下我们的开发成果。如图1.11所示,在IntelliJ IDEA中,单击 快捷图标,即可运行本项目。

图1.11 IntelliJ IDEA的快捷图标

成功运行本项目,打开浏览器,访问http://localhost:8080/pages/bikes.html,即可看到如图1.12所示的电瓶车品牌信息管理系统的主页面。

主页面一共显示10条数据。因为一个分页只能显示7条数据,所以需要两个分页,即第1个分页显示7条数据,第2个分页显示3条数据。用户通过分页导航可以随意切换并访问这两个分页的数据。

用户在单击“新增电瓶车品牌信息”按钮后,需要先在弹出的窗口中依次输入电瓶车的品牌名称、品牌评分、好评率和品牌介绍等信息,再单击“确定”按钮,进而完成新增电瓶车品牌信息的操作。

在主页面上显示的每一条数据的后面,都有一个“删除”按钮,用户先单击某一个“删除”按钮,再单击删除提示窗口中的“确定”按钮,进而完成删除电瓶车品牌信息的操作。

图1.12 电瓶车品牌信息管理系统的主页面

这样,我们就成功地检验了本项目的运行。

本项目比较简单,虽然只应用了单表查询,但是麻雀虽小,五脏俱全。本项目使用Vue.js实现了前端页面、使用Spring Boot实现了后端业务逻辑。Vue.js在创建组件实例时会调用特定的方法,进而达到渲染页面的目的。Spring Boot在处理业务逻辑时层次分明、结构清晰,Controller层主要负责具体的业务模块流程的控制,Service层主要负责业务模块的逻辑应用设计,DAO层主要负责与数据库进行联络。 yo3AQttulzQesHGHzrY3T2tTiJdt4xVtSlnBrb9w3EzI2rMeTx3PPtm8Q7pD9U8G

点击中间区域
呼出菜单
上一章
目录
下一章
×