《JavaEE进阶》----12.<SpringIOCDI【扫描路径+DI详解+经典面试题+总结】>
本篇博客主要讲解
扫描路径
DI详解:三种注入方式及优缺点
经典面试题
总结
五、环境扫描路径
虽然我们没有告诉Spring扫描路径是什么,但是有一些注解已经告诉Spring扫描路径是什么了
如启动类注解@SpringBootApplication。
里面有一个注解是@componentScan这个注解就是扫描路径注解。
默认扫描路径
默认为:启动类所在的路径
被@componentScan标识的当前类所在的路径。
而启动类@SpringBootApplication包含了@componentScan。因此也是被@SpringBootApplication标识的类。
注:
这五大注解必须在Spring的扫描路径下才会生效。
package com.qiyangyang.iocdome;
因此
我们这个项目Spring的扫描路径是com.qiyangyang.iocdome这个路径。
因此,当我们移动IocDomeApplication这个类到不同的文件中。Spring的默认扫描路径就会发生改变。
我们通常把启动类放在最外层(Controller、Service...的上一层)。这样就可以扫描到使用IoC的类了。如果非要放在别的地方。你希望它扫描哪些路径,你也可以指定。
指定扫描路径
我们也可以通过 @ComponentScan注解 指定扫描路径
指定方法:在启动类上面加上如下注解
扫描路径的起始文件地址是java文件的下一级目录。
@ComponentScan(“扫描路径”)
eg:
@ComponentScan(“com.qiyangyang.iocdome”)
如果什么都没加,默认为被该注解标识的所在的类的路径。
六、依赖注入(DI)详解
DI(Dependency-Injection依赖注入)
容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入。
程序运⾏时需要某个资源,此时容器就为其提供这个资源. 从这点来看, 依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过 引入IoC容器,利用依赖关系注入的方式,实现对象之间的解耦。
依赖注入是⼀个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象. 在下面程序案例中
我们使用了 @Autowired 这个注解,完成了依赖注⼊的操作. 简单来说,就是把对象取出来放到某个类的属性中.。在一些文章中。依赖注⼊也被称之为"对象注入","属性装配",
具体含义需要结合文章的上下文来理解
关于依赖注入,Spring也给我们提供了三种方式:
6.1 @Autowired属性注入(Field-Injection)
这个方法我们在上一篇文章使用过。这是重述一遍
@RestController
@RequestMapping("/book")
public class BookController {
//属性注入@Autowiredprivate BookService bookService;
}
由于BookService类已经被@Service注解过。因此已经将BookService类的对象存入了Spring容器
在我们后续使用它的对象的时候。不需要我们自己new这个对象。而是可以通过如上定义一个BookService属性。通过@Autowired注解,将这个对象取出。这样后续就可以直接用这个对象
6.2 构造方法注入(Constructor-Injection)
注:当我们手动加上构造函数之后。一定要养成习惯把默认无参构造方法写上
当有单个构造函数
Spring知道使用哪个。因此会正常运行。
@RestController
@RequestMapping("/book")
public class BookController {
//构造方法注入private BookService bookService;public BookController(BookService bookService){this.bookService = bookService;}
@Controller("bean") //创建对象 //括号中是对bean进行重命名 如果没有指定名称spring帮我们指定
public class UserController {private UserService userService;private UserRepository userRepository;public UserController(UserService userService, UserRepository userRepository) {this.userService = userService;this.userRepository = userRepository;}public void say(){userService.say();userRepository.say();System.out.println("Hi Controller!!!");}
}
当有多个构造函数
@Controller("bean") //创建对象 //括号中是对bean进行重命名 如果没有指定名称spring帮我们指定
public class UserController {private UserService userService;private UserRepository userRepository;public UserController(){}public UserController(UserRepository userRepository) {this.userRepository = userRepository;}public UserController(UserService userService, UserRepository userRepository) {this.userService = userService;this.userRepository = userRepository;}public void say(){userService.say();userRepository.say();System.out.println("Hi Controller!!!");}
}
优先使用默认无参构造函数。如果没有。就会报错。
@Autowired 指定使用某个构造函数:
@Autowiredpublic UserController(UserService userService, UserRepository userRepository) {this.userService = userService;this.userRepository = userRepository;}
6.3 Setter 注入(SetterInjection)
也需要搭配@AutoWired注解才可以正常注入。
private UserService userService;
private UserRepository userRepository;
@Autowired
public void setUserService(UserService userService) {this.userService = userService;
}
@Autowired
public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;
}
6.3三种依赖注入优缺点
属性注入(Spring最不推荐,但没关系):
优点:
简介,使用方便;
缺点:
1.只能用于 IoC 容器。因为它是Spring提供的。
若不是 IoC 容器不可用,且只有在使用的时候才会出现 NPE(空指针异常)
2.不能注入一个final修饰的属性。
构造方法注入(Spring4.x推荐):
我们可以点进@Autowired注解。再点击目录。看到我们现在使用的Spring版本。
优点:
1.可以注入final修饰的属性
注:final修饰的属性有一个要求,需要满足下列任意条件
①声明时,要完成初始化
②在构造函数中进项赋值。
2.注入的对象不会被修改
3.依赖对象在使用前一定会被完全初始化,因为依赖是在类在构造方法中执行的。而构造方法是在类加载阶段就会执行的方法。
4.通用性好。构造方法是JDK支持的,所以更换任何框架他都适用。
缺点:
1.注入多个对象时,代码会比较繁琐。
Setter注入(Spring3.x推荐)
优点:
方便在类实例之后,重新对该对象进行配置或注入
缺点:
1.不能注入一个final修饰的属性
2.注入对象可能会被改变。因为setter方法可能会被多次调用。就有被修改的风险。
6.4@AutoWired存在的问题
当同一类型存在多个Bean时。使用@Autowired会存在问题
@Configuration
public class BeanConfig {@Beanpublic UserInfo userInfo1(){UserInfo userInfo = new UserInfo();userInfo.setId(120);userInfo.setName("张三");userInfo.setAge(18);return userInfo;}@Beanpublic UserInfo userInfo2(){UserInfo userInfo2 = new UserInfo();userInfo2.setId(121);userInfo2.setName("李四");userInfo2.setAge(28);return userInfo2;}
}
报错的原因是非唯一Bean对象。
Spring提供了一下几种解决方案。
@Primary
在构造多个对象时
使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。
@Configuration
public class BeanConfig {@Primary@Beanpublic UserInfo userInfo1(){UserInfo userInfo = new UserInfo();userInfo.setId(120);userInfo.setName("张三");userInfo.setAge(18);return userInfo;}@Beanpublic UserInfo userInfo2(){UserInfo userInfo2 = new UserInfo();userInfo2.setId(121);userInfo2.setName("李四");userInfo2.setAge(28);return userInfo2;}
}
这样就不会报错了。也就是默认使用张三这个对象进行使用。
@Qualifier
使用@Qualifier注解:在注入时。指定当前要注入的bean对象。在@Qualifier的value属性中,指定注入的bean 的名称。
@Qualifier注解不能单独使用,必须配合@Autowired使用
@Qualifier("userInfo2")//指定bean的名称@Autowiredprivate UserInfo userInfo;public void say(){System.out.println("Hi Controller!!!");userService.say();System.out.println(userInfo);}
@Resource
使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。
不用@Autowirede注解配合使用。
@Resource(name="userInfo2")private UserInfo userInfo;public void say(){System.out.println("Hi Controller!!!");userService.say();System.out.println(userInfo);}
常见面试题:
1.@Autowird与@Resource的区别
• @Autowired是spring框架提供的注解,@Resource是JDK提供的注解
• @Autowired 默认是按照类型注入,@Resource是按照名称注入,相比于@Autowired来说, @Resource支持更多的参数设置,例如 name 设置,根据名称获取 Bean。
2.Spring,SpringBoot,SpringMVC之间的区别和联系,你是如何理解的?
我的理解
1.Spring(很早) 简单来说,Spring是一个轻量级、一站式、模块化的开发应用框架。主要用于简化企业级应用程序开发。
Spring的主要功能:管理对象,以及对象之间的依赖关系。面向切面编程、数据库事务管理、数据访问、web框架支持等。
Spring(这里指Spring-core。Spring家族都具有高度可开放性)具备高度可开放性。并不强制依赖Spring,开发者可以自由选择Spring的部分或者全部,Spring可以无缝集成第三方框架。比如数据访问框架(Hibernate、JPA)、web框架(如Struts、JSF)。
在使用Spring时,不强制使用Spring框架,也可以使用第三方框架。
2.SpringBoot(晚) 是对Spring的一个封装,为简化Spring应用的开发而出现的。中小型企业。没有成本研究自己的框架,使用SpringBoot可以快速的搭建框架,降低开发成本。让开发人员更加专注于Spring应用的开发。而无需过多关注xml的配置和一些底层实现。
SpringBoot是一个手脚架,插拔式搭建项目,可以快速的集成其他框架进来。
比如想使用SpringBoot开发Web项目,只需要引入SpringMVC即可。Web开发的工作是SpringMVC完成的。而不是SpringBoot。想完成数据访问,只需要引入Mybatis框架即可。
SpringBoot只是辅助简化项目开发的,让开发变得更简单,甚至不需要额外的web服务器,直接生产jar包执行即可。
使我们在创建项目的时候可以直接添加一些依赖。并且内置web服务器、提供许多注解方便我们书写代码。对项目进行更多的监控指标,更好的了解项目的运行情况。简化我们的开发。
3.SpringMVC(早) 是一个Spring家族的子框架。是针对Web开发和网络接口的一种MVC的思想的实现。也被称作Spring Web MVC(Spring Web)。
在创建项目时,我们添加的依赖Spring Web实际上引的就是SpringMVC。可以认为Spring给我们提供的Web功能就叫做SpringMVC。
我们现在认为SpringMVC就是SpringWeb。主要进行Web开发(网站开发)。
最后一句总结:Spring MVC和Spring Boot都属于Spring。Spring MVC是基于Spring的一个MVC框架。而Spring Boot是基于Spring的一套快速开发整合包
比如我们的图书系统代码中
整体框架是通过SpringBoot搭建的
IoC、DI功能是Spring的提供的,
web相关功能是Spring MVC提供的。
这三者专注的领域不同,解决的问题也不⼀样,总的来说,Spring就像一个大家族,有众多衍生产 品,但他们的基础都是Spring,用一张图来表他们三个的关系:
假如把Spring看作火车。(而做项目相当于坐火车) 但是它买票不方便。
因此就可以把SpringBoot看作是12306。而12306不仅可以订票还可以订酒店。打的等等。让我们坐火车(做项目更加的方便)
而SpringMVC 可以认为是火车里面提供的一些功能。比如买票,改签,插座等等。(注解/Cookie&Session)
3.ApplicationContext VS BeanFactory
1.继承关系和功能方面来说:
Spring 容器有两个顶级的接口:BeanFactory和 ApplicationContext。其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext 属于BeanFactory的⼦类,它除了继承了BeanFactory的所有功能之外, 它还拥有独特的特性,还添加了对国际化支持、资源访问支持、以及事件传播等方面的支持.
2.从性能方面来说:
ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象,而BeanFactory 是需要那个才去加载那个,因此更加轻量. (空间换时间)
4.三种依赖注入优缺点
属性注入(Spring最不推荐,但没关系):
优点:
简介,使用方便;
缺点:
1.只能用于 IoC 容器。因为它是Spring提供的。
若不是 IoC 容器不可用,且只有在使用的时候才会出现 NPE(空指针异常)
2.不能注入一个final修饰的属性。
构造方法注入(Spring4.x推荐):
我们可以点进@Autowired注解。再点击目录。看到我们现在使用的Spring版本。
优点:
1.可以注入final修饰的属性
注:final修饰的属性有一个要求,需要满足下列任意条件
①声明时,要完成初始化
②在构造函数中进项赋值。
2.注入的对象不会被修改
3.依赖对象在使用前一定会被完全初始化,因为依赖是在类在构造方法中执行的。而构造方法是在类加载阶段就会执行的方法。
4.通用性好。构造方法是JDK支持的,所以更换任何框架他都适用。
缺点:
1.注入多个对象时,代码会比较繁琐。
Setter注入(Spring3.x推荐)
优点:
方便在类实例之后,重新对该对象进行配置或注入
缺点:
1.不能注入一个final修饰的属性
2.注入对象可能会被改变。因为setter方法可能会被多次调用。就有被修改的风险。
5.常见注解有哪些?分别是什么作用?
1. web url映射: @RequestMapping
2.参数接收和接口响应:@RequestParam,@RequestBody,@ResponseBody
3.bean的存储:@Controller,@Service,@Repository,@Component,@Configuration,@Bean
4.bean的获取: @Autowired, @Qualifier, @Resource
5.多个bean的默认指定:@Primary
6.Spring两大核心思想IOC和AOP
待续
总结:
告诉spring管理Bean,Bean的存储
1.类注解:五大注解@Controller@Component@Configuration@Service@Repository
2.方法注解:@Bean
Bean的名称:
1.五大注解
类名首字母小写,如果前两位字母均为大写,则为原类名,也可以指定Bean的名称
指定方法:@Controller(“beanName”)
2.@bean
默认名称是方法名,也可以指定名称:@Bean(“beanName”)
使用场景
1.五大注解 自己开发的程序
2.@Bean
①存储第三方的对象(代码不在自己的项目中)
②一个类型需要创建多个对象时
获取bean三种扫描路径的方式
通过Spring运行环境的Spring上下文
ApplicationContext context = SpringApplication.run(IocDomeApplication.class, args);
获取Bean的功能是BeanFeactory提供的
三种方式
1.通过类型
UserController bean1 = context.getBean(UserController.class);
bean1.say();
2.通过bean名称
UserService userService =(UserService) context.getBean("userService");
userService.say();
3.通过bean名称+类型
UserComponent userComponent = context.getBean("userComponent", UserComponent.class);
userComponent.say();
扫描路径:
默认扫描路径:启动类所在的路径
指定扫描路径: 在启动类上面加上如下注解
@ComponentScan(“扫描路径”)
相关文章:

《JavaEE进阶》----12.<SpringIOCDI【扫描路径+DI详解+经典面试题+总结】>
本篇博客主要讲解 扫描路径 DI详解:三种注入方式及优缺点 经典面试题 总结 五、环境扫描路径 虽然我们没有告诉Spring扫描路径是什么,但是有一些注解已经告诉Spring扫描路径是什么了 如启动类注解SpringBootApplication。 里面有一个注解是componentS…...

Selenium 自动化测试:常用函数与实例代码
引言 Selenium 是一个强大的自动化测试工具,广泛用于网页应用的自动化测试。它支持多种编程语言,包括 Python。本文将介绍 Selenium 的常用函数,并提供参数解释和代码示例。 Selenium 简介 Selenium 是一个用于自动化 Web 应用测试的工具&…...

python网络爬虫(五)——爬取天气预报
1.注册高德天气key 点击高德天气,然后按照开发者文档完成key注册;作为爬虫练习项目之一。从高德地图json数据接口获取天气,可以获取某省的所有城市天气,高德地图的这个接口还能获取县城的天气。其天气查询API服务地址为https://re…...

四.海量数据实时分析-Doris数据导入导出
数据导入 1.概述 Apache Doris 提供多种数据导入方案,可以针对不同的数据源进行选择不同的数据导入方式。 数据源导入方式对象存储(s3),HDFS使用 Broker 导入数据本地文件Stream Load, MySQL LoadKafka订阅 Kafka 数据Mysql、PostgreSQL&a…...

一. 从Hive开始
1. 怎么理解Hive Hive不能理解成一个传统意义上的数据库,应该理解成一个解决方案。 是Hadoop在hdfs和mapreduce之后才出现的一个结构化数据处理的解决方案。 Hdfs解决了大数据的存储问题,mapreduce解决了数据的计算问题。 一切似乎很美好。 但是使用成本…...

Linux下的PWM驱动
PWM PWM简介⭕ **PWM(Pulse Width Modulation,脉冲宽度调制)**是一种利用微处理器的数字输出对模拟电路进行控制的技术。通过改变脉冲的占空比,可以控制模拟电路的输出电压或电流。PWM技术广泛应用于电机控制、灯光调节、音频信号…...

日语输入法平假名和片假名切换
在学日语输入法的时候,我们在使用罗马音输入的时候,在进行平假名和片假名切换: 1、使用电脑在打字,日语输入法切换的时候使用 Shift Alt 如果日语输入法显示为 A 需要切换为 あ的话可以按Caps Lock键 。(相当于中文…...

Oracle向量搜索及其应用场景
Oracle 向量搜索(AI Vector Search)是一个集成到 Oracle 数据库中的功能,旨在优化人工智能(AI)工作负载。它允许用户存储和查询非结构化数据的语义内容,如文档、图像等,形式为向量。 向量数据类…...

【排序算法】六、快速排序补充:三指针+随机数法
「前言」文章内容是对快速排序算法的补充,之前的算法流程细节多难处理,这里补充三指针随机数法(递归),这个容易理解,在时间复杂度上也更优秀。 快排:三指针随机数法 原理跟之前的一致ÿ…...

PyTorch torch.cdist函数介绍及示例代码
1. torch.cdist 函数介绍 torch.cdist 是 PyTorch 中用于计算两组向量之间成对距离的函数。它可以计算两个张量(矩阵)中的每对向量之间的距离,支持多种距离度量方式,如欧氏距离(默认)或 p 范数距离。 函数原型 torch.cdist(x1, x2, p=2.0, compute_mode=use_mm_for_eu…...

CTK框架(四): 插件编写
目录 1.生成插件 1.1.环境说明 1.2.服务类,纯虚类,提供接口 1.3.实现插件类,实现纯虚函数 1.4.激活插件,加入ctk框架的生命周期中 1.5.添加资源文件 1.6..pro文件 2.使用此插件 3.总结 1.生成插件 1.1.环境说明 编译ct…...

深入理解C代码中的条件编译
引言 条件编译是 C 编程中的一个重要特性,它允许开发人员根据不同的条件选择性地编译源代码的不同部分。这一特性对于编写跨平台的程序、优化代码性能或控制编译时资源消耗等方面非常重要。本文将深入探讨条件编译的工作原理、使用场景、高级应用以及注意事项&…...

Ubuntu16.04操作系统-内核优化
1. 概述 本文所用优化是生产环境中经过长期验证的内核优化策略,针对的服务器与POD主要用于高CPU、高内存、高IO的业务场景。 备注: OS: ubuntu16.04, 内核: 4.15.0-147-generic 主要涵盖以下内容优化: ulimit优化加强tcp参数其他内存参数 …...

Qt/C++编写的Onvif调试助手调试神器工具/支持云台控制/预置位设置等/有手机版本
一、功能特点 广播搜索设备,支持IPC和NVR,依次返回。可选择不同的网卡IP进行对应网段设备的搜索。依次获取Onvif地址、Media地址、Profile文件、Rtsp地址。可对指定的Profile获取视频流Rtsp地址,比如主码流地址、子码流地址。可对每个设备设…...

【原创】java+swing+mysql密码管理器系统设计与实现
个人主页:程序员杨工 个人简介:从事软件开发多年,前后端均有涉猎,具有丰富的开发经验 博客内容:全栈开发,分享Java、Python、Php、小程序、前后端、数据库经验和实战 文末有本人名片,希望和大家…...

JavaEE-HTTPHTTPS
目录 HTTP协议 一、概念 二、http协议格式 http请求报文 http响应报文 URL格式 三、认识方法 四、认识报头 HTTP响应中的信息 HTTPS协议 对称加密 非对称加密 中间人攻击 解决中间人攻击 HTTP协议 一、概念 HTTP (全称为 "超⽂本传输协议") 是⼀种应⽤…...

iLogtail 开源两周年:社区使用调查报告
作者:玄飏 iLogtail 作为阿里云开源的可观测数据采集器,以其高效、灵活和可扩展的特性,在可观测采集、处理与分析领域受到了广泛的关注与应用。在 iLogtail 两周年之际,我们对 iLogtail 开源社区进行了一次使用调研,旨…...

Ubuntu 比较两个文件夹
比较两个文件夹下的大量文件是否一致,可以通过以下几种方式完成: 1. 使用 diff 命令 diff 命令不仅可以比较文件,还能递归比较文件夹。可以使用 -r 选项来递归比较两个目录下的文件: diff -r /path/to/dir1 /path/to/dir2 如…...

两数之和--力扣1
两数之和 题目思路C代码 题目 思路 根据题目要求,元素不能重复且不需要排序,我们这里使用哈希表unordered_map。注意题目说了只对应一种答案。 所以我们在循环中,使用目标值减去当前循环的nums[i],得到差值,如果我们…...

vue原理分析(三)new()创建Vue实例
今天我们来分析Vue实例的创建 代码如下: Vue.config.productionTip falsenew Vue({render: h > h(App),}).$mount(#app) Vue.config.productionTip false 这个配置成false,是阻止启动生产消息 new Vue({render: h > h(App),}).$mount(#app)…...

Spring MVC: 构建Web应用的强大框架
Spring MVC: 构建现代Web应用的强大框架 1. MVC设计模式简介 MVC (Model-View-Controller) 是一种广泛使用的软件设计模式,它将应用程序的逻辑分为三个相互关联的组件: Model (模型): 负责管理数据、业务逻辑和规则。View (视图): 负责用户界面的展示,将数据呈现给用户。Con…...

网络学习-eNSP配置NAT
NAT实现内网和外网互通 #给路由器接口设置IP地址模拟实验环境 <Huawei>system-view Enter system view, return user view with CtrlZ. [Huawei]undo info-center enable Info: Information center is disabled. [Huawei]interface gigabitethernet 0/0/0 [Huawei-Gigabi…...

动态规划-最长回文子串
题目描述 给你一个字符串 s,找到 s 中最长的 回文子串。 对于该题使用中心扩展法在某些情况下可以比动态规划方法更优,尤其是在处理较长字符串时。这是因为中心扩展法具有更好的空间复杂度,并且在实际应用中可能具有更快的运行速度…...

海康威视 嵌入式 面经 海康威视嵌入式软件 嵌入式硬件总结面试经验 面试题目汇总
标题海康威视 嵌入式 面经 海康威视嵌入式软件 嵌入式硬件总结面试经验 面试题目汇总 整理总结了海康威视嵌入式的面试题目!,可以供大家面试参考 标题海康威视 嵌入式 面经 五月底投递,六月初面试,一场技术面,一场H…...

使用图论技巧——有遍数限制的最短路
给定一个 n个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。 请你求出从 11 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible。 注意:图中可能 存在负权回路…...

flume 使用 exec 采集容器日志,转储磁盘
flume 使用 exec 采集容器日志,转储磁盘 在该场景下,docker 服务为superset,flume 的sources 选择 exec , sinks选择 file roll 。 任务配置 具体配置文件如下: #simple.conf: A single-node Flume configuration#…...

459重复的子字符串
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。 public repeatedSubstringPattern(String s){int n s.length();for(int i 1; i < n / 2; i){if(n % i ! 0) continue;// substring获取子字符串是左闭右开的String ss s.substring(0,…...

【HarmonyOS NEXT】实现截图功能
【HarmonyOS NEXT】实现截图功能 【需求】 实现:实现点击截图按钮,实现对页面/组件的截图 【步骤】 编写页面UI Entry Component struct Screenshot {BuildergetSnapContent() {Column() {Image().width(100%).objectFit(ImageFit.Auto).borderRadi…...

小皮面板webman ai项目本地启动教程
1.前置条件 下载小皮面板 下载后,双击安装,一路next(下一步),无需更改配置。 2.安装必须软件 在小皮面板的软件管理页,安装编号①②③④下面四个软件。 3.启动本地服务 进入到小皮面板的首页&#x…...

从零实现诗词GPT大模型:实现多头自注意力
专栏规划: https://qibin.blog.csdn.net/article/details/137728228 在上一篇文章的最后,我们已经介绍了为什么要使用多头注意力了,本篇文章我们主要来实现多头自注意力,然后综合我们之前实现的FFN和TransformerBlock其实就差不多完成了整个GPT模型的实现了。 在开始实现之…...