SpringBoot 基础

news2024/9/30 17:32:12

一、简介

SpringBoot用来简化Spring应用的初始搭建和开发过程,

SpringBoot四大核心:

自动配置

起步依赖

Actuator

命令行界面

二、入门案例

1. 基于IDEA创建步骤

① 创建新模块,选择Spring Initializr,并配置模块相关基础信息

② 选择当前模块需要使用的技术集

③ 开发控制器类

④ 运行自动生成的Application

2. 最简SpringBoot所包含的基础文件:

pom.xml 文件

Application

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3. Spring 程序和 SpringBoot 对比

类/配置文件SpringSpringBoot
pom文件中的坐标手工添加勾选添加
web.3.0配置类手工制作

Spring/SpringMVC配置类

手工制作
控制器手工制作手工制作

4. 基于官网创建项目

① IDEA联网有问题时,可在官网创建项目

https://start.spring.io/

② 点击生成一个压缩文件

③ 解压并导入IDEA即可

5. 基于阿里云创建

https://start.aliyun.com/

 6. 手工创建项目

① 创建普通Maven工程

② 手工导入坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0           
             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>springboot_01_03_quickstart</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

③ 手工制作引导类

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

7. 隐藏指定文件夹

 8. 解析

(1) parent

(仅定义未使用)

所有SpringBoot项目要继承的项目,定义了

若干个坐标版本号(依赖管理,而非依赖),

以达到减少依赖冲突的目的

spring-boot-starter-parent各版本间存在着诸

多坐标版本不同

① 定义一系列的常用坐标版本

project-parent:pom.xml

< properties >
        < druid.version >1.1.16</ druid.version >
        < mybatis.version >3.5.6</ mybatis.version >
        < mysql.version >5.1.47</ mysql.version >
        < db2.version >1.2.3</ db2.version >
        < oracle.version >2.3.4</ oracle.version >
        < sybase.version >3.4.5</ sybase.version >
        < dbase.version >4.5.6</ dbase.version >
        < foxpro.version >5.6.7</ foxpro.version >
        < …….version >6.7.8</ …….version >
</ properties >

② 定义一系列的常用坐标组合

project-dependencies:pom.xml

< dependency >
        < groupId >com.alibaba</ groupId >
        < artifactId >druid</ artifactId >
        < version >${druid.version}</ version >
</ dependency >
< dependency >
        < groupId >org.mybatis</ groupId >
        < artifactId >mybatis</ artifactId >
        < version >${mybatis.version}</ version >
</ dependency >
< dependency >
        < groupId >mysql</ groupId >
        < artifactId >mysql-connector-java</ artifactId >
        < version >${mysql.version}</ version >
</ dependency >

③ 直接使用组合

project-a:pom.xml

< dependency >
        < groupId >example</ groupId >
        < artifactId >project-dependencies</ artifactId >
        < version >1.1.10</ version >
</ dependency >

① 开发SpringBoot程序要继承spring-boot-starter-parent

② spring-boot-starter-parent中定义了若干个依赖管理

③ 继承parent模块可以避免多个依赖使用相同技术时出

     现依赖版本冲突

④ 继承parent的形式也可以采用引入依赖的形式实现效果 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0     https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot-01-quickstart</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

(2) starter

定义了当前项目使用的所有依赖坐标,以达到

减少依赖配置的目的

实际开发:

使用任意坐标时,仅书写GAV中的G和A,V由

SpringBoot提供,除非SpringBoot未提供对应

版本V

如发生坐标错误,再指定Version(小心版本冲突)

spring-boot-starter-web.pom

① 开发SpringBoot程序需要导入坐标时通常导入

     对应的starter

②  每个不同的 starter 根据功能不同,通常包含多
     个依赖坐标
③   使用 starter 可以实现快速配置的效果,达到
      化配置目的

(3) 引导类

启动方式:

@SpringBootApplication
public class Springboot01QuickstartApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot01QuickstartApplication.class, args);
    }
}
① SpringBoot  的引导类是  Boot  工程的 执行
    入口 ,运行  main   方法就可以启动项目
② SpringBoot  工程运行后 初始化  Spring 
    器, 扫描引导类所在包加载  bean

(4) 内嵌 tomcat

辅助功能:

使用maven依赖管理变更起步依赖项:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!--web起步依赖环境中,排除Tomcat起步依赖-->
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--添加Jetty起步依赖,版本由SpringBoot的starter控制-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
</dependencies>
Jetty Tomcat 更轻量级,可扩展性更强
(相较于 Tomcat) ,谷歌应用引擎( GAE)
经全面切换为 Jetty
tomcat( 默认 )
apache 出品,粉丝多, 应用面广
负载了若干较重的组件
jetty
更轻量级 ,负载性能远不及 tomcat
undertow
undertow ,负载性能 勉强 跑赢 tomcat
① 内嵌 Tomcat 服务器是 SpringBoot 辅助
     功能之一
 ②  内嵌 Tomcat 工作原理是将 Tomcat 服务
      器作为对象运行,并将该对象交给
      Spring容器管理
③  变更内嵌服务器思想是去除现有服务器,
     添加全新的服务器

9. REST 风格

 ① 设定 http 请求动作

@RequestMapping(value = "/users",method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user){
    System.out.println("user save..." + user);
    return "{'module':'user save'}";
}

@RequestMapping(value = "/users",method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user){
    System.out.println("user update..." + user);
    return "{'module':'user update'}";
}

@RequestMapping 为方法注解,位于

SpringMVC 控制器方法定义上方,用于

设置当前控制器方法请求访问路径

属性:

value (默认):请求访问路径

method:http 请求动作,标准动作 (GET/

                POST/PUUT/DELETE)

② 设定请求参数  (路径变量)

@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE
)
@ResponseBody
public String delete(@PathVariable Integer id){
    System.out.println("user delete..." + id);
    return "{'module':'user delete'}";
}

@PathVariable 为形参注解,位于

SpringMVC 控制器方法形参定义上方,

用于绑定路径参数处理器方法形参

的关系,要求路径参数名与形参名一一

对应

其中 @RequestBody、@RequestParam、

@PathVariable 三者区别:

@RequestParam 用于接收 url 地址传参

或表单传参

@RequestBody 用于接收 json 数据

@PathVariable 用于接收路径参数,使用

(参数名称)描述路径参数

应用:

① 后期开发中,发送请求参数超过1个时

json 格式为主,@RequestBody 应用

较广

② 如果发送非 json 格式数据,选用

@RequestParam 接收请求参数

③ 采用 RESTful 进行开发,当参数数量

较少时,例如1个,可以采用 

@PathVariable 接收请求路径变量,通常

用于传递 id 值

RESTful 快速开发:

@RestController 类注解,基于 SpringMVC

的 RESTful 开发控制器类定义上方,用于设

置当前控制器类为 RESTful 风格,等同于

@Controller 与 @ResponseBody 两个注解

组合功能

@RestController
public class BookController{
}

@GetMapping、@PostMapping、

@PutMapping、@DeleteMapping 方法

注解,基于SpringMVC 的 RESTful 开发

控制器方法定义上方,用于设置当前控

制器方法请求访问路径请求动作,每种

对应一个请求动作,例如 @GetMapping

对应 GET 请求

@GETMapping("/{id}")
public String getById(@PathVariable Integer id){
    System.out.println("user getById..." + id);
    return "{'module':'book getById'}";
}

属性:

value (默认):请求访问路径

二、基础配置

1. 复制工程

① 在工作空间中复制对应工程,并修改

    工程名称

② 删除与 IDEA 相关配置文件,仅保留

     src 目录与 pom.xml 文件

③ 修改 pom.xml 文件中的 artfactId 与新

     工程/模块名相同

④ 删除 name 标签 (可选)

⑤ 保留备份工程供后期使用

2. 属性配置

(1) 修改服务器端口:

② SpringBoot 默认配置文件 application.properties

     通过键值对配置对应属性

    关闭运行日志图标(banner),设置日志相关

 

 SpringBoot 内置属性查询:

Common Application Properties

① SpringBoot 中导入对应 starter 后,提供

     对应配置属性

② 书写 SpringBoot 配置采用关键字 + 提示

     形式书写

(2) SpringBoot 提供了多种属性配置方式

application.properties (传统格式/默认格式)

server.port=80

application.yml (主流格式)

server:

    port: 81

application.yaml

server:

    port: 82

三者共存时加载顺序:

application.propertiesapplication.yml application.yaml

注:不同配置文件中相同配置按照加载

优先级相互覆盖,不同配置文件中不同

配置全部保留

(3) 属性自动提示消失解决方法

 

 

  

 

(4) yaml 数据格式

YAML 是一种数据序列化格式,优点是容

易阅读,容易与脚本语言交互,以数据为

核心, 重数据轻格式

YAML 文件扩展名:

.yml (主流)

.yaml

(5) yaml 语法规则

① 大小写敏感

② 属性层级关系使用多行描述每行结尾

     使用冒号结束

③ 使用缩进表示层级关系,同层级左侧对

     齐,只允许使用空格 (不允许使用 Tab

     键)

④ 属性值前面添加空格 (属性名与属性值之

     间使用冒号+空格作为分隔)

⑤ # 表示注释

核心规则:数据前面要加空格与冒号隔开

① 字面值表示方式:

boolean : TRUE

TRUE,true,True,FALSE,false,False

均可

float: 3.146.8523015e+5 #支持科学计数法
int: 123

0b1010_0111_0100_1010_1110

#支持二进制、八进制、十六进制

null: ~使用 ~ 表示 null
string: HelloWorld字符串可以直接书写
string2: "Hello World"可以使用双引号包裹特殊字符
date: 2018-02-17日期必须使用 yyyy-MM-dd 格式
datetime: 2018-02-17T15:02:31+08:00

时间和日期之间使用 T 连接

最后使用 + 代表时区

② 数组表示方式:

在属性名书写位置的下方使用减号作为

数据开始符号,每行书写一个数据,减

号与数据间空格分隔

(6) yaml 数据读取

   1) 读取单一属性数据

使用 @Value 读取单个数据,属性名引用

方式:${一级属性名.二级属性名......}

center :
dataDir : /usr/local/fire/data
tmpDir : /usr/local/fire/tmp
logDir : /usr/local/fire/log
msgDir : /usr/local/fire/msgDir

① 在配置文件中可以使用属性名引用方式

     引用属性

baseDir : /usr/local/fire
center :
dataDir : ${ baseDir }/data
tmpDir : ${ baseDir }/tmp
logDir : ${ baseDir }/log
msgDir : ${ baseDir }/msgDir

② 属性中如果出现转义字符,需要使用

    双括号包裹

lesson : "Spring\tboot\nlesson"

   2) 读取全部属性数据

封装全部数据到 Environment 对象

① 使用 Environment 对象封装全部配置

    信息

②  使用 @Autowired 自动装配数据到

     Environment 对象中

自定义对象封装指定数据:

自定义对象封装指定数据的作用 

 

 ① 使用 @ConfigurationProperties 注解

      绑定配置信息到封装类中

② 封装类需要定义为 Spring 管理的 bean

     否则无法进行属性注入

三、整合第三方技术

1. 整合 JUnit

@SpringBootTest
class Springboot07JunitApplicationTests {
    @Autowired
    private BookService bookService;
    @Test
    public void testSave(){
        bookService.save();
    }
}

@SpringBootTest 测试类注解,位于测

试类定义上方,设置 JUnit 加载的

SpringBoot 启动类

class:设置 SpringBoot 启动类

① 导入测试对应的 starter

② 测试类使用 @SpringBootTest 修饰

③ 使用自动装配的形式添加要测试的对象

 注:如果测试类在SpringBoot启动类包或子

        包中,可以省略启动类的设置,也就是省

        略 classes 的设定

2. 整合 MyBatis

核心配置:数据库连接相关信息

映射配置:SQL 映射(XML /注解)

① 创建新模块,选择Spring初始化,并配置模

     块相关基础信息

 

 ② 选择当前模块需要使用的技术集 (MyBatisMySQL)

 ③ 设置数据源参数

spring:
    datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/ssm_db
        username: root
        password: root
SpringBoot  版本低于  2.4.3 ( 不含 ) Mysql 
动版本大于 8.0  时,需要在  url  连接串中配置
时区
jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
或在  MySQL  数据库端配置时区解决此问题

④ 定义数据层接口与映射配置

@Mapper
public interface UserDao {
    @Select("select * from user")
    public List<User> getAll();
}

⑤ 测试类中注入dao接口,测试功能

@SpringBootTest
class Springboot08MybatisApplicationTests {
    @Autowired
    private BookDao bookDao;
    @Test
    public void testGetById() {
        Book book = bookDao.getById(1);
        System.out.println(book);
    }
}

① 勾选 MyBatis 技术,也就是导入 MyBatis 

     对应的 starter

② 数据库连接相关信息转换成配置

③ 数据库 SQL 映射需要添加 @Mapper 被容

     器识别到

3. 整合 MyBatis-Plus

MyBatis-Plus 与 MyBatis 区别:

① 导入坐标不同

② 数据层实现简化

整合:

① 手动添加SpringBoot整合 MyBatis-Plus 

    坐标,可以通过 mvnrepository 获取 

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
</dependency>
由于 SpringBoot 中 未收录  MyBatis-Plus  的坐
标版本,需要指定对应的 Version

② 定义数据层接口与映射配置,继承 BaseMapper

@Mapper
public interface UserDao extends BaseMapper<User> {
}

③ 其余同 SpringBoot 整合 MyBatis

① 手工添加 MyBatis-Plus 对应的 starter

② 数据层接口使用 BaseMapper  简化开发

③ 需要使用的第三方技术无法通过勾选确

    定时,需要手工添加坐标

4. 整合 Druid

① 指定数据源类型
spring:
    datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
        username: root
        password: root
        type: com.alibaba.druid.pool.DruidDataSource

② 导入 Druid 对应的 starter

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>

③ 变更 Druid 的配置方式

spring:
    datasource:
        druid:
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
            username: root
            password: root

5. 整合任意第三方技术

① 导入对应的 starter

② 配置对应的设置或采用默认配置

四、基于SpringBoot的 SSMP 整合案例

实体类开发使用 Lombok 快速制作实体类
Dao开发整合 MyBatisPlus,制作数据层测试类
Service开发

基于 MyBatisPlus 进行增量开发,制作业务层

测试类

Controller开发基于 Restful 开发,使用 PostMan 测试接口功能
Controller开发前后端开发协议制作
页面开发
基于  VUE+ElementUI  制作,前后端联调,页
面数据处理,页面消息处理
列表、新增、修改、删除、分页、查询 
项目异常处理
按条件查询

页面功能调整、Controller 修正功能、Service

修正功能 

1. 制作流程: 

① 先开发基础 CRUD 功能,做一层

     测一层

② 调通页面,确认异步提交成功后,

     制作所有功能

③ 添加分页功能与查询功能

2. 模块创建

① 勾选 SpringMVC 与 MySQL 坐标

② 修改配置文件为 yml 格式

③ 设置端口为 80 方便访问

3. 实体类开发

 (1) 使用 lombok 简化开发

Lombok,一个 Java 类库,提供了一

组注解,简化 POJO 实体类开发

Lombok 版本由 SpringBoot 提供,无

需指定版本

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

   (2) 常用注解:@Data

@Data
public class Book {
    private Integer id;
    private String type;
    private String name;
    private String description;
}
为当前实体类在编译期设置对应的
get/set  方法, toString  方法,
hashCode  方法, equals  方法等

4. 数据层开发

(1) 技术实现方案:

MyBatisPlus

Druid

(2) 导入 MyBatisPlus 与 Druid 对应

     starter

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>

(3) 配置数据源与 MyBatisPlus 对应的

    基础配置 (id 生成策略使用数据库自

    增策略)

spring:
    datasource:
        druid:
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://localhost:3306/ssm_db?servierTimezone=UTC
            username: root
            password: root
mybatis-plus:
    global-config:
        db-config:
            table-prefix: tbl_
            id-type: auto

(4) 继承 BaseMapper 并指定泛型

@Mapper
public interface BookDao extends BaseMapper<Book> {
}

 (5) 制作测试类测试结果

@SpringBootTest
public class BookDaoTest {
    @Autowired
    private BookDao bookDao;
    @Test
    void testSave() {
        Book book = new Book();
        book.setName("测试数据");
        book.setType("测试类型");
        book.setDescription("测试描述数据");
        bookDao.insert(book);
    }
    @Test
    void testGetById() {
        System.out.println(bookDao.selectById(13));
    }
    ...
}

① 手工导入 starter 坐标 (2个)

② 配置数据源与 MyBatisPlus 对应的
    配置
③ 开发 Dao 接口 (继承 BaseMapper)
④ 制作测试类测试 Dao 功能是否有效

(6) 为方便调试可以开启 MyBatisPlus 

     日志,设置日志输出方式为标准输出

mybatis-plus:
    configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

5. 数据层开发--分页功能

(1) 分页操作需要设定分页对象  IPage
@Test
void testGetPage(){
    IPage page = new Page(1,5);
    bookDao.selectPage(page,null);
}

(2) IPage 对象中封装了分页操作中的

     所有数据

① 数据
当前 页码值
每页数据总量
最大 页码值
⑤ 数据总量

(3) 分页操作是在 MP 的常规操作基础

     上增强得到,内部是动态的拼写

     SQL 语句,因此需要增强对应的功

     能, 使用 MP 拦截器实现

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定义Mp拦截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.添加具体的拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }
}
6. 数据层开发--条件查询功能
(1)  使用  QueryWrapper   对象 封装查询条
    件 ,推荐使用 LambdaQueryWrapper
    对象,所有 查询操作 封装成方法调用
@Test
void testGetByCondition(){
    IPage page = new Page(1,10);
    LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
    lqw.like(Book::getName,"Spring");
    bookDao.selectPage(page,lqw);
}
@Test
void testGetByCondition(){
    QueryWrapper<Book> qw = new QueryWrapper<Book>();
    qw.like("name","Spring");
    bookDao.selectList(qw);
}

(2) 支持动态拼写查询条件

@Test
void testGetByCondition(){
    String name = "Spring";
    IPage page = new Page(1,10);
    LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
    lqw.like(Strings.isNotEmpty(name),Book::getName,"Spring");
    bookDao.selectPage(page,lqw);
}

7. 业务层开发

(1) Service 层接口定义与数据层接口

     定义具有较大区别,不要混用

selectByUserNameAndPassword (
     String username,String password);
login (String username,String
     password);

(2) 接口定义

public interface BookService {
    boolean save(Book book);
    boolean delete(Integer id);
    boolean update(Book book);
    Book getById(Integer id);
    List<Book> getAll();
    IPage<Book> getByPage(int currentPage,int pageSize);
}

(3) 实现类定义

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;

    public Boolean save(Book book) {
        return bookDao.insert(book) > 0;
    }

    public Boolean delete(Integer id) {
        return bookDao.deleteById(id) > 0;
    }

    public Boolean update(Book book) {
        return bookDao.updateById(book) > 0;
    }
}

(4) 测试类定义

@SpringBootTest
public class BookServiceTest {
    @Autowired
    private BookService bookService;
    @Test
    void testGetById(){
        bookService.getById(9);
    }
    @Test
    void testGetAll(){
        bookService.getAll();
    }
    @Test
    void testGetByPage(){
        bookService.getByPage(1,5);
    }
    … …
}
Service  接口名称定义成 业务名
     称 ,并与  Dao   接口名称进行
     分
② 制作测试类测试 Service 功能是
     否有效

8. 业务层开发--快速开发

① 使用 MP  提供有业务层通用接口
     ( ISerivce<T> 与业务层通用实现
     类 ( ServiceImpl<M,T> )
② 在通用类基础上做 功能重载 功能
     追加
③ 注意 重载时不要覆盖原始操作 ,避
     免原始提供的功能丢失

(1) 接口定义

public interface IBookService extends IService<Book> {
}
public interface IBookService extends IService<Book> {
    //追加的操作与原始操作通过名称区分,功能类似
    Boolean delete(Integer id);
    Boolean insert(Book book); 
    Boolean modify(Book book);
    Book get(Integer id);
}

(2) 实现类定义

@Service
public class BookServiceImpl2 extends ServiceImpl<BookDao,Book> implements IBookService {
}
@Service
public class BookServiceImpl2 extends ServiceImpl<BookDao,Book> implements IBookService {
    @Autowired
    private BookDao bookDao;
    public Boolean insert(Book book) {
        return bookDao.insert(book) > 0;
    }
    public Boolean modify(Book book) {
        return bookDao.updateById(book) > 0;
    }
    public Boolean delete(Integer id) {
        return bookDao.deleteById(id) > 0;
    }
    public Book get(Integer id) {
        return bookDao.selectById(id);
    }
}

9. 表现层开发

① 基于  Restful  进行 表现层 接口开发
② 使用  Postman  测试表现层接口功能

(1) 功能测试

@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private IBookService bookService;
    @GetMapping
    public List<Book> getAll(){
        return bookService.list();
    }
}

(2) 表现层接口开发

@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private IBookService bookService;
    @PostMapping
    public Boolean save(@RequestBody Book book){
        return bookService.insert(book);
    }
    @PutMapping
    public Boolean update(@RequestBody Book book){
        return bookService.modify(book);
    }
    @DeleteMapping("/{id}")
    public Boolean delete(@PathVariable Integer id){
        return bookService.delete(id);
    }
}
@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private IBookService bookService;
    @GetMapping("/{id}")
    public Book getById(@PathVariable Integer id){
        return bookService.getById(id);
    }
    @GetMapping
    public List<Book> getAll(){
        return bookService.list();
    }
    @GetMapping("/{currentPage}/{pageSize}")
    public List<Book> getAll(@PathVariable Integer currentPage,@PathVariable Integer pageSize){
        return bookService.getPage(currentPage,pageSize).getRecords();
    }
}

基于 Restful 制作表现层接口: 

① 新增:POST

② 删除:DELETE

③ 修改:PUT

④ 查询:GET

接收参数:

① 实体数据:@RequestBody

② 路径变量:@PathVariable

10. 表现层消息一致性处理

设计表现层返回结果的模型类,用于

后端前端进行数据格式统一,也称

前后端数据协议

@Data
public class R{
    private Boolean flag;
    private Object data;

    public R(){
    }

    public R(Boolean flag){
        this.flag = flag;
    }

    public R(Boolean flag,Object data){
        this.flag = flag;
        this.data = data;
    }
}

表现层接口统一返回值类型结果

@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private IBookService bookService;
    @PostMapping
    public R save(@RequestBody Book book){
        Boolean flag = bookService.insert(book);
        return new R(flag);
    }
    @PutMapping
    public R update(@RequestBody Book book){
        Boolean flag = bookService.modify(book);
        return new R(flag);
    }
}
@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private IBookService bookService;
    @DeleteMapping("/{id}")
    public R delete(@PathVariable Integer id){
        Boolean flag = bookService.delete(id);
        return new R(flag);
    }
    @GetMapping("/{id}")
    public R getById(@PathVariable Integer id){
        Book book = bookService.getById(id);
        return new R(true,book);
    }
}
@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private IBookService bookService;
    @GetMapping
    public R getAll(){
        List<Book> bookList = bookService.list();
        return new R(true ,bookList);
    }
    @GetMapping("/{currentPage}/{pageSize}")
    public R getAll(@PathVariable Integer currentPage,@PathVariable     Integer pageSize){
        IPage<Book> page = bookService.getPage(currentPage, pageSize);
        return new R(true,page);
    }
}
① 设计 统一的返回值结果类型 便于前端
     开发读取数据
② 返回值结果类型可以根据需求自行设
     定, 没有固定格式
③ 返回值结果模型类用于后端与前端进
     行数据格式统一,也称为前后端数据
     协议

11. 前后端协议联调

① 前后端分离结构设计中页面归属前端
     服务器
② 单体工程中页面放置在  resources  
    录下的 static   目录中(建议执行  clean )

(1) 前端发送异步请求,调用后端接口

//列表
getAll() {
    axios.get("/books").then((res)=>{
        console.log(res.data);
    });
},
① 单体项目中页面放置在  resources/static
    目录下
created  钩子函数 用于初始化页面时发起
     调用
③ 页面使用  axios  发送异步请求获取数据后
     确认前后端是否联通

(2) 列表页

查询数据返回到页面,利用前端数据双向

绑定进行数据展示

//列表
getAll() {
    axios.get("/books").then((res)=>{
        this.dataList = res.data.data;
    });
},

(3) 弹出添加窗口

//弹出添加窗口
handleCreate() {
    this.dialogFormVisible = true;
},

(4) 清除数据

//重置表单
resetForm() {
    this.formData = {};
},
//弹出添加窗口
handleCreate() {
    this.dialogFormVisible = true;
    this.resetForm();
},

(5) 添加

//添加
handleAdd () {
    //发送异步请求
    axios.post("/books",this.formData).then((res)=>{
        //如果操作成功,关闭弹层,显示数据
        if(res.data.flag){
            this.dialogFormVisible = false;
            this.$message.success("添加成功");
        }else {
            this.$message.error("添加失败");
        }
    }).finally(()=>{
        this.getAll();
    });
},

(6) 取消添加

//取消
cancel(){
    this.dialogFormVisible = false;
    this.$message.info("操作取消");
},
① 请求方式使用 POST 调用后台对应
     操作
② 添加操作结束后 动态刷新页面 加载数据
③ 根据操作结果不同,显示对应的提示信息
④ 弹出添加 Div 清除表单数据

(7) 删除

// 删除
handleDelete(row) {
    axios.delete("/books/"+row.id).then((res)=>{
        if(res.data.flag){
            this.$message.success("删除成功");
        }else{
            this.$message.error("删除失败");
        }
    }).finally(()=>{
        this.getAll();
    });
}
// 删除
handleDelete(row) {
    //1.弹出提示框
    this.$confirm("此操作永久删除当前数据,是否继续?","提示",{
        type:'info'
    }).then(()=>{
        //2.做删除业务
        axios.delete("/books/"+row.id).then((res)=>{
            ……
        }).finally(()=>{
            this.getAll();
        });
    }).catch(()=>{
        //3.取消删除
        this.$message.info("取消删除操作");
    });
}
① 请求方式使用 Delete 调用后台对应
     操作
② 删除操作需要传递当前行数据对应
     的 id 值到后台
③ 删除操作结束后动态刷新页面加载
     数据
④ 根据操作结果不同,显示对应的提
     示信息
⑤ 删除操作前弹出提示框避免误操作

(8) 弹出修改窗口

//弹出编辑窗口
handleUpdate(row) {
    axios.get("/books/"+row.id).then((res)=>{
        if(res.data.flag){
            //展示弹层,加载数据
            this.formData = res.data.data;
            this.dialogFormVisible4Edit = true;
        }else{
            this.$message.error("数据同步失败,自动刷新");
        }
    });
},

(9) 删除消息维护

//删除
handleDelete(row) {
    axios.delete("/books/"+row.id).then((res)=>{
        if(res.data.flag){
            this.$message.success("删除成功");
        }else{
            this.$message.error("数据同步失败,自动刷新");
        }
    }).finally(()=>{
        this.getAll();
    });
}
① 加载要修改数据通过传递当前行数据
     对应的 id 值到后台查询数据
② 利用前端数据双向绑定将查询到的数
     据进行 回显

(10) 修改

//修改
handleEdit() {
    axios.put("/books",this.formData).then((res)=>{
        //如果操作成功,关闭弹层并刷新页面
        if(res.data.flag){
            this.dialogFormVisible4Edit = false;
            this.$message.success("修改成功");
        }else {
            this.$message.error("修改失败,请重试");
        }
    }).finally(()=>{
        this.getAll();
    });
},

(11) 取消添加和修改

cancel(){
    this.dialogFormVisible = false;
    this.dialogFormVisible4Edit = false;
    this.$message.info("操作取消");
},
① 请求方式使用  PUT  调用后台对应操作
② 修改操作结束后动态刷新页面加载数
     据 (同新增)
③ 根据操作结果不同,显示对应的提示
     信息 (同新增)

12. 业务消息一致性处理

(1) 对异常进行统一处理,出现异常后,

     返回指定信息

@RestControllerAdvice
public class ProjectExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public R doOtherException(Exception ex){
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,ex对象发送给开发人员
        ex.printStackTrace();
        return new R(false,null,"系统错误,请稍后再试!");
    }
}

(2) 修改表现层返回结果的模型类,封

     装出现异常后对应的信息

flag:false
Data: null
消息( msg ): 要显示信息
@Data
public class R{
    private Boolean flag;
    private Object data;
    private String msg;

    public R(Boolean flag,Object data,String msg){
        this.flag = flag;
        this.data = data;
        this.msg = msg;
    }
}

(3) 页面消息处理,没有传递消息加载默

     认消息,传递消息后加载指定消息

//添加
handleAdd () {
    //发送ajax请求
    axios.post("/books",this.formData).then((res)=>{
        //如果操作成功,关闭弹层,显示数据
        if(res.data.flag){
            this.dialogFormVisible = false;
            this.$message.success("添加成功");
        }else {
            this.$message.error(res.data.msg);
        }
    }).finally(()=>{
        this.getAll();
    });
},

(4) 可以在表现层 Controller 中进行消息

     统一处理

目的:国际化

@PostMapping
public R save(@RequestBody Book book) throws IOException {
    Boolean flag = bookService.insert(book);
    return new R(flag , flag ? "添加成功^_^" : "添加失败-_-!");
}

(5) 页面消息处理

//添加
handleAdd () {
    //发送ajax请求
    axios.post("/books",this.formData).then((res)=>{
        if(res.data.flag){
            this.dialogFormVisible = false;
            this.$message.success(res.data.msg);
        }else {
            this.$message.error(res.data.msg);
        }
    }).finally(()=>{
        this.getAll();
    });
},
① 使用注解  @RestControllerAdvice 
      定义 SpringMVC 异常处理器用来
     处理异常的
② 异常处理器 必须被扫描加载 ,否则
     无法生效
③ 表现层返回结果的模型类中 添加消
     息属性 用来传递消息到页面

13. 分页功能

(1) 页面使用 el 分页主键添加分页功能

<!--分页组件-->
<div class="pagination-container">
    <el-pagination
            class="pagiantion"
            @current-change="handleCurrentChange"
            :current-page="pagination.currentPage"
            :page-size="pagination.pageSize"
            layout="total, prev, pager, next, jumper"
            :total="pagination.total">
    </el-pagination>
</div>

(2) 定义分页组件需要使用的数据并将

     数据绑定分页组件

data:{
    pagination: {         //分页相关模型数据
        currentPage: 1,   //当前页码
        pageSize:10,      //每页显示的记录数
        total:0,          //总记录数
    }
},

(3) 替换查询全部功能为分页功能

getAll() {       
    axios.get("/books/"+this.pagination.currentPage+"/"
+this.pagination.pageSi ze).then((res) => {
    });
},

(4) 分页查询

使用路径参数传递分页数据或封装

象传递数据

@GetMapping("/{currentPage}/{pageSize}")
public R getAll(@PathVariable Integer currentPage,@PathVariable Integer pageSize){
    IPage<Book> pageBook = bookService.getPage(currentPage, pageSize);
    return new R(null != pageBook ,pageBook);
}

(5) 加载分页数据

getAll() { axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((res) => {
        this.pagination.total = res.data.data.total;
        this.pagination.currentPage = res.data.data.current;
        this.pagination.pagesize = res.data.data.size;
        this.dataList = res.data.data.records;
    });
},

(6) 分页页码值切换

//切换页码
handleCurrentChange(currentPage) {
    this.pagination.currentPage = currentPage;
    this.getAll();
},
① 使用 el 分页组件
②  定义 分页组件绑定的数据模型
 异步调用 获取分页数据
④ 分页数据 页面回显

14. 删除功能维护

@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize){
    IPage<Book> page = bookService.getPage(currentPage, pageSize);
    //如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
    if( currentPage > page.getPages()){
        page = bookService.getPage((int)page.getPages(), pageSize);
    }
    return new R(true, page);
}

15. 条件查询功能

(1) 查询条件数据封装

① 单独封装

② 与分页操作混合封装

pagination: {         //分页相关模型数据
    currentPage: 1,   //当前页码
    pageSize:10,      //每页显示的记录数
    total:0,          //总记录数
    name: "",
    type: "",
    description: ""
}

(2) 页面数据模型绑定

<div class="filter-container">
    <el-input placeholder="图书类别" v-model="pagination.type"     
         class="filter-item"/>
    <el-input placeholder="图书名称" v-model="pagination.name" 
         class="filter-item"/>
    <el-input placeholder="图书描述" v-model="pagination.description" 
         class="filter-item"/>
    <el-button @click="getAll()" class="dalfBut">查询</el-button>
    <el-button type="primary" class="butT" @click="handleCreate()">新建         
    </el-button>
</div>

(3) 组织数据成为 get 请求发送的数据

getAll() {
    //1.获取查询条件,拼接查询条件
    param = "?name="+this.pagination.name;
    param += "&type="+this.pagination.type;
    param += "&description="+this.pagination.description;
    console.log("-----------------"+ param);            
    axios.get("/books/"+this.pagination.currentPage+"/"
+this.pagination.pageSize+param)
        .then((res) => {
        this.dataList = res.data.data.records;
    });
},

  条件参数组织可以通过条件判定书

  写的更简洁

(4) Controller 接收参数

@GetMapping("{currentPage}/{pageSize}")
public R getAll(@PathVariable int currentPage,@PathVariable int pageSize,Book book) {
    System.out.println("参数=====>"+book);
    IPage<Book> pageBook = bookService.getPage(currentPage,pageSize);
    return new R(null != pageBook ,pageBook);
}

(5) 业务层接口功能开发

public interface IBookService extends IService<Book> {
    IPage<Book> getPage(Integer currentPage,Integer pageSize,Book queryBook);
}
@Service
public class BookServiceImpl2 extends ServiceImpl<BookDao,Book> implements IBookService {
    public IPage<Book> getPage(Integer currentPage,Integer pageSize,Book queryBook){
        IPage page = new Page(currentPage,pageSize);
        LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();            
        lqw.like(Strings.isNotEmpty(queryBook.getName()),
            Book::getName,queryBook.getName());    
        lqw.like(Strings.isNotEmpty(queryBook.getType()),
            Book::getType,queryBook.getType());
        lqw.like(Strings.isNotEmpty(queryBook.getDescription()),
            Book::getDescription,queryBook.getDescription());
        return bookDao.selectPage(page,lqw);
    }
}

(6)  Controller 调用业务层分页条件

      查询接口

@GetMapping("{currentPage}/{pageSize}")
public R getAll(@PathVariable int currentPage,@PathVariable int pageSize,Book book) {
    IPage<Book> pageBook = bookService.getPage(currentPage,pageSize,book);
    return new R(null != pageBook ,pageBook);
}

(7) 页面回显数据

getAll() {
    //1.获取查询条件,拼接查询条件
    param = "?name="+this.pagination.name;
    param += "&type="+this.pagination.type;
    param += "&description="+this.pagination.description;
    console.log("-----------------"+ param);        
    axios.get("/books/"+this.pagination.currentPage+"/"
            +this.pagination.pageSize+param)
        .then((res) => {
        this.pagination.total = res.data.data.total;
        this.pagination.currentPage = res.data.data.current;
        this.pagination.pagesize = res.data.data.size;
        this.dataList = res.data.data.records;
    });
},
① 定义查询条件数据模型 (当前封
     装到分页数据模型中)
②  异步调用分页功能并通过请求参
     数传递数据到后台
pom.xml配置起步依赖
application.yml
设置数据源、端口、框架技术相关
配置等
dao
继承BaseMapper、设置@Mapper
④ dao 测试类
service
调用数据层接口或MyBatis-Plus提供
的接口快速开发
⑥ service 测试类
controller 
基于Restful开发,使用Postman测试
跑通功能
⑧ 页面
放置在resources目录下的static目录中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/485479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI和GPT之间什么关系,GPT怎么推动AI发展

AI&#xff08;人工智能&#xff09;和GPT&#xff08;Generative Pre-trained Transformer&#xff09;都是与计算机技术相关的术语&#xff0c;但它们代表着不同层次的概念。以下是它们之间的区别&#xff1a; 定义&#xff1a; AI是指任何类型的计算机科学&#xff0c;如机…

【AWS入门】AWS Lamda

目录 创建一个Lamda函数用Lamda函数控制启停EC2实例创建一台EC2实例创建角色创建lamda函数 使用Amazon EventBridge计划启停实例创建EventBridge 用户往S3存储桶上传图片文件&#xff0c;触发Lambda函数&#xff0c;将图片压缩并上传至另一个存储桶创建两个存储桶通过Cloudform…

【前端】3.CSS基础知识

系列文章链接 1.背景知识与学习路线 2.HTML基础知识 3.CSS基础知识 4.JavaScript基础知识 文章目录 1. 基本概念1.1 CSS 定义1.2 CSS 用途1.3 CSS 支持的属性举例 2. CSS 基础语法2.1 demo2.2 选择器2.3 CSS创建2.3.1 外部样式表2.3.2 内部样式表2.3.3 内联样式 3. 总结3.1 CSS…

Linux线程:信号量

1. 信号量 &#xff08;1&#xff09;概念 信号量是非负整数计数器&#xff0c;表示当前可使用的公共资源的个数&#xff1b;主要用于进程或线程的同步或互斥。 信号量 > 0&#xff0c;则可访问公共资源&#xff0c;信号量&#xff1b;信号量 < 0&#xff0c;则阻塞。…

dreamer的创作纪念日

先来看看这张图&#xff1a; 创作者dreamer来到CSDN的第1024天 机缘 提示&#xff1a;可以和大家分享最初成为创作者的初心 最初开始写CSDN博客&#xff0c;初衷是为了记录&#xff1a;自己工作或学习中遇到的一些问题&#xff08;就像我的第一篇博客&#xff0c;就是刷Lee…

Windows powershell添加自定义快捷指令(Linux下也有)

Windows Powershell 1.创建并修改Windows Powershell 启动执行文件 echo $PROFILE编辑C:\Users\hongyang.jia\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 2.按照以下格式输入命令 function 别名 { 需要替代的命令 }例如我的Git简写命令 function gs {git sta…

dm5爬虫(selenium)

因为纯requests请求获取不到图片链接,所以用了selenium import requests import os from lxml import etree import re from selenium import webdriverurl https://www.dm5.com/manhua-qiangweishaonv/ # 漫画的url response requests.get(url) # 获取响应 print(response)…

PowerJob客户端连接

本文来说下PowerJob客户端连接 文章目录 启动服务端编写示例代码编写自己的处理器启动示例程序任务配置与运行配置任务创建任务执行结果运行结果问题及解决 本文小结 启动服务端 启动服务端&#xff1a;http://127.0.0.1:7700/#/oms/home 编写示例代码 进入示例工程&#xff08…

RGB-D基准测试工具:绝对轨迹误差 (ATE)、相对位姿误差 (RPE)和从图像生成点云

在估计RGB-D相机轨迹并将其保存到文件中后&#xff0c;我们需要通过将其与地面事实进行比较来评估估计轨迹中的误差。有不同的错误指标。两种突出的方法是绝对轨迹误差&#xff08;ATE&#xff09;和相对姿势误差&#xff08;RPE&#xff09;。ATE非常适合测量可视 SLAM 系统的…

5 map、结构体、方法、面向对象【Go语言教程】

5 map、结构体、方法、面向对象【Go语言教程】 1 map 1.1 概念及声明 ①概念 基本语法&#xff1a;var map 变量名 map[keytype]valuetype key 可以是什么类型&#xff1f; golang 中的 map&#xff0c;的 key 可以是很多种类型&#xff0c;比如 bool, 数字&#xff0c;stri…

OK6410A 中的 irqdomain 之 gpio

文章目录 VIC domain 与 gpio domain 的硬件拓扑语言描述VIC 与 INT_EINTx 的关系INT_EINTx 与 GPIO的关系INT_EINT0INT_EINT1INT_EINT2INT_EINT3INT_EINT4INT_EINT4 与 External interrupt Group1-9 的关系External interrupt Group1External interrupt Group2External interr…

基于Open3D的点云处理2-Open3D的IO与数据转换

三维数据类型 点云 某个坐标系下的点数据集&#xff0c;每个点包括三维坐标X&#xff0c;Y&#xff0c;Z、颜色、分类值、强度值、时间等信息&#xff1b; 储存格式&#xff1a;pts、LAS、PCD、xyz、asc、ply等&#xff1b;Mesh 多边形网格&#xff0c;常见的是三角网格&#…

node快速搭建接口实现登录退出,增删改查功能供前端使用,结尾有完整代码

用node快速搭建接口 环境实现功能具体实现步奏数据库设计用express创建一个服务器实例创建数据库连接池配置跨域请求和解析前端数据登录接口实现验证token的中间件退出接口获取用户信息接口增删改查功能 完整代码 环境 node版本v17.0.0 所用到的依赖 "dependencies"…

量子力学奇妙之旅-从相对论下薛定谔方程到量子场论

专栏目录: 高质量文章导航-持续更新中 1.用于描述一个多粒子体系的波函数 前置:最小的尺度 由于不确定性原理 因此为了测量到更小的空间尺度,我们就需要提高探测粒子的动量

RSA非对称加密(附工具类Util)

文章目录 非对称加密是什么非对称加密通信流程RSA非对称加密算法非对称加密工具类Util及案例演示 之前写过一篇关于DES对称加密的帖子&#xff0c;感兴趣的小伙伴可以去看看&#xff1a;DES对称加密算法 今天主要聊聊什么是非对称加密&#xff0c;以及它是如何实现的。 一、非…

f(x)与|f(x)|,f ‘ (x),F(x)常见关系。

1.f(x)与|f(x)|关系。 1.连续关系。(f(x)在"[a,b]上连续" > |f(x)|在"[a,b]连续") ①如果f(x)在[a,b]上连续。则|f(x)|在[a,b]上连续. &#xff08;因为f(x)在x0的连续点>x0必为|f(x)|的连续点&#xff09; 注&#xff1a;”[a,b]连续“包括&#…

PUBG绝地求生的制作公司计划今年推出NFT元宇宙游戏

欢迎来到Hubbleverse &#x1f30d; 关注我们 关注宇宙新鲜事 &#x1f4cc; 预计阅读时长&#xff1a;5分钟 本文仅代表作者个人观点&#xff0c;不代表平台意见&#xff0c;不构成投资建议。 近年来&#xff0c;元宇宙的概念在游戏界获得了极大的关注。元宇宙指的是一个虚…

武汉大学惯性导航课程合集【2021年秋】2.1 惯导机械编排算法

Vrb是客观存在的&#xff0c;b相对于r的速度&#xff08;从R到b的变化&#xff09;&#xff0c;右上角的b表示投影到p坐标系&#xff0c;只是表达数值的不同。 &#xff08;工程上5-10&#xff0c;50倍&#xff09;奈奎斯特采样率&#xff0c;香农采样定理&#xff0c;又称奈…

Synthesys:语音合成和视频生成平台

【产品介绍】 Synthesys是一个基于人工智能的语音合成和视频生成平台&#xff0c;可以让你用几分钟的时间&#xff0c;就能制作出专业的音频和视频内容&#xff0c;无需花费大量的金钱和时间去雇佣演员、摄像机或音频设备。Synthesys的技术可以把你的文本转换成逼真的人声和人像…

《花雕学AI》AI时代来临,互联网教父凯文·凯利给你50条生存指南:5000天后的世界会是什么样?

你知道凯文凯利吗&#xff1f;他是《连线》杂志的创始人之一&#xff0c;被誉为“世界互联网教父”&#xff0c;他的预言和观点影响了无数人的思考和行动。他曾经预言过互联网、社交媒体、区块链等技术的发展和变革&#xff0c;而现在&#xff0c;他又给我们带来了一个全新的预…