苍穹外卖随记(一)
黑马苍穹外卖逻辑和细节的问题和解决
1.后端查询到员工的日期信息,将信息进行json化传给前端时发生:前端收到的是不标准的日期json串。
解决:1.注解进行json格式化(@JsonFormat)2. 在spring MVC中,通过消息转换器(重写configureMessageConverters
方法),统一进行日期的标准json化
2.关于在设计接口时,返回值的Result是否带有泛型,主要取决于Result的data属性。data是我们给前端传输的数据,我们当设计到查询业务时,应该设定泛型
3.//TODO:直接在接口测试传参数的时候发现,我们在进行员工信息修改时输入的信息并没有合法化,但是也能保存入数据库。因此会发现,当再次进行信息修改时,原本的信息会被判定为不合法
4.如何实现自动填充公共字段(逻辑思路:需要干什么?;怎么写?【熟悉反射等机制】)
解决:解决代码重用问题,我们会想到aop,也就是切面思维。那么其底层的实现逻辑其实就是使用拦截器,去拦截需要对公共字段进行赋值的方法,然后对他进行赋值。那么有个问题是:如何让拦截器知道,我们需要拦截的方法是哪些呢?其实就是给一个标志。基于这个思路:我们的解决办法其实也就是 自定义注解AutoFill和自定义切面AutoFillAspect。最后在Mapper的方法上加入AutoFill注解
自定义注解AutoFill,用于标识出需要进行公共字段自动填充的方法
自定义切面类AutoFillAspect,统一拦截加入了AutoFill注解的方法,通过反射为公共字段赋值
技术点:自定义注解、自定义切面、定义切入点、定义通知方法
5.借助 阿里云oss存储 上传文件
使用MultipartFile接口的参数,实现http文件上传
MultipartFile
是 Spring 框架中用于处理 HTTP 文件上传的一个接口。它定义了处理上传文件所需的基本操作,比如获取文件名、文件大小、文件类型以及最重要的,获取文件的字节流来读取文件内容。
6.Endpoint 通常包含地域信息和阿里云的服务标识,如 oss-cn-beijing.aliyuncs.com
中的 oss
表示对象存储服务,cn-beijing
表示中国北京地域。aliyuncs.com
是阿里云的顶级域名。
7.@ConditionalOnMissingBean
是一个 Spring 框架的注解,它用来确保当没有其他相同类型的 bean 存在于 Spring 容器中时,才创建并注册一个 bean。简单来说,它的作用就是:
-
如果系统中没有这个类型的 bean,就创建一个。
-
如果已经有了,就不创建了,避免重复。
这个注解通常用在自动配置类中,以提供一个默认的 bean 实现,让开发者可以根据自己的需要替换掉这个默认实现。
8.
//根据id修改菜品信息和对应的口味信息 @Override public void updateWithFlavor(DishDTO dishDTO) { //菜品基本信息直接修改:菜品基本信息 直接dish类型,不需要dto Dish dish = new Dish(); BeanUtils.copyProperties(dishDTO,dish); dishMapper.update(dish); //菜品口味由于多种可能:未改、追加、全部修改【不是简单的变量的数据修改】 //删除原来的口味数据 dishFlavorMapper.deleteByDishId(dishDTO.getId()); //再用传来的参数重新新增口味 List<DishFlavor> flavors = dishDTO.getFlavors(); if(flavors != null && !flavors.isEmpty()){ //口味信息也可能是新增的口味, flavors.forEach(dishFlavor -> { dishFlavor.setDishId(dishDTO.getId()); }); dishFlavorMapper.insertBatch(flavors); } }这里的:
//口味信息也可能是新增的口味, flavors.forEach(dishFlavor -> { dishFlavor.setDishId(dishDTO.getId()); });为什么还要重新设置id?没明白
我的想法:就是修改菜品id,菜品id已知,为什么还需要设置id?
9.sql经常出现的错误:赋值时:比如:update_user = #{updateUser},其中,update_user 是数据库表的字段 ;updateUser是参数
10.springData redis操作redis数据库:
-
导入springData redis的maven坐标
-
配置Redis数据源
-
编写redis的配置类,创建redisTemplate对象
-
通过redisTemplate操作redis数据库
11.编写redis的配置类:
作用:创建并初始化redisTemplate对象,并返回。(作为bean组件,创建以后可以直接注入)
步骤:
创建redisTemplate对象
redisTemplate初始化:
设置redis的连接对象工厂
设置redis 中 key 的序列化器
返回redisTemplate对象
其他:
redis的连接对象工厂RedisConnectionFactory是什么?有什么作用?
答:
RedisConnectionFactory
是 Spring Data Redis 提供的一个接口,它的作用是创建与 Redis 服务器的连接。你可以把它想象成一个工厂,它的职责是制造(即创建)和管理 Redis 连接。这个接口的主要目的是抽象化和封装创建 Redis 连接的细节,使得在 Spring 应用程序中使用 Redis 变得更加容易和统一。无论你使用的是哪种 Redis 客户端库(比如 Jedis 或 Lettuce),你都可以通过实现
RedisConnectionFactory
接口来提供连接。
RedisConnectionFactory
的作用包括:
创建连接:它提供了创建新连接的方法。当你的应用程序需要与 Redis 服务器交互时,它可以通过
RedisConnectionFactory
获取一个连接。配置连接:你可以在
RedisConnectionFactory
实现中配置连接的各种参数,比如超时设置、密码认证、数据库选择等。连接池管理:在高并发的应用程序中,直接创建和关闭连接是非常耗费资源的。
RedisConnectionFactory
可以配合连接池使用,复用连接,提高性能。透明化连接管理:开发者不需要关心连接的低级管理,如打开、关闭连接等,这些都由
RedisConnectionFactory
来处理。
在 Spring Boot 中,通常会有一个自动配置的 RedisConnectionFactory
实现,它根据你的 application.properties
或 application.yml
文件中的配置来创建连接。如果你需要自定义连接的某些特定行为,你可以自己实现 RedisConnectionFactory
接口或者扩展现有的实现类。
简单来说,RedisConnectionFactory
就像是 Redis 连接的“制造商”,它让应用程序能够方便地获取和使用 Redis 连接。
StringRedisSerializer是什么?有什么用?
答:
StringRedisSerializer
是 Spring Data Redis 中的一个序列化器,默认情况下,StringRedisSerializer
使用 Java 的String
类型作为序列化和反序列化的对象。实际上可以接收Object的java类型;
StringRedisSerializer
的主要用途包括:
键值序列化:在 Redis 中存储键值对时,键和值都需要被序列化为字符串。
StringRedisSerializer
可以确保键和值在存入 Redis 之前被正确地转换为字符串格式。字符串操作:在执行需要字符串参数的 Redis 命令时(如
SET
、GET
等),StringRedisSerializer
可以确保传入的参数是正确序列化的。一致性:在分布式系统中,确保所有的 Java 对象在网络传输和存储时使用相同的序列化机制,可以避免数据不一致的问题。
12.关于redis数据库
@Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { log.info("开始创建redis操作对象"); RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory); //redis的key的序列化是非常有必要的,否则查询很复杂 redisTemplate.setKeySerializer(new StringRedisSerializer()); //但是考虑是否可以切换其他的序列化方法??????? //因为得到的值仍然是“乱码” return redisTemplate; }
13.在项目中有管理端和客户端和服务端,那么服务端是后端,管理端和客户端是前端,那么在设计接口时有这样的:/api/admin/applications,那么后端怎么根据这个接口开始开发呢?那个api怎么理解,或者说怎么替换?
在软件开发中,API(应用程序编程接口)是一组预定义的函数、协议和工具,用于构建软件应用程序。API 允许不同的软件系统相互交互。在您提到的 /api/admin/applications
接口中,api
通常是指应用程序编程接口的前缀,而后面的路径则定义了具体的资源和操作。
对于后端开发来说,设计和实现这样的接口通常涉及以下步骤:
-
理解需求:首先,需要明确接口的目的和需求。在这个例子中,
/api/admin/applications
可能意味着这是一个供管理员使用的接口,用于管理应用程序。 -
定义资源和操作:确定接口将处理哪些资源(在这个例子中是“applications”)以及支持哪些操作(如获取列表、创建、更新、删除等)。
-
设计数据模型:根据接口的需求,设计数据库模型,确定需要存储哪些数据。
-
编写路由和控制器:在后端框架中,编写路由来匹配请求的URL,然后编写控制器来处理这些请求。例如,使用 Express.js 框架的 Node.js 应用可能会这样定义路由:
app.get('/api/admin/applications', adminController.listApplications); app.post('/api/admin/applications', adminController.createApplication); // 其他路由...
-
实现业务逻辑:在控制器中实现具体的业务逻辑,比如从数据库中查询数据、验证数据、处理数据等。
-
错误处理:确保接口能够妥善处理错误,并返回适当的错误信息。
-
测试:编写单元测试和集成测试来验证接口的功能和性能。
-
文档:编写API文档,说明每个接口的用途、请求参数、响应格式和可能的错误代码。
至于“api”如何理解或替换,它通常是一个固定的前缀,用来标识这是一个API接口。在实际部署时,它可能会被替换为实际的域名或子域名,例如 https://api.yourdomain.com/admin/applications
。这里的 api
可以被替换为任何符合项目命名规范的前缀,但通常保留 api
是为了明确表示这是一个供外部系统调用的接口。
在开发过程中,后端开发者需要与前端开发者紧密合作,确保接口的设计符合前端的需求,并且能够被前端正确地调用和处理。
14.如果接口都是这样设计的,那么我是不是可以全部取掉api?
在设计RESTful API时,使用 /api
作为前缀是一种常见的做法,它有助于区分API端点和其他类型的URL。然而,是否使用 /api
前缀并不是强制性的,它更多的是一个约定俗成的习惯。以下是一些考虑因素:
-
一致性:如果你的团队或项目中已经采用了
/api
作为API端点的前缀,那么保持一致性是很重要的。这有助于团队成员理解和维护代码。 -
简洁性:有些开发者和团队可能倾向于更简洁的URL,因此他们可能会选择不使用
/api
前缀。这在小型项目或者内部使用的应用中可能更常见。 -
版本控制:在某些情况下,
/api
后面可以跟版本号,如/api/v1
,这样可以在不更改现有API路径的情况下引入新版本的API。 -
部署考虑:在某些部署环境中,可能需要将API部署在特定的路径下,这时
/api
前缀可以帮助明确API的部署路径。 -
CORS(跨源资源共享):如果你的API需要支持跨域请求,那么使用
/api
前缀可以帮助你更容易地配置CORS策略。
如果你决定不使用 /api
前缀,你的端点可能看起来像这样:
GET /admin/applications POST /admin/applications
在决定是否使用 /api
前缀时,你应该考虑你的项目需求、团队偏好以及未来的可维护性。如果你的项目规模较小,或者你预计不会有多个版本的API,那么省略 /api
前缀可能是合理的。但是,如果你预计项目会增长,或者你需要更清晰地区分API端点和非API端点,那么保留 /api
前缀可能是更好的选择。
最后,无论是否使用 /api
前缀,重要的是确保你的API设计遵循RESTful原则,如使用资源名称、HTTP方法和状态码来清晰地表达API的行为。
15.例如,非API端点可能包括:
/:网站的主页。 /login:用户登录页面。 /contact:联系我们页面。 /search:执行搜索的页面或端点。这样的话就是后端不需要对这些api进行开发?
实际上,即使是非API端点,后端通常也需要进行一定的开发工作,因为这些端点可能涉及到服务器端的逻辑处理、数据库交互、用户认证、数据检索等。以下是一些常见非API端点的后端开发任务:
-
主页(/):
-
后端可能需要从数据库中检索特定的内容,如最新文章、推荐产品等。
-
可能需要处理用户认证状态,以决定显示给用户的内容。
-
-
登录页面(/login):
-
后端需要处理用户的登录请求,包括验证用户的凭证。
-
可能需要与数据库交互,检索用户信息。
-
需要处理会话管理和用户认证。
-
-
联系我们页面(/contact):
-
后端可能需要处理用户提交的表单数据,如发送电子邮件或存储用户的联系信息。
-
可能需要验证表单数据的有效性。
-
-
搜索页面或端点(/search):
-
后端需要处理用户的搜索请求,可能涉及到复杂的查询逻辑和数据库搜索。
-
需要优化搜索结果,提供相关性和性能。
-
即使这些端点不直接用于数据的CRUD操作,后端开发仍然需要确保这些端点能够正确地处理用户请求,并提供所需的功能。后端开发不仅仅是关于API端点的开发,它还包括了整个Web应用的后端逻辑,确保应用的稳定性、安全性和可用性。
此外,后端开发还涉及到服务器配置、安全性措施(如防止SQL注入、跨站脚本攻击等)、性能优化、错误处理和日志记录等方面。因此,即使是非API端点,后端开发也是不可或缺的一部分。
*16.在进行开发时会遇到controller同名的时候,这时如果不处理,那么在容器中,组件会发生冲突。具体说明冲突的原因,如何解决冲突?(除了给controller改名字)
17.在接口文档中,如何将用户端和管理端区分开?
在web的配置类中直接:
/** * 通过knife4j生成管理端接口文档 * @return */ @Bean public Docket docket1() { log.info("准备生成接口文档:"); ApiInfo apiInfo = new ApiInfoBuilder() .title("苍穹外卖项目接口文档") .version("2.0") .description("苍穹外卖项目接口文档") .build(); Docket docket = new Docket(DocumentationType.SWAGGER_2) .groupName("管理端接口") .apiInfo(apiInfo) .select() .apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin")) .paths(PathSelectors.any()) .build(); return docket; }
通过 groupName 进行分组,然后就可以在Swagger页面上展示: