Bootstrap

SpringBoot+Vue全栈开发--学习笔记(更新ing)

 转自:2.SpringBoot快速上手_哔哩哔哩_bilibili

目录

 转自:2.SpringBoot快速上手_哔哩哔哩_bilibili

基础知识

B/S

Maven 

SpringBoot 

创建项目 

填写项目信息

项目基本信息

运行项目

运行结果

开发环境热部署

1、在pom.xml配置文件中添加dev-tools依赖

2、 在application.properties中配置devtools

3、结果展示

Web入门

控制器

 @Controller的用法

@RestController的用法

路由映射

Method匹配

参数传递

举例分析1

举例分析2

http://127.0.0.1/postTest1

http://127.0.0.1/postTest2

http://127.0.0.1/postTest3

http://127.0.0.1/postTest4

通配符http://127.0.0.1/test

通配符htpp://127.0.0.1/test1

web开发进阶

静态资源访问

文件上传

SpirngBoot实现文件上传功能

拦截器

拦截器定义 

拦截器注册

构建RESTful服务

RESTful介绍构建

RESTful特点

RESTful应用接口使用

RESTful API

RESTful Method

HTTP状态码

SpringBoot 实现RESTful API

实例 

Swagger

使用Swagger生成Web API文档

使用 Swagger2 进行接口测试

Swagger常用注解

MyBatisPlus快速上手

ORM介绍

MyBatis-Plus介绍

全局配置

MyBatis-Plus CRUD操作

MyBatis-Plus CRUD注解

CRUD操作

实例

结果展示

查询用户

插入用户(使用工具postman)

简化操作

Mybatisplus官网  注解 | MyBatis-Plus

多表及分页查询

多表查询

实例分析

数据库信息

实体类

控制类

接口类

结果展示

条件查询

分页查询

编写配置文件

控制类(userController)

SpringBoot项目分享

VUE框架快速上手

前端环境准备

Vue框架介绍

MVVM模式

Vue快速入门

官网链接简介 | Vue.js

通过 CDN 使用 Vue

01内容渲染指令

02属性绑定指令

03使用javaScript表达式

04事件绑定指令

05条件渲染指令

06列表渲染指令

07v-for的key属性

​编辑组件化开发

NPM使用

​​​​NPM简介

Nodejs安装

NPM使用

Vue CLI使用

项目创建

组件化开发

组件的构成

实例

第三方组件element-ui

组件间的传值

实例

element-ui介绍

组件的使用

图标的使用

Axios网络请求

Axios的使用

Axios简介

发送网络请求

发送get请求

发送post请求

 异步回调问题

其他请求方式

与Vue整合

跨域

为什么会出现跨域问题

跨域问题解决

简单请求

非简单请求

Spring Boot中配置CORS

实例

前端路由VueRouter 

VueRouter安装与使用

参数传递

创建路由组件

声明路由链接和占位标签

创建路由模块

子路由

嵌套路由

动态路由

导航守卫

状态管理VueX

Vuex介绍

状态管理

State

Mutation

Action

Getter

Vuex安装与使用

前端数据模拟MockJS 

mockjs介绍

mockjs基本使用

核心方法

数据生成规则

​编辑 企业级后台集成方案

vue-element-admin介绍

安装与使用

源码解读

JWT跨域认证

Session认证

Token认证

JWT的使用

 Header

Payload

Signature

JWT的特点

JWT的实现

前后端集成


基础知识

B/S

在BS架构下,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器,获取Web页面,并把Web页面展示给用户即可。


Maven 

Maven 是一个项目管理工具,可以对 Java 项目进行自动化的构建和依赖管理

 

Maven的作用可以分成三类:
项目构建:提供标准的,跨平台的自动化构建项目的方式
依赖管理:方便快捷的管理项目依赖的资源 (jar包),避免资源间的版本冲突等问题
统一开发结构:提供标准的,统一的项目开发结构,如下图所示


SpringBoot 

Spring Boot是由Pivotal团队提供的基于Spring的全新框架,旨在简化Spring应用的初始搭建和开发过程。
Spring Boot是所有基于Spring开发项目的起点
Spring Boot就是尽可能地简化应用开发的门槛,让应用开发、测试、部署变得更加简单。

SpringBoot特点
1.遵循“约定优于配置”的原则,只需要很少的配置或使用默认的配置

2.能够使用内嵌的Tomcat、Jetty服务器,不需要部署war文件提供定制化的启动器Starters,简化Maven配置,开箱即用。

3.纯Java配置,没有代码生成,也不需要XML配置
4.提供了生产级的服务监控方案,如安全监控、应用监控、健康检测等。


创建项目 

填写项目信息

Group:一般输入公司域名

Artifact:项目名称。

 创建项目成功后,记得查看settings-->Build, Execution, Deployment-->Build Tools->Meaven

 

项目基本信息

 


运行项目

新建controller类

package com.example.hellotest.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
//控制器可以接受客户端请求
public class HelloController {

    //http://127.0.0.1:8080/hello
    @GetMapping("/hello")
    //浏览器以get的请求方式访问 hello()
    public  String hello(){
        return "hello world";
    }
}

运行结果

 如果代码进行了修改,则需要重启项目才能生效

开发环境热部署可以解决这一问题。


开发环境热部署

1、在实际的项目开发调试过程中会频繁地修改后台类文件,导致需要重新编译.重新启动,整个过程非常麻烦,影响开发效率
2、Spring Boot提供了spring-boot-devtools组件,使得无须手动重启SpringBoot应用即可重新编译、启动项目,大大缩短编译启动的时间。
3、devtools会监听classpath下的文件变动,触发Restart类加载器重新加载该类从而实现类文件和属性文件的热部署。
4、可以通过设置并不是所有的更改都需要重启应用(如静态资源、视图模板),spring.devtoolsrestart.exclude属性来指定一些文件或目录的修改不用重启应用

1、在pom.xml配置文件中添加dev-tools依赖

使用optional=true表示依赖不会传递,即该项目依赖devtools;其他项目如果引入此项目生成的JAR包,则不会包含devtools

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

2、 在application.properties中配置devtools

#热部署生效
spring.devtools.restart.enabled=true

#设置重启目录
spring.devtools.restart.additional-paths=src/main/java

#设置cLasspath目录下的WEB-INF文件夹内容修改不重启
spring.devtools.restart.exclude=static/**

如果使用了Eclipse,那么在修改完代码并保存之后,项目将自动编译并触发重启,而如果使用了IntelliJIDEA,还需要配置项目自动编译。

打开Settings页面,在左边的菜单栏依次找到Build,Execution,Deployment-Compile,勾选Build project automatically

Ctrl+Shift+Alt+/快捷键调出Maintenance页面,单击Registry,勾选compiler.automake.allow.when.app.running复选框,重启项目

做完这两步配置之后,若开发者再次在IntelliJIDEA中修改代码,则项目会自动重启。

3、结果展示

项目自动重启,提升开发效率


Web入门

Spring Boot将传统Web开发的mvc、json、tomcat等框架整合,提供了spring-boot-starter-web组件,简化了Web应用配置。

创建SpringBoot项目勾选Spring Web选项后,会自动将spring-boot-starter-web组件加入到项目中。


spring-boot-starter-web启动器主要包括web、webmvc、json、tomcat等基础依赖组件,作用是提供Web开发场景所需的所有底层依赖。


webmvc为Web开发的基础框架,json为JSON数据解析组件,tomcat为自带的容器依赖。

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

控制器

Spring Boot提供了@Controller和@RestController两种注解来标识此类负责接收和处理HTTP请求。
如果请求的是页面和数据,使用@Controller注解即可;

如果只是请求数据则可以使用@RestController注解。

 @Controller的用法

示例中返回了hello页面和name的数据,在前端页面中可以通过$(name)参数获取后台返回的数据并显示。
@Controller通常与Thymeleaf模板引擎结合使用

@RestController的用法

默认情况下,@RestController注解会将返回的对象数据转换为JSON格式。

路由映射

解决如何接收浏览器请求问题

@RequestMapping注解主要负责URL的路由映射。它可以添加在Controller类或者具体的方法上。


如果添加在Controller类上,则这个Controller中的所有路由映射都将会加上此映射规则,如果添加在方法上,则只对当前方法生效。


@RequestMapping注解包含很多属性参数来定义HTTP的请求映射规则。常用的属性参数如下:
        value:请求URL的路径,支持URL模板、正则表达式

        method:HTTP请求方法

        consumes:请求的媒体类型(Content-Type) ,如application/json

        produces: 响应的媒体类型params,

        headers: 请求的参数及请求头的值

@RequestMapping的value属性用于匹配URL映射,value支持简单表达式@RequestMapping("/user")


@RequestMapping支持使用通配符匹配URL,用于统一映射某些URL规则类似的请求: @RequestMapping("/getJson/*json"),当在浏览器中请求/getJson/a.json或者/getJson/b.json时都会匹配到后台的Json方法

@RequestMapping的通配符匹配非常简单实用,支持“*”,“**”,“?”等通配符。

符号“*”匹配任意字符,符号“**”匹配任意路径,符号“?”匹配单个字符。

有通配符的优先级低于没有通配符的,比如/user/add.json比/user/*.json优先匹配。


有“**”通配符的优先级低于有“*”通配符的。

Method匹配

HTTP请求Method有GET、POST、PUT、DELETE等方式。HTTP支持的全部
Method


@RequestMapping注解提供了method参数指定请求的Method类型,包括RequestMethod.GET、RequestMethod.POST、RequestMethod.DELETE、RequestMethod.PUT等值,分别对应HTTP请求的Method

Method匹配也可以使用@GetMapping、@PostMapping等注解代替

参数传递

@RequestParam将请求参数绑定到控制器的方法参数上,接收的参数来自HTTP请求体或请求url的QueryString,当请求的参数名称与Controller的业务方法参数名称一致时,@RequestParam可以省略


@PathVaraible: 用来处理动态的URL,URL的值可以作为控制器中处理方法的参数


@RequestBody接收的参数是来自requestBody中,即请求体。

一般用于处理非Content-Type: application/x-www-form-urlencoded编码格式的数据比如: application/json、application/xml等类型的数据

举例分析1
public class HelloController {

    //http://127.0.0.1:8080/hello
    //http://127.0.0.1/hello?name=zhangsan&phone=123  参数传递
    @GetMapping("/hello")
    //浏览器以get的请求方式访问 hello()
    public  String hello(String name,String phone){
        return "hhh"+name+phone;
    }
}

举例分析2

源代码

package com.example.hellotest.controller;

import com.example.hellotest.entity.User;
import org.springframework.web.bind.annotation.*;

@RestController
public class ParamController {

    @RequestMapping(value = "/getTest1", method = RequestMethod.GET)
    public String getTest1() {
        return "Get请求";
    }

    @RequestMapping(value = "/getTest2", method = RequestMethod.GET)
    //http://127.0.0.1/getTest2?name=zhangsan&phone=13999999999
    public String getTest2(String name, String phone) {
        System.out.println("name" + name);
        System.out.println("phone" + phone);
        return "Get请求" + name + phone;
    }

    @RequestMapping(value = "/getTest3", method = RequestMethod.GET)
    //http://127.0.0.1/getTest3?nikename=zhangsan
    public String getTest3(@RequestParam(value = "name", required = false) String name) {
        /*参数名对应不上(nikename与name),可加注解@RequestParam(参数映射),加上该注解后必须要传递参数,否则浏览器将报400错误
        (@RequestParam(value = "name") String name)

        加上required=false,即为可选,及不传递参数也可访问http://127.0.0.1/getTest3,不会报错
        (@RequestParam(value = "name",required = false) String name)

         */
        System.out.println("name" + name);
        return "Get请求";
    }

    @RequestMapping(value = "/postTest1", method = RequestMethod.POST)
    public String postTest1(){
        return "Post请求";
    }

    @RequestMapping(value = "/postTest2",method = RequestMethod.POST)
    public  String postTest2(String name){
        System.out.println("name:"+name);
        return "Post请求";
    }


    @RequestMapping(value = "/postTest3",method = RequestMethod.POST)
    public  String postTest3(User user){
        //将参数封装在一个对象(user)里面
        System.out.println(user);
        return "Post请求";
    }

    @RequestMapping(value = "/postTest4",method = RequestMethod.POST)
    public  String postTest4(@RequestBody User user){
        //将参数封装在一个对象(user)里面,传递的是json格式,需要注解@RequestBody
        System.out.println(user);
        return "Post请求";
    }

    @GetMapping("/test/**")
    public String test(){
        return "通配符请求";
    }

    @GetMapping("/test1/*")
    public String test1(){
        return "通配符请求";
    }

}

使用工具 postman

http://127.0.0.1/postTest1

http://127.0.0.1/postTest2

修改请求体类型。将数据放入body里面

若想要在url显示出来

http://127.0.0.1/postTest3

http://127.0.0.1/postTest4

通配符http://127.0.0.1/test

通配符htpp://127.0.0.1/test1

web开发进阶

静态资源访问

使用IDEA创建Spring Boot项目,会默认创建出classpath:/static/目录,静态资源一般放在这个目录下即可。


如果默认的静态资源过滤策略不能满足开发需求,也可以自定义静态资源过滤策略。

在applicationproperties中直接定义过滤规则和静态资源位置
spring.mvc.static-path-pattern=/static/**

spring.web.resources.static-locations=classpath:/static


过滤规则为/static/**,静态资源位置为classpath:/static/

直接访问http://127.0.0.1/1.png即可

若不想在直接在更目录下进行访问

方法1:


方法2:自己创建目录

spring.web.resources.static-locations=classpath:/css


文件上传

表单的enctype属性规定在发送到服务器之前应该如何对表单数据进行编码

当表单的enctype="application/x-www-form-urlencoded”(默认)时form表单中的数据格式为: key=value&key=value

当表单的enctype="multipart/form-data"时,其传输数据形式如下

SpirngBoot实现文件上传功能

Spring Boot工程嵌入的tomcat限制了请求的文件大小,每个文件的配置最大为1Mb,

单次请求的文件的总数不能大于10Mb。


要更改这个默认值需要在配置文件(如application.properties) 中加入两个配置


spring.servlet.multipart.max-file-size=10MB

spring.servlet.multipart.max-request-size=10MB

当表单的enctype="multipart/form-data"时,可以使用MultipartFile 获取上传的文件数据,再通过transferTo方法将其写入到磁盘中
源代码

package com.example.hellotest.controller;

import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletMapping;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;

@RestController
public class FileController {
    //private static final String UPLOADED_FOLDER=System.getProperty("user.dir")+"/upload/";

    @PostMapping("/upload")
    public String upload(String name , MultipartFile file , HttpServletRequest request) throws IOException {
        System.out.println(name);
        System.out.println("文件大小:"+file.getSize());
        //获取图片类型
        System.out.println(file.getContentType());
        //获取图片原始名称
        System.out.println(file.getOriginalFilename());
        //获取web服务器的运行目录
        String path =request.getServletContext().getRealPath("/upload/");
        System.out.println(path);
        saveFile(file,path);
        return "上传成功";

    }

    public void saveFile(MultipartFile file, String path) throws IOException {
        File upDir=new File(path);
        //判断目录是否存在,不存在则创建
        if (!upDir.exists()){
            upDir.mkdir();
        }
        //
        File file1=new File(path+file.getOriginalFilename());
        file.transferTo(file1);//传入磁盘
    }
}

结果展示

若需要在浏览器内访问,加入以下语句

spring.web.resources.static-locations=/upload/

/*
    注意查看代码,不要直接复制,尽量手敲
    我的图片一直没显现出来。结果发现实语句写错了
    我的是 
    spring.resources.static-locations=/upload/
*/


拦截器

拦截器在Web系统中非常常见,对于某些全局统一的操作,我们可以把它提取到拦截器中实现。

总结起来,拦截器大致有以下几种使用场景:

权限检查:如登录检测,进入处理程序检测是否登录,如果没有,则直接返回登录页面。
性能监控:有时系统在某段时间莫名其妙很慢,可以通过拦截器在进入处理程序之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间

通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有提取Locale、Theme信息等,只要是多个处理程序都需要的,即可使用拦截器实现

Spring Boot定义了Handlerlnterceptor接口来实现自定义拦截器的功能

Handlerlnterceptor接口定义了preHandle、postHandle、afterCompletion三种方法,通过重写这三种方法实现请求前、请求后等操作

拦截器定义 

拦截器注册

addPathPatterns方法定义拦截的地址

excludePathPatterns定义排除某些地址不被拦截添加的一个拦截器没有

addPathPattern任何一个url则默认拦截所有请求如果没有

excludePathPatterns任何一个请求,则默认不放过任何一个请求

 源代码

package com.example.hellotest.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler ) throws Exception {
        System.out.println("LoginInterceptor");
        return false;
    }
}
package com.example.hellotest.config;

import com.example.hellotest.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/user/**");
    }
}

结果显示,访问hello不拦截,访问user则拦截


构建RESTful服务

RESTful介绍构建

RESTful是目前流行的互联网软件服务架构设计风格
REST (Representational State Transfer,表述性状态转移)一词是由RoyThomas Fielding在2000年的博士论文中提出的,它定义了互联网软件服务的架构原则,如果一个架构符合REST原则,则称之为RESTful架构。
REST并不是一个标准,它更像一组客户端和服务端交互时的架构理念和设计原则,基于这种架构理念和设计原则的Web API更加简洁,更有层次。

RESTful特点

1、每一个URI代表一种资源
2、客户端使用GET、POST、PUT、DELETE四种表示操作方式的动词对服务端资源进行操作:GET用于获取资源,POST用于新建资源(也可以用于更新资源),PUT用于更新资源,DELETE用于删除资源
3、通过操作资源的表现形式来实现服务端请求操作
4、资源的表现形式是JSON或者HTML

5、客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都包含必需的信息。

RESTful应用接口使用

RESTful API

符合RESTful规范的Web API需要具备如下两个关键特性

安全性:安全的方法被期望不会产生任何副作用,当我们使用GET操作获取资源时,不会引起资源本身的改变,也不会引起服务器状态的改变()
幂等性: 幂等的方法保证了重复进行一个请求和一次请求的效果相同(并不是指响应总是相同的,而是指服务器上资源的状态从第一次请求后就不再改变了),在数学上幂等性是指N次变换和一次变换相同(对一个接口发送多次请求或一次请求获取的资源应该是一样的

RESTful Method

HTTP提供了POST、GET、PUT、DELETE等操作类型对某个Web资源进行Create、Read、Update和Delete操作。
个HTTP请求除了利用URI标志目标资源之外,还需要通过HTTP Method指定针对该资源的操作类型,一些常见的HTTP方法及其在RESTful风格下的使用

HTTP状态码

HTTP状态码就是服务向用户返回的状态码和提示信息,客户端的每一次请求服务都必须给出回应,回应包括HTTP状态码数据两部分
HTTP定义了40个标准状态码,可用于传达客户端请求的结果。状态码分为以下5个类别:


1xx:信息,通信传输协议级信息
2xx:成功,表示客户端的请求已成功接受
3xx:重定向,表示客户端必须执行一些其他操作才能完成其请求

4xx:客户端错误,此类错误状态码指向客户端
5xx:服务器错误,服务器负责这写错误状态码

RESTfulAPI中使用HTTP状态码来表示请求执行结果的状态,适用于RESTAPI设计的代码以及对应的HTTP方法。(了解)


SpringBoot 实现RESTful API

Spring Boot提供的spring-boot-starter-web组件完全支持开发RESTfulAP提供了与REST操作方式(GET、POST、PUT、DELETE)对应的注解
@GetMapping:处理GET请求,获取资源

@PostMapping: 处理POST请求,新增资源.
@PutMapping:处理PUT请求,更新资源
@DeleteMapping: 处理DELETE请求,删除资源
@PatchMapping:处理PATCH请求,用于部分更新资源

在RESTful架构中,每个网址代表一种资源,所以URI中建议不要包含动词,只包含名词即可,而且所用的名词往往与数据库的表格名对应。用户管理模块API示例


实例 
//注意,前面对/user/路径的拦截器需要放行,return true即可
@RestController
public class UserController {
    @GetMapping("/user/{id}")
    public String getUserById(@PathVariable int id){
        //因为要动态显示ID,所以加上注解@PathVariable
        System.out.println(id);
        return "根据用户ID获取信息";
    }
    @PostMapping("/user")
    public String save(User user){
        return "添加用户";
    }
    @PutMapping("/user")
    public String put(User user){
        return "更新用户";
    }
    @DeleteMapping("/user/{id}")
    public String deleteUserById(@PathVariable int id){
        System.out.println(id);
        return "根据用户ID删除信息";
    }
}

删除用户测试(使用工具postman)


Swagger

Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务,是非常流行的API表达工具
Swagger能够自动生成完善的RESTfulAPI文档同时并根据后台代码的修改同步更新,同时提供完整的测试页面来调试API

使用Swagger生成Web API文档

在Spring Boot项目中集成Swagger同样非常简单,只需在项目中引入springfox-swagger2springfox-swagger-ui依赖即可。

<!--        添加swagger2相关功能-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
<!--        添加swagger-ui相关功能-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

配置类

@Configuration  //告诉spring容器,只是一个配置类
@EnableSwagger2 //启用swagger2功能
public class SwaggerConfig {
    /**
     * 配置swagger2的相关bean
     */
    @Bean
    public Docket createRestApi(){
        return  new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com"))
                //com包下所有API交给swagger管理(com.example.hellotest)
                .paths(PathSelectors.any())
                .build();
    }

    /**
     *此处主要是API文档页面显示信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("演示项目API")//标题
                .description("演示项目")//描述
                .version("1.0")//版本
                .build();
    }

}
使用 Swagger2 进行接口测试

启动项目访问 http://127.0.0.1/swagger-ui.html,即可打开自动生成的可视化测试页面

Swagger常用注解

Swagger提供了一系列注解来描述接口信息,包括接口说明、请求方法、请求参数、返回信息等

MyBatisPlus快速上手

ORM介绍

ORM(Object Relational Mapping,对象关系映射)是为了解决面向对象与关系数据库存在的互不匹配现象的一种技术。
ORM通过使用描述对象和数据库之间映射的元数据将程序中的对象自动持久化到关系数据库中。
ORM框架的本质是简化编程中操作数据库的编码

MyBatis-Plus介绍

MyBatis是一款优秀的数据持久层ORM框架,被广泛地应用于应用系统
MyBatis能够非常灵活地实现动态SQL,可以使用XML或注解来配置和映射原生信息,能够轻松地将Java的POJO (Plain Ordinary Java Object,普通的Java对象)与数据库中的表和字段进行映射关联。
MyBatis-Plus是一个MyBatis 的增强工具,在MyBatis 的基础上做了增强简化了开发

全局配置

添加依赖

<!--        MyBatisPlus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!--       mysql驱动依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!--        数据连接池 druid(一次性申请多个连接,提高数据库连接效率)-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.20</version>
        </dependency>

配置数据库相关信息

server.port=80

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#数据库:mydb
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false 
spring.datasource.username=root
spring.datasource.password=密码
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#我理解的是在控制台打印相关的日志

添加@MapperScan注解

@MapperScan("复制自己的Mapper路径")

MyBatis-Plus CRUD操作

MyBatis-Plus CRUD注解

CRUD操作


实例

数据库信息

注意:数据库内id要设为递增

控制类(UserController)核心代码

@Autowired
    //将实例化的对象自动注入
  private UserMapper userMapper; 

@GetMapping("/user")
    public List query(){

        List<User> list= userMapper.find();
        System.out.println(list);
        return list;
    }

    @PostMapping("/user")
    public  String save(User user){
        int i=userMapper.insert(user);
        if(i>0){
            return "插入成功";
        }else{
            return "插入失败";
        }
    }

UseMapper核心代码

@Mapper
public interface UserMapper  {
    //查询所有用户
    @Select("select * from t_user")
    public List<User> find();

    //插入
    @Insert("insert into t_user values (#{id}, #{username}, #{password}, #{birthday}) ")
    public  int insert(User user);

}

实体类(User)

@TableName("t_user")
public class User {
    @TableId(type = IdType.AUTO)
    private int id;
    private String username;
    private String password;
    private String birthday;


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public List<Order> getOrders() {
        return orders;
    }

    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", birthday='" + birthday +
                '}';
    }
}
结果展示
查询用户

插入用户(使用工具postman)

简化操作

接口类(UseMapper)

@Mapper
public interface UserMapper extends BaseMapper<User> {

}
@GetMapping("/user")
    public List query(){

        List<User> list= userMapper.selectList(null);
        System.out.println(list);
        return list;
    }

    @PostMapping("/user")
    public  String save(User user){
        int i=userMapper.insert(user);
        System.out.println(user);
        if(i>0){
            return "插入成功";
        }else{
            return "插入失败";
        }
    }

控制类(UseController)

@GetMapping("/user")
    public List query(){

        List<User> list= userMapper.selectList(null);
        System.out.println(list);
        return list;
    }

    @PostMapping("/user")
    public  String save(User user){
        int i=userMapper.insert(user);
        System.out.println(user);
        if(i>0){
            return "插入成功";
        }else{
            return "插入失败";
        }
    }

Mybatisplus官网  注解 | MyBatis-Plus


多表及分页查询

多表查询

实现复杂关系映射,可以使用@Results注解,@Result注解,@One注解@Many注解组合完成复杂关系的配置。

实例分析
数据库信息

实体类

user

@TableName("t_user")
public class User {
    @TableId(type = IdType.AUTO)
    private int id;
    private String username;
    private String password;
    private String birthday;

    //描述所有用户订单  告诉mybatisplus不存在order字段。否则my..plus会将所有的字段进行查询,出现报错
    //查询sql语句为:select id ,username.password,birthday,orders from t_user;
    @TableField(exist = false)
    private List<Order> orders;


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public List<Order> getOrders() {
        return orders;
    }

    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", birthday='" + birthday + '\'' +
                ", orders=" + orders +
                '}';
    }
}

order 

@TableName("t_order")
public class Order {
    @TableId(type = IdType.AUTO)
    private int id;
    private String order_time;
    private String total;
    private int uid;

    @TableField(exist = false)
    private User user;//一个订单只有一个用户

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getOrder_time() {
        return order_time;
    }

    public void setOrder_time(String order_time) {
        this.order_time = order_time;
    }

    public String getTotal() {
        return total;
    }

    public void setTotal(String total) {
        this.total = total;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", order_time='" + order_time + '\'' +
                ", total='" + total + '\'' +
                ", uid=" + uid +
                ", user=" + user +
                '}';
    }
}
控制类

usercontroller

@RestController
public class UserController {

    @Autowired
    //将实例化的对象自动注入
    private UserMapper userMapper;

    @GetMapping("/user/findAll")
    public List<User> find(){
        return userMapper.selectAllUsersAndOrders();
    }

}

ordercontroller

@RestController
public class OrderController {

    @Autowired
    private OrderMapper orderMapper;

    @GetMapping("order/findAll")
    public List findAll(){
        List orders=orderMapper.selectAllOrdersAndUsers();
        return  orders;
    }

}
接口类

usemapper

    //根据用户id查询信息
    @Select(" select * from t_user where id =#{id}")
    User selectById(int id);

    //查询所有用户及其所有订单
    @Select("select * from t_user")
    @Results(
            //结果集映射
            {
                    @Result(column = "id",property = "id"),
                    //给每一个字段进行赋值  column代表表里面的字段   Property代表类里面的字段
                    @Result(column = "username",property = "username"),
                    @Result(column = "password",property = "password"),
                    @Result(column = "birthday",property = "birthday"),
                    @Result(column = "id",property = "orders",javaType = List.class,
                            //orders 的类型是一个list ,根据用户id查询订单
                            many = @Many(select = "com.example.mybatisplus_test.mapper.OrderMapper.selectByUid")
                            //many 一个用户有多个订单。@many查询多个数据
                    )

            }
    )
    List<User> selectAllUsersAndOrders();

ordermapper

@Mapper
public interface OrderMapper extends BaseMapper<Order> {

    @Select(" select * from t_order where uid =#{uid}")
    List<Order> selectByUid(int uid);

    //查询所有订单及其用户
    @Select("select * from t_order")
    @Results(
            //结果集映射
            {
                    @Result(column = "id",property = "id"),
                    //给每一个字段进行赋值  column代表表里面的字段   Property代表类里面的字段
                    @Result(column = "order_time",property = "order_time"),
                    @Result(column = "total",property = "total"),
                    @Result(column = "uid",property = "user",javaType = User.class,
                            //orders 的类型是一个list ,根据用户id查询订单
                            one = @One(select = "com.example.mybatisplus_test.mapper.UserMapper.selectById")

                    )

            }
    )
    List<Order> selectAllOrdersAndUsers();

}
结果展示

火狐上看视觉效果更佳

http://127.0.0.1/user/findAll

http://127.0.0.1/order/findAll

条件查询

在于sql语句的书写

 @Select(" select * from t_order where uid =#{uid}")
    List<Order> selectByUid(int uid);
 //条件查询
    @GetMapping("/user/find")
    public List<User> findByCond(){
        QueryWrapper<User> queryWrapper=new QueryWrapper();
        queryWrapper.eq("username","test");
        return userMapper.selectList(queryWrapper);
    }

分页查询

编写配置文件
@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor paginationInterceptor(){
        MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();//创建了一个mybatisplus的拦截器
        PaginationInnerInterceptor paginationInterceptor=new PaginationInnerInterceptor(DbType.MYSQL);//分页拦截器
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }

}
控制类(userController)
  //分页查询
    @GetMapping("/user/findByPage")
    public IPage findByPage(){
        //设置起始值及每页条数
        Page<User> page=new Page<>(0,2);
        IPage iPage=userMapper.selectPage(page,null);//描述结果集
        return iPage;
    }

SpringBoot项目分享

链接:https://pan.baidu.com/s/1xsbWsoHQ_sRqP-u-A-zovA?pwd=3e7Y 
提取码:3e7Y 
--来自百度网盘超级会员V1的分享


VUE框架快速上手

前端环境准备

        编码工具:VSCode

        依赖管理:NPM

        项目构建:VueCli

(        需要有html、css、js的基础)

Vue框架介绍

Vue(读音/vju:/,类似于 view)是一套用于构建用户界面的渐进式框架

Vue.js提供了MVVM数据绑定和一个可组合的组件系统,具有简单、灵活的API.

其目标是通过尽可能简单的API实现响应式的数据绑定和可组合的视图组件

MVVM模式

MVVM是Model-View-ViewModel的缩写,它是一种基于前端开发的架构模式,其核心是提供对View和ViewModel的双向数据绑定


Vue提供了MVVM风格的双向数据绑定,核心是MVVM中的VM,也就是ViewModel,ViewModel负责连接View和Model,保证视图和数据的一致性

Vue快速入门

导入 vue.js的 script 脚本文件

<script src="https://unpkg.com/vue@next">< script>

在页面中声明一个将要被 vue 所控制的 DOM区域,既MVVM中的View

<div id="app">
    {{message}}
</div>

创建vm 实例对象(vue实例对象)

const hello = {
// 指定数据源,既MVVM中的Model
    data: function() {    
        return {
            message: 'Hello Vue!'
        }
    }     
}   
const app = Vue.createApp(hello)
app.mount('#app') // 指定当前vue实例要控制页面的哪个区域

官网链接简介 | Vue.js

通过 CDN 使用 Vue

可参考官方文档

源代码(demo.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>

<body>
    <div id="app">{{ message }}</div>

    <script>
    const { createApp, ref } = Vue

    createApp({
        setup() {
        const message = ref('Hello vue!')
        return {
            message
        }
        }
    }).mount('#app')
    </script>
</body>
</html>

01内容渲染指令

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
    <div id ="app">
            <p>姓名:{{username}} </p>
            <p>性别:{{sex}}</p>

            <p>{{desc}}</p>
            <p v-html="desc"></p>
    </div>

    <script>
        const vm ={
            data: function() {
                return {
                    username:'hhhhh',
                    sex :'男',
                    desc:'<a href="https://cn.vuejs.org/guide/quick-start.html#using-vue-from-cdn">vue</a>'
                }
            }
        }

        const app = Vue.createApp(vm)
        app.mount('#app')
    </script>
    
</body>
</html>

02属性绑定指令

<body>
    <div id ="app">
        <a :href ="link">vue</a> 
        <!-- <a v-bind:href ="link">vue</a> 绑定指令,一般省略v-bind -->
        <input type="text" :placeholder="inputValue">
        <img :src="imgSrc" :style="{ width : w }" alt="">
    </div>

    <script>
        const vm ={
            data: function() {
                return {
                    link:"https://cn.vuejs.org/guide/quick-start.html#using-vue-from-cdn",
                    //文本框的占位符内容
                    inputValue:'请输入内容',
                    //图片的src地址
                    imgSrc:'C:/Users/LENOVO/Pictures/Saved Pictures/3.jpg',
                    w:'500px'
                }
            }
        }

        const app = Vue.createApp(vm)
        app.mount('#app')
    </script>
    
</body>

03使用javaScript表达式

<body>
    <!-- vue 实例要控制的 DOM 区域 -->
    <div id="app">
        <p>{{number + 1}}</p>
        <p>{{ok ?'True' :'False'}}</p>
        <!-- 拆分 倒序 拼接 -->
        <p>{{message.split('').reverse().join('')}}</p>
        <p :id="'list-' + id">xxx</p>
        <!-- 绑定了user对象 -->
        <p>{{user.name}}</p>
    </div>

    <script>
        const vm ={
            data: function() {
                return {
                    number:9,
                    ok: false,
                    message:'ABC',
                    id: 3,
                    user:{
                        name:'zs' ,
                    }
                }
            }
        }

        const app = Vue.createApp(vm)
        app.mount('#app')
    </script>
    
</body>

04事件绑定指令

<body>
    <div id ="app">
        <h3>count 的值为: {{count}}</h3>
        <!-- v-on或@ 用于件监听 -->
        <button v-on:click="addCount">+1</button>
        <button @click="count+=1">+1</button>
    </div>

    <script>
        const vm ={
            data: function() {
                return {
                    count:0,
                }
            },
            methods: {
                // 点击按钮,让 count 自增 +1
                addCount(){
                    this.count += 1
                },
            },
        }

        const app = Vue.createApp(vm)
        app.mount('#app')
    </script>
    
</body>

05条件渲染指令

<body>
    <div id ="app">
        <button @click="flag = !flag">Toggle Flag</button>
        <p v-if="flag">请求成功 --- 被 v-if 控制</p>
        <p v-show="flag">请求成功 --- 被 v-show 控制</p>
    </div>

    <script>
        const vm ={
            data: function() {
                return {
                    flag: false,
                }
            }
        }

        const app = Vue.createApp(vm)
        app.mount('#app')
    </script>
    
</body>

06列表渲染指令

<body>
    <div id ="app">
        <ul>
          <li v-for="(user,i) in userList">索引是: {{i}},姓名是: {{user.name}}</li>
        </ul>
    </div>

    <script>
        const vm ={
            data: function() {
                return {
                    userList:[
                        {id: 1, name:'lala'},
                        {id: 2, name:'lisi'},
                        {id: 3, name: 'wangwu'},
                   ],
                }
            },
        }

        const app = Vue.createApp(vm)
        app.mount('#app')
    </script>
    
</body>

07v-for的key属性

<body>
    <div id ="app">
        <!-- 添加用户的区域 -->
        <div>
            <!-- v-model="something" 双向绑定 页面数据发生变化也会影响name值-->
            <input type="text" v-model="name" >
            <button v-on:click=" addNewUser">添加</button>
        </div>

        <!-- 用户列表区域 -->
        <ul>
            <li v-for="(user, index) in userlist" :key="user.id">
                <input type="checkbox" />
                姓名: {{user.name}}
            </li>
        </ul>
    </div>

    <script>
        const vm ={
            data: function() {
                return {
                    //用户列表
                    userlist: [
                        {id: 1, name:'zhangsan'},
                        {id: 2, name:'lisi'}
                    ],
                    //输入的用户名
                    name:' ',
                    //下一个可用的 id 值
                    nextId: 3
                }
            },
            methods: {
                //点击添加按钮
                addNewUser(){
                    this.userlist.unshift({id : this.nextId,name : this.name})
                    this.name=' '
                    this.nextId++
                }
            }
        }

        const app = Vue.createApp(vm)
        app.mount('#app')
    </script>
    
</body>

组件化开发

NPM使用

​​​​NPM简介

NPM (Node Package Manager) 是一个NodeJS包管理和分发工具
NPM以其优秀的依赖管理机制和庞大的用户群体,目前已经发展成为整个JS领域的依赖管理工具
NPM最常见的用法就是用于安装和更新依赖。要使用NPM,首先要安装Node工具。

Nodejs安装

Node.js 是一个基于 Chrome V8引擎的JavaScript 运行时环境

Node中包含了NPM包管理工具
下载地址: https://nodejs.org/zh-cn/

NPM使用

Vue CLI使用

Vue CLI是Vue官方提供的构建工具,通常称为脚手架

用于快速搭建一个带有热重载(在代码修改后不必刷新页面即可呈现修改后的效果)及构建生产版本等功能的单页面应用。

VueCLI基于 webpack 构建,也可以通过项目内的配置文件进行配置

安装: npm install -g @vue/cli

项目创建

1、可视化创建项目,cmd内输入 vue ui

可参考 链接vue可视化面板创建项目_可视化创建vue项目-CSDN博客

2、cmd创建,在对应的项目文件夹内输入cmd,后回车

创建项目成功


 

组件化开发

组件(Component)是Vuejs最强大的功能之一。组件可以扩展HTML元素封装可重用的代码。
Vue的组件系统允许我们使用小型、独立和通常可复用的组件构建大型应用

组件的构成

Vue中规定组件的后缀名是.

vue每个.vue 组件都由3 部分构成,分别是

        template,组件的模板结构,可以包含HTML标签及其他的组件

        script,组件的JavaScript 代码

        style,组件的样式

核心文件

main.js

import { createApp } from 'vue'//通过vue导入了createapp方法

import App from './App.vue'

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

// 模块化开发,通过import导入相应的模块 ,js文价也可以通过import导入

app.vue

app.vue,vue提供的一个根组件

<template></template>组件的标签文本/结构

<script></script>组件行为代码

<style></style>组件css样式

实例

app.vue

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <hello></hello>
</template>

<script>
import hello from './components/hello.vue'

export default {
  name: 'App',
  components: {
    hello
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

hello.vue

<template>
    <div>
        <h1>我爱前端开发</h1>
    </div>
</template>

<script>

</script>

<style>
</style>

第三方组件element-ui

组件间的传值

组件可以由内部的Data提供数据,也可以由父组件通过prop的方式传值

兄弟组件之间可以通过Vuex等统一数据源提供数据共享。

实例

创建一个vue2的项目
app.vue

<template>
  <div id="app">
    <Movie v-for="movie in movies" :key="movie.id" :title="movie.title" :rating="movie.rating"></Movie>
    <Hello></Hello>
  </div>
</template>

<script>
import Movie from './components/Movie.vue';
import Hello from './components/Hello.vue'

export default {
  name: 'App',
  data :function(){
    return{
      movies:[
        {id:1 ,title:"1111",rating :"8.7"},
        {id:2 ,title:"2222",rating :"8.7"},
        {id:3 ,title:"3333",rating :"8.7"},
        {id:4 ,title:"4444",rating :"8.7"},
      ]
    }

  },
  components: {
    Movie,
    Hello
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Movie.vue

<template>
    <div>
        <h1>{{title}}</h1>
        <span>{{ rating }}</span>
        <button @click="fun">收藏</button>
    </div>

</template>

<<script>
export default {//导出
    name:"Movie",
    props:["title","rating"],//自定义属性

    data :function(){
        return{

        }
    },
    methods:{
        fun(){
            alert("收藏成功")
        }

    }
}
</script>

Hello.vue

<template>
    <div>
        <h1>hello</h1>
    </div>
</template>

element-ui介绍

1、Element是国内饿了么公司提供的一套开源前端框架,简洁优雅,提供了Vue、React、Angular等多个版本。
2、文档地址: https://element.eleme.cn/#/zh-CN

3、安装:npm i element-ui

4、引入 Element:

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});
组件的使用

源代码,

Hello.vue

<template>
  <div>
    <el-table
      :data="tableData"
      style="width: 100%"  
      :row-class-name="tableRowClassName">
      <!-- 第一列 -->
      <el-table-column
        prop="date"
        label="日期"
        width="180">
      </el-table-column>
      <!-- 第二列 -->
      <el-table-column
        prop="name"
        label="姓名"
        width="180">
      </el-table-column>
      <el-table-column
        prop="address"
        label="地址">
      </el-table-column>
    </el-table>

    <el-date-picker
      v-model="value1"
      type="date"
      placeholder="选择日期">
    </el-date-picker>
  </div>
  </template>
  
  <script>
    export default {
      methods: {
        tableRowClassName({row, rowIndex}) {
          if (rowIndex === 1) {
            return 'warning-row';
          } else if (rowIndex === 3) {
            return 'success-row';
          }
          return '';
        }
      },
      data() {
        return {
          tableData: [{
            date: '2016-05-02',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1518 弄',
          }, {
            date: '2016-05-04',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1518 弄'
          }, {
            date: '2016-05-01',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1518 弄',
          }, {
            date: '2016-05-03',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1518 弄'
          }],
          value1: '',
       }
      }
    }
  </script>

<style>
.el-table .warning-row {
  background: oldlace;
}

.el-table .success-row {
  background: #f0f9eb;
}
</style>

图标的使用

第三方图标库
1、由于Element UI提供的字体图符较少,一般会采用其他图表库,如著名的FontAwesome
Font Awesome提供了675个可缩放的矢量图标,可以使用CSS所提供的所有特性对它们进行更改,包括大小、颜色、阴影或者其他任何支持的效果。
2、文档地址: Font Awesome,一套绝佳的图标字体库和CSS框架
3、安装: npm install font-awesome
4、使用: import 'font-awesome/css/font-awesome.min.css'

<i class="fa fa-camera-retro"></i> fa-camera-retro


Axios网络请求

Axios的使用
Axios简介

1、在实际项目开发中,前端页面所需要的数据往往需要从服务器端获取,这必然涉及与服务器的通信。
2、Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中Axios在浏览器端使用

3、XMLHttpRequests发送网络请求,并能自动完成JSON数据的转换。
4、安装:npm install axios
5、地址:Axios中文文档 | Axios中文网 | Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js

发送网络请求
发送get请求

发送post请求

 异步回调问题

async/await

其他请求方式

参考:请求配置 | Axios Docs

<script>
import Movie from './components/Movie.vue';
import Hello from './components/Hello.vue';
import axios from 'axios';

export default {
  name: 'App',
  data :function(){
    return{
      movies:[
        {id:1 ,title:"1111",rating :"8.7"},
        {id:2 ,title:"2222",rating :"8.7"},
        {id:3 ,title:"3333",rating :"8.7"},
        {id:4 ,title:"4444",rating :"8.7"},
      ]
    }
  },
  // 组件被创建的时候,该函数自动调用
  created: function(){
    console.log("app组件被创建了")
    axios.get("http://127.0.0.1/user/findAll").then(function(response){
      console.log(response)//异步函数
    })
  },
  mounted:function(){
    console.log("app组件被挂载")
  },
  components: {
    Movie,
    Hello
  }
}
</script>

与Vue整合

在实际项目开发中,几乎每个组件中都会用到 axios 发起数据请求。

此时会遇到如下两个问题:
每个组件中都需要导入axios
每次发请求都需要填写完整的请求路径

可以通过全局配置的方式解决上述问题

跨域
为什么会出现跨域问题

1、为了保证浏览器的安全,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源,称为同源策略,同源策略是浏览器安全的基石
2、同源策略 (Sameoriginpolicy) 是一种约定,它是浏览器最核心也最基本的安全功能
3、所谓同源 (即指在同一个域)就是两个页面具有相同的协议 (protocol),主机(host)和端口号(port)
4、当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨,此时无法读取非同源网页的 Cookie,无法向非同源地址发送AJAX 请求

跨域问题解决

1、CORS(Cross-Origin Resource Sharing)是由W3C制定的一种跨域资源共享技术标准,其目的就是为了解决前端的跨域请求。
2、CORS可以在不破坏即有规则的情况下,通过后端服务器实现CORS接口,从而实现跨域通信。
3、CORS将请求分为两类:简单请求和非简单请求,分别对跨域通信提供了支持

简单请求

满足以下条件的请求即为简单请求
1、请求方法:GET、POST、HEAD
2、除了以下的请求头字段之外,没有自定义的请求头:
3、Accept、Accept-Language、 Content-Language、 Last-Event-ID、Content-Type
4、Content-Type的值只有以下三种
                text/plain、multipart/form-data、application/x-www-form-urlencoded

简单请求的服务器处理
对于简单请求,CORS的策略是请求时在请求头中增加一个Origin字段

服务器收到请求后,根据该字段判断是否允许该请求访问,如果允许,则在HTTP头信息中添加Access-Control-Allow-Origin字段。

非简单请求

1、对于非简单请求的跨源请求,浏览器会在真实请求发出前增加一次OPTION请求,称为预检请求(preflight request)
2、预检请求将真实请求的信息,包括请求方法、自定义头字段、源信息添加到HTTP头信息字段中,询问服务器是否允许这样的操作。

例如一个GET请求

Access-Control-Request-Method表示请求使用的HTTP方法,AccessControl-Request-Headers包含请求的自定义头字段

3、服务器收到请求时,需要分别对Origin、Access-Control-Request-Method.Access-Control-Request-Headers进行验证,验证通过后,会在返回HTTP头信息中添加:

Access-Control-Allow-Methods、Access-Control-Allow-Headers: 真实请求允许的方法、允许使用的字段
Access-Control-Allow-Credentials: 是否允许用户发送、处理cookie

Access-Control-Max-Age: 预检请求的有效期,单位为秒,有效期内不会重发送预检请求。

4、当预检请求通过后,浏览器才会发送真实请求到服务器这样就实现了跨域资源的请求访问。

Spring Boot中配置CORS

在传统的Java EE开发中,可以通过过滤器统一配置,而Spring Boot中对此则提供了更加简洁的解决方案

在控制器内加一个注解也可以@CrossOrigin

实例

main.js

import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import 'font-awesome/css/font-awesome.min.css'
import axios from 'axios';

axios.defaults.baseURL="http://127.0.0.1"

Vue.prototype.$http=axios
Vue.config.productionTip = false
Vue.use(ElementUI);

new Vue({
  render: h => h(App),
}).$mount('#app')

Hello.vue

<template>
  <div>
    <el-table
      :data="tableData"
      style="width: 100%"  
      :row-class-name="tableRowClassName">
      <!-- 第一列 -->
      <el-table-column
        prop="id"
        label="编号"
        width="180">
      </el-table-column>
      <!-- 第二列 -->
      <el-table-column
        prop="username"
        label="姓名"
        width="180">
      </el-table-column>
      <el-table-column
        prop="birthday"
        label="生日">
      </el-table-column>
    </el-table>
    <i class="fa fa-camera-retro"></i>
    <el-date-picker
      v-model="value1"
      type="birthday"
      placeholder="选择日期">
    </el-date-picker>
  </div>
  </template>
  
<script>

  export default {
    methods: {
      tableRowClassName({row, rowIndex}) {
        if (rowIndex === 1) {
          return 'warning-row';
        } else if (rowIndex === 3) {
          return 'success-row';
        }
        return '';
      }
    }, 
    // 组件被创建的时候,该函数自动调用
    created: function(){
      console.log("app组件被创建了")
      this.$http.get("/user/findAll").then((response)=>{
        // =>继承于他的父级
        console.log(response.data)//异步函数
        this.tableData=response.data
      })
    },
    data() {
      return {
        tableData: [],
        value1: '',
      }
    }
  }
</script>

<style>
.el-table .warning-row {
  background: oldlace;
}

.el-table .success-row {
  background: #f0f9eb;
}
</style>

 后端代码只加了一句注释

@CrossOrigin

前端路由VueRouter 

VueRouter安装与使用

1、Vue路由vue-router是官方的路由插件,能够轻松的管理SPA项目中组件的切换。
2、Vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来
3、vue-router 目前有 3.x 的版本和4.x 的版本,vue-router 3.x 只能结合 vue2进行使用,vue-router 4.x 只能结合 vue3 进行使用

4、安装:npm install vue-router@4


参数传递

创建路由组件

Discover.vue

<template>
    <div>
        <h1>发现音乐</h1>
    </div>
</template>

Friends.vue

<template>
    <div>
        <h1>关注</h1>
    </div>
</template>

My.vue

<template>
    <div>
        <h1>我的</h1>
    </div>
</template>
声明路由链接和占位标签

可以使用<router-link> 标签来声明路由链接,并使用<router-view> 标签来声明路由占位符

示例代码如下:

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <!-- 声明路由链接 -->
    <router-link to="/discover"> 发现音乐</router-link>
    <router-link to="/my">我的</router-link>
    <router-link to="/friends">关注</router-link>
  </div>
</template>
创建路由模块

在项目中创建index.js 路由模块,加入以下代码

import VueRouter from "vue-router";
import Vue from "vue";
import Discover from "../components/Discover.vue"
import Friends from "../components/Friends.vue"
import My from "../components/My.vue"

Vue.use(VueRouter)

const router =new VueRouter({
    // 指定hash属性与组件的对应关系
    routes:[
        { path :'/discover' ,component :Discover},
        { path :'/friends' ,component:Friends},
        { path :'/my' ,component :My},
    ]
})

// 导出
export default router

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router :router
}).$mount('#app')

子路由

嵌套路由

 Discover.vue

<template>
    <div>
        <h1>发现音乐</h1>
        <!-- 子路由链接 -->
        <router-link to="/discover/playlist">歌单</router-link>
        <hr>
        <router-view></router-view>
    </div>
</template>

index.js

import VueRouter from "vue-router";
import Vue from "vue";
import Discover from "../components/Discover.vue"
import Friends from "../components/Friends.vue"
import My from "../components/My.vue"
import Playlist from "../components/Playlist.vue"

Vue.use(VueRouter)

const router =new VueRouter({
    // 指定hash属性与组件的对应关系
    routes:[
        // 重定向到discover页面
        {path:'/',redirect:"/discover"},
        { path :'/discover' ,
          component :Discover,
            children:[
                { path :'playlist' ,component :Playlist},
            ]
        },
        { path :'/friends' ,component:Friends},
        { path :'/my' ,component :My},
        // { path :'/discover/playlist' ,component :Playlist},

    ]
})

// 导出
export default router

动态路由

动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。在 vue-router 中使用英文的冒号(:)来定义

路由的参数项。示例代码如下:

 {path :':id',component : Product}

通过动态路由匹配的方式渲染出来的组件中,可以使用 $route.params 对象访问到动态匹配的参数值,比如在商品详情组件的内部,根据id值,请求不同的商品数据。

<template>
    <div>
        <h3>商品{{ $route.params.id }}</h3>
    </div>
</template>

为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。示例代码如下:

<template>
    <div>
        <!-- <h3>商品{{ $route.params.id }}</h3> -->
        <h3>商品{{id}}</h3>
    </div>
</template>

<script >
export default{
    props:["id"]
}
</script>
     { path :'/my' ,
          component :My,
            children:[
                {path :':id',component : Product,props:true}
            ]
        },
导航守卫

导航守卫可以控制路由的访问权限。

示意图如下:全局导航守卫会拦截每个路由规则,从而对每个路由进行访问权限的控制.

你可以使用 router.beforeEach 注册一个全局前置守卫

 伪代码:

to:即将要进入的目标
from:当前导航正要离开的路由
在守卫方法中如果声明了 next 形参,则必须调用 next() 函数,否则不允许用户访问任何一个路
        直接放行:next()
        强制其停留在当前页面: next(false)
        强制其跳转到登录页面: next('/login')


状态管理VueX

Vuex介绍

1、对于组件化开发来说,大型应用的状态往往跨越多个组件。在多层嵌套的父子组件之间传递状态已经十分麻烦,而Vue更是没有为兄弟组件提供直接共享数据的办法。
2、基于这个问题,许多框架提供了解决方案一使用全局的状态管理器将所有分散的共享数据交由状态管理器保管,Vue也不例外。
3、Vuex是一个专为 Vue.js 应用程序开发的状态管理库,采用集中式存储管理应用的所有组件的状态。
4、简单的说,Vuex用于管理分散在Vue各个组件中的数据 Vuex 是什么? | Vuex
5、安装:npm install vuex@next

状态管理

1、每一个Vuex应用的核心都是一个store,与普通的全局对象不同的是,基于Vue数据与视图绑定的特点,当store中的状态发生变化时,与之绑定的视图也会被重新渲染。
2、store中的状态不允许被直接修改,改变store中的状态的唯一途径就是显式地提交(commit) mutation,这可以让我们方便地跟踪每一个状态的变化
3、在大型复杂应用中,如果无法有效地跟踪到状态的变化,将会对理解和维护代码带来极大的困扰。
4、Vuex中有5个重要的概念: State、Getter、Mutation、Action、Module.

State

State用于维护所有应用层的状态,并确保应用只有唯一的数据源

在组件中,可以直接使用this.$store.state.count访问数据,也可以先用mapState辅助函数将其映射下来

Mutation

Mutation提供修改State状态的方法

在组件中,可以直接使用store.commit来提交mutation

也可以先用mapMutation辅助函数将其映射下来

Action

Action类似Mutation,不同在于

Action不能直接修改状态,只能通过提交mutation来修改,Action可以包含异步操作

在组件中,可以直接使用this.$store.dispatch(xxx)分发 action,或者使用mapActions辅助函数先将其映射下来

Getter

Getter维护由State派生的一些状态,这些状态随着State状态的变化而变化

在组件中,可以直接使用this.$store.getters.doneTodos,也可以先用mapGetters辅助函数将其映射下来,代码如下:

Vuex安装与使用

详情请参考官方文档  Vuex 是什么? | Vuex

index.js

import Vue from "vue"; 
import Vuex from "vuex"

Vue.use(Vuex)

const store =new Vuex.Store({
    state:{
        count:0,
        todos: [
            { id: 1, text: '吃饭', done: true },
            { id: 2, text: '睡觉', done: false }
          ]
    },
    mutations:{
        increment (state, n){
            state.count+=n
        }
    },
    getters: {
        doneTodos: state => {
          return state.todos.filter(todo => todo.done)
        }
      }
})

export default store

main.js

import Vue from 'vue'
import App from './App.vue'
import store  from './store'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store:store
}).$mount('#app')

HelloWord.vue

<template>
  <div class="hello">
    <!-- {{ this.$store.state.count }} -->
    <!-- 自动监听数据 -->
    {{count}}
    <button @click="add()">+1</button>

    <ul>
      <!-- <li v-for="todo in todos" :key="todo.id"> {{todo.text}}</li> -->
      <li v-for="todo in doneTodos" :key="todo.id"> {{todo.text}}</li>
    </ul>
  </div>
</template>

<script>
import { mapState ,mapGetters} from 'vuex'


export default {
  name: 'HelloWorld',
  // computed:{
  //   count(){
  //     return this.$store.state.count
  //   }
  // },
  computed:{
    // ...对象的展开函数
      ...mapState([
    // 映射 this.count 为 store.state.count
        'count',
        'todos'
      ]),
      ...mapGetters([
        'doneTodos'
      ])


  },
  methods:{
    add(){
      this.$store.commit("increment",20)
    }
  }


}
</script>

app.vue

<template>
  <div id="app">
    <HelloWorld />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

前端数据模拟MockJS 

mockjs介绍

1、Mockjs 是一款前端开发中拦截Ajax请求再生成随机数据响应的工具,可以用来模拟服务器响应
2、优点是非常简单方便无侵入性,基本覆盖常用的接口数据类型.

3、支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等

4、安装:npminstall mockjs

mockjs基本使用

在项目中创建mock目录,新建index.js文件

import Mock from "mockjs";
// 使用mockjs模拟数据
Mock.mock('/user/findAll',{
    "ret":0,
    "data":{
        "mtime":"@datetime",//随机生成日期时间
        "score|1-800": 1,//随机生成1-899的数字
        "rank|1-100": 1,//随机生成1-1e的数字
        "stars|1-5": 1,//随机生成1-5的数字
        "nickname":"@cname",//随机生成中文名字
        "img":"@image('200x10','#ffcc33','#FFF','png','Fast Mock')"//生成图片
        
    }
});

组件中调用mock.js中模拟的数据接口,这时返回的response就是mock.js中用Mock.mock('url', data) 中设置的data

axios.get("/user/findAll").then((response)=>{
        console.log(response)
      })
核心方法

Mock.mock( rurl?, rtype?, template|function( options ))

rurl,表示需要拦截的 URL,可以是URL 字符串或 URL正则

rtype,表示需要拦截的Ajax 请求类型。例如 GET、POST、PUT、DELETE等

template,表示数据模板,可以是对象或字符串   返回给后端的数据

function,表示用于生成响应数据的函数
设置延时请求到数据

数据生成规则

参考链接:mock语法-CSDN博客​​​​​​

mock的语法规范包含两层规范: 数据模板 (DTD)、数据占位符 (DPD)

数据模板中的每个属性由3部分构成:属性名name 生成规则rule、属性值value :

'name|rule':value

属性名和生成规则之间用竖线|分隔,生成规则是可选的,有7种格式

 企业级后台集成方案

vue-element-admin介绍

1、vue-element-admin 是一个后台前端解决方案,它基于 vue 和element-ui实现。
2、内置了i18国际化解决方案,动态路由,权限验证,提炼了典型的业务模型提供了丰富的功能组件。
3、可以快速搭建企业级中后台产品原型

4、地址: 介绍 | vue-element-admin

安装与使用

# 克隆项目
git clone https://github.com/PanJiaChen/vue-element-admin.git

# 进入项目目录
cd vue-element-admin

# 安装依赖
npm install

# 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npmmirror.com

# 本地开发 启动项目
npm run dev

源码解读

建议去看视频15.vue-element-admin后台集成方案_哔哩哔哩_bilibili,多看看源码,因为本人听得不是很明白,尽力了......哭死


JWT跨域认证

Session认证

互联网服务离不开用户认证。一般流程是下面这样
1、用户向服务器发送用户名和密码
2、服务器验证通过后,在当前对话 (session)里面保存相关数据,比如用户角色登录时间等。
3、服务器向用户返回一个 session_id,写入用户的 Cookie。
4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器

5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份

session 认证的方式应用非常普遍,但也存在一些问题,扩展性不好,如果是服务器集群,或者是跨域的服务导向架构,就要求session 数据共享,每台服务器都能够读取 session,针对此种问题一般有两种方案:
1、一种解决方案是session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大
2、一种方案是服务器不再保存 session 数据,所有数据都保存在客户端,每次请求都发回服务器。Token认证就是这种方案的一个代表。 

Token认证

Token 是在服务端产生的一串字符串是客户端访问资源接口(API)时所需要的资源凭证,流程如下:
1、客户端使用用户名跟密码请求登录,服务端收到请求,去验证用户名与密码

2、验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端

3、客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者localStorage 里
4、客户端每次向服务端请求资源的时候需要带着服务端签发的 token

5、服务端收到请求,然后去验证客户端请求里面带着的 token,如果验证成功就向客户端返回请求的数据

Token认证的特点

1、基于token的用户认证是一种服务端无状态的认证方式,服务端不用存放token 数据。
2、用解析token的计算时间换取session的存储空间,从而减轻服务器的压力减少频繁的查询数据库
3、token 完全由应用管理,所以它可以避开同源策略

JWT的使用

JSON Web Token (简称JWT) 是一个token的具体实现方式,是目前最流行的跨域认证解决方案
JWT的原理是,服务器认证以后,生成一个JSON 对象,发回给用户,具体如下:

 用户与服务端通信的时候,都要发回这个JSON对象。服务器完全只靠这个对象认定用户身份。
为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名

JWT的由三个部分组成,依次如下
Header(头部)
Payload(负载)
Signature(签名)
三部分最终组合为完整的字符串,中间使用.分隔,如下
HeaderPayload.Signature

 Header

Header 部分是一个JSON 对象,描述JWT的元数据

alg属性表示签名的算法 (algorithm),默认是 HMAC SHA256(写成HS256)

typ属性表示这个令牌 (token)的类型 (type),JWT 令牌统一写为JWT

最后,将上面的JSON 对象使用 Base64URL算法转成字符串

Payload

Payload 部分也是一个JSON 对象,用来存放实际需要传递的数据。JWT 规定
了7个官方字段,供选用。

注意,JWT默认是不加密的,任何人都可以读到,所以不要把秘密信息放在个部分。

这个JSON对象也要使用 Base64URL算法转成字符串 

Signature

signature部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥 (secret) 。这个密钥只有服务器才知道,不能泄露给用户

然后,使用 Header 里面指定的签名算法(默认是HMAC SHA256),按照下面的公式产生签名。

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串每个部分之间用”点”(.)分隔,就可以返回给用户。 

JWT的特点

客户端收到服务器返回的JWT,可以储存在 Cookie 里面,也可以储存在localStorage。
客户端每次与服务器通信,都要带上这个JWT,可以把它放在 Cookie 里面自动发送,但是这样不能跨域。
更好的做法是放在HTTP请求的头信息'Authorization'字段里面,单独发送 

JWT的实现

加入依赖

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

生成Token

 private static long expire =604800;
    //32位秘钥
    private static String secret ="abcdefghijklnmabcdefghijklnmiade";

    //生成token
    public static String generateToken(String username){
        Date now=new Date();
        Date expiration =new Date(now.getTime()+1000*expire);
        return Jwts.builder()
                .setHeaderParam("type","JWT")
                .setSubject(username)
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(SignatureAlgorithm.HS512,secret)
                .compact();

    }

解析Token 

//解析token
    public static Claims getClaimsByToken(String token){
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

 

usercontroller

RestController
@RequestMapping("/user")
@CrossOrigin
public class UseController {

    @PostMapping("/login")
    //querystring: username=zhangsan&password=123
    //User user,String username,String password
    //json: {username:zhangsan,password:123}
    //如果前端传递的数据是json格式,必须使用对象接收,同时需要添加@RequestBody
    public Result login (@RequestBody User user)
        {
            String token= JwtUtils.generateToken(user.getUsername());
            return Result.ok().data("token",token);
        }

    @GetMapping("/info") //token:XXX
    public Result info(String token){
        String username=JwtUtils.getClaimsByToken(token).getSubject();
        String url="";
        return Result.ok().data("name",username).data("avatar",url);
    }

    @PostMapping("logout") //token :xxx
    public Result logout(){return Result.ok();}
}

user

package com.example.jwt.entity;

public class User {
    private String username;
    private  String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

jwtutils

public class JwtUtils {
    //7天过期
    private static long expire =604800;
    //32位秘钥
    private static String secret ="abcdefghijklnmabcdefghijklnmiade";

    //生成token
    public static String generateToken(String username){
        Date now=new Date();
        Date expiration =new Date(now.getTime()+1000*expire);
        return Jwts.builder()
                .setHeaderParam("type","JWT")
                .setSubject(username)
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(SignatureAlgorithm.HS512,secret)
                .compact();

    }

    //解析token
    public static Claims getClaimsByToken(String token){
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }
}

result

package com.example.jwt.utils;

import java.util.HashMap;
import java.util.Map;

//统一返回结果的类
public class Result {
    private Boolean success;
    private Integer code; //状态码
    private String message;//状态码解释信息
    private Map<String,Object> data=new HashMap<>(); //map保存与前端数据的交互信息

    public Boolean getSuccess() {
        return success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Map<String, Object> getData() {
        return data;
    }

    public void setData(Map<String, Object> data) {
        this.data = data;
    }

    //构造方法私有
    private Result() {}

    //成功静态方法
    public static Result ok(){
        Result r=new Result();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        return  r;
    }

    //失败静态方法
    public static Result error(){
        Result r =new Result();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    }

    public Result success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public Result message(String message){
        this.setMessage(message);
        return this;
    }

    public Result code (Integer code){
        this.setCode(code);
        return this;
    }

    public Result data(String key,Object value){
        this.data.put(key,value);//键值对
        return this;
    }

    public Result data(Map<String,Object> map){
        this.setData(map);
        return this;
    }
}

resultcode

package com.example.jwt.utils;

public interface ResultCode {
    public static Integer SUCCESS=20000;//成功

    public static Integer ERROR=20001;//失败
}

前后端集成

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;