请简单介绍一下你们的项目?
项目是一个资讯类的项目
你们项目的技术架构是怎样的?
项目架构:
最外层是nginx,通过nginx将请求转发到网关,用于项目有三个前端,所以设计了三个网关,通过网关路由到具体微服务。
微服务方面,由nacos管理微服务,和微服务中的配置,微服务与微服务之间的调用通过fegin来调用。
通过seata来解决分布式事务问题,通过xxljob来实现定时任务,通过RabbitMq来实现异步微服务之间的调用,通过elk来做分布式日志
数据库方面,mysql用来存储核心数据,比如用户信息等。
redis用来做热点缓存,比如热点新闻
MongoDB用来做评论之间的增删改查,文章的点赞、收藏之类的。
将所有文件存在了oss当中
es,把所有文章放在es一份,当搜索是使用es搜索,效率更高
文章的审核用到了阿里云的内容安全
实名认证审核用到了用友云
你们Springboot及SpringCloud使用的版本?
springboot2.3.9,springcloudHoxton.SR8
你是如何理解前后端分离开发的?
需求分析,梳理用户需求,分析业务流程
根据需求分析定义接口,定义出接口文档
前端通过前端技术,比如Node.js,Vue框架,webpack打包工具,饿了么ui框架等等,将页面搭建起来
后端提供每个微服务的接口
前后端通过APi文档进行对接
两者是并行开发的 ,
前后端都写完后,进行前后端联调
项目上线
你们后端接口是如何测试的?
1、单元测试
2、PostMan
3、knife4j
4、Swagger
SpringMVC接收参数的注解有哪些?
@RequestBody @RequestParam @PathVaribale
你们的接口文档是如何定义的:
根据业务需求,和前端进行讨论,后端通过swagger框架+knife4j框架生成的接口文档
你们项目的数据库设计情况? 你是否有独立设计过数据库?
根据不同的微服务设计了不同的数据库,不同的表,大概100来张。
目前还没有独立的设计过数据库,但是参与过。如果让我独立设计,我会先根据原型,梳理出具体的业务模块,根据业务模块之间的关系梳理出不同表之间的关系,在PowerDesigner上创建出对应的表 和 表字段。
能否说出SpringBoot的自动装配原理?
@SpringBootApplication注解由三个注解共同完成自动转配
@SpringBootConfiguration:声明启动类为配置类
@EnableAutoConfiguration通过 @Import 注解导入 AutoConfigurationImportSelector类,然后通过AutoConfigurationImportSelector 类的 selectImports 方法去读取需要被自动装配的组件依赖下的spring.factories文件配置的组件的类全名,并按照一定的规则过滤掉不符合要求的组件的类全名,将剩余读取到的各个组件的类全名集合返回给IOC容器并将这些组件注册为bean
@ComponentScan: 扫描启动类所在的包以及子包下所有标记为Bean的组件并注册到IOC容器中
项目中是否自定义过starter起步依赖,如何定义?
将需要定义为起步依赖的模块,在其resources下新建META-INF包,在包下新建spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.heima.common.exception.ExceptionCatch //类的全路径名
项目中异常是怎么处理的?
设计了,全局异常处理类,使用@ControllerAdvice Springmvc 异常处理拦截注解标注,配合@ExceptionHandler注解,在该注解上指定异常类型。当controller层抛出对应的异常时,方法就会执行。可以在通用异常处理类中定义不同的异常,来处理不同的异常
项目中注册中心的作用?
1、注册服务
2、发现服务
3、服务的健康检测
4、服务中的配置管理
项目中api网关的作用?
可以对请求进行过滤、拦截,身份认证
通过路由规则,将请求分发到指定的服务中
还可以解决前后端跨域问题
filters:
stripPrefix: 去除前缀的过滤器 1
http://localhost:6001/admin/api/v1/channel/list
http://localhost:9001/admin/api/v1/channel/list
==> 去除前缀: /admin
http://localhost:9001/api/v1/channel/list
1、接口版本规范说明:
随着业务的复杂,同一个接口可能出现多个版本,为了方便后期切换和AB测试,需要定义接口的版本号
在某一个微服务下访问controller的时候在包名下加一个版本号,如下
com.heima.article.controller.v1
访问具体的接口方法的url映射的时候也应该加上版本说明,如下:
@RequestMapping("/api/v1/article")
2、业务中需要的实体类
将这些类抽取出来单独放在一个模块中,在模块中分别建好没一个微服务的包,用来存储对应微服务的实体类,在微服务的父工程中引入依赖即可
在微服务的父工程中引入依赖:
Swagger介绍:
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(https://swagger.io/)。 它的主要作用是:
- 使得前后端分离开发更加方便,有利于团队协作
- 接口的文档在线自动生成,降低后端开发人员编写接口文档的负担
功能测试 Spring已经将Swagger纳入自身的标准,建立了Spring-swagger项目,现在叫Springfox。通过在项目中引入Springfox ,即可非常简单快捷的使用Swagger。
使用步骤:
在父工程中引入依赖:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>在 heima-leadnews-model 模块中引入该依赖:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency>在具体的微服务工程的config包中添加一个配置类:
@Configuration @EnableSwagger2 public class SwaggerConfiguration { @Bean public Docket buildDocket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(buildApiInfo()) .select() // 要扫描的API(Controller)基础包 .apis(RequestHandlerSelectors.basePackage("com.heima")) .paths(PathSelectors.any()) .build(); } private ApiInfo buildApiInfo() { Contact contact = new Contact("黑马程序员","",""); return new ApiInfoBuilder() .title("黑马头条-平台管理API文档") .description("平台管理服务api") .contact(contact) .version("1.0.0").build(); } }Swagger常用注解
在Java类中添加Swagger的注解即可生成Swagger接口文档,常用Swagger注解如下:
@Api:修饰整个类,描述Controller的作用
@ApiOperation:描述一个类的一个方法,或者说一个接口
@ApiParam:单个参数的描述信息
@ApiModel:用对象来接收参数
@ApiModelProperty:用对象接收参数时,描述对象的一个字段
@ApiResponse:HTTP响应其中1个描述
@ApiResponses:HTTP响应整体描述
@ApiIgnore:使用该注解忽略这个API
@ApiError :发生错误返回的信息
@ApiImplicitParam:一个请求参数
@ApiImplicitParams:多个请求参数的描述信息在Controller层上添加注解
@RestController @RequestMapping("/api/v1/channel") @Api(value = "频道管理", tags = "频道管理", description = "频道管理API") public class AdChannelController { /** * 根据名称分页查询频道列表 * @param dto * @return */ @PostMapping("/list") @ApiOperation("频道分页列表查询") public ResponseResult findByNameAndPage(@RequestBody ChannelDTO dto) { return channelService.findByNameAndPage(dto); } }ChannelDTO:
@Data public class ChannelDTO extends PageRequestDTO { /** * 频道名称 */ @ApiModelProperty("频道名称") private String name; }PageRequestDTO:
@Data @Slf4j public class PageRequestDTO { @ApiModelProperty(value="当前页",required = true) protected Integer page; @ApiModelProperty(value="每页显示条数",required = true) protected Integer size; public void checkParam() { if (this.page == null || this.page < 0) { setPage(1); } if (this.size == null || this.size < 0 || this.size > 100) { setSize(10); } } }启动a微服务,访问地址:http://微服务ip:微服务端口/swagger-ui.html
knife4j:
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!
Swagger的注解在它上面也能用使用步骤:
在heima-leadnews-basic 模块下,右键创建子工程 heima-knife4j-spring-boot-starter, 并引入依赖
<dependencies> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> </dependency> </dependencies>在heima-leadnews-basic —> heima-knife4j-spring-boot-starter下 创建Swagger配置文件
新建Swagger的配置文件SwaggerConfiguration.java文件,创建springfox提供的Docket分组对象,代码如下: ```java package com.heima.knife4j.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration @EnableSwagger2 @EnableKnife4j @Import(BeanValidatorPluginsConfiguration.class) public class Swagger2Configuration {
@Bean(value = "defaultApi2")
public Docket defaultApi2() {
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
//分组名称
.groupName("1.0")
.select()
//这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("com.heima"))
.paths(PathSelectors.any())
.build();
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("黑马头条API文档")
.description("黑马头条API文档")
.version("1.0")
.build();
}
}
在heima-leadnews-basic --> heima-knife4j-spring-boot-starter 资源目录resources文件夹下添加<br />META-INF/spring.factories 文件,这样微服务引入依赖后,即可自动装配
```properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.heima.knife4j.config.Swagger2Configuration
heima-leadnews-services 下的pom中,加入该 knife4j通用配置依赖 ,所有的微服务即可使用api文档生成功能
<!-- 接口文档起步依赖starter 引入后可使用knife接口文档 -->
<dependency>
<artifactId>heima-knife4j-spring-boot-starter</artifactId>
<groupId>com.heima</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
http://host:port/doc.html:访问地址
项目全局异常处理
不可预知异常:
在heima-leadnews-basic模块下 新建heima-exception-spring-boot-starter 工程
(1) pom中引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-leadnews-model</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
(2) 创建通用异常处理类 com.heima.common.exception.ExceptionCatch
package com.heima.common.exception;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @Description: 目的是给用户提供友好的提示信息
* @Version: V1.0
*/
@Slf4j
@Configuration
@RestControllerAdvice // Springmvc 异常处理拦截注解
public class ExceptionCatch {
/**
* 解决项目中所有的异常拦截
* @return
*/
@ExceptionHandler(Exception.class) // exception 所有子类
public ResponseResult exception(Exception ex) {
ex.printStackTrace();
// 记录日志
log.error("ExceptionCatch ex:{}", ex);
return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "您的网络异常,请稍后重试");
}
}
@ControllerAdvice 控制器增强注解
@ExceptionHandler 异常处理器 与上面注解一起使用,可以拦截指定的异常信息
在 META-INF/spring.factories 配置文件中添加:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.heima.common.exception.ExceptionCatch
(3) heima-leadnews-services服务聚合工程引入统一异常依赖
<!-- 统一异常处理依赖 引入后就不用在try catch异常啦 ~~ -->
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-exception-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
8.2 可预知异常处理
package com.heima.common.exception;
import com.heima.model.common.enums.AppHttpCodeEnum;
public class CustomException extends RuntimeException {
// 异常处理的枚举
private AppHttpCodeEnum appHttpCodeEnum;
public CustomException(AppHttpCodeEnum appHttpCodeEnum) {
this.appHttpCodeEnum = appHttpCodeEnum;
}
public CustomException(AppHttpCodeEnum appHttpCodeEnum,String msg) {
appHttpCodeEnum.setErrorMessage(msg);
this.appHttpCodeEnum = appHttpCodeEnum;
}
public AppHttpCodeEnum getAppHttpCodeEnum() {
return appHttpCodeEnum;
}
}
配置到全局异常处理
package com.heima.common.exception;
/**
* @Description: 目的是给用户提供友好的提示信息
* @Version: V1.0
*/
@Slf4j
@RestControllerAdvice // Springmvc 异常处理拦截注解
public class ExceptionCatch {
/**
* 解决项目中所有的异常拦截
* @return
*/
@ExceptionHandler(Exception.class) // exception 所有子类
public ResponseResult exception(Exception ex) {
ex.printStackTrace();
// 记录日志
log.error("ExceptionCatch ex:{}", ex);
return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "您的网络异常,请稍后重试");
}
/**
* 拦截自定义异常
* @return
*/
@ExceptionHandler(CustomException.class)
public ResponseResult custException(CustomException ex) {
ex.printStackTrace();
log.error("CustomException ex:{}", ex);
AppHttpCodeEnum codeEnum = ex.getAppHttpCodeEnum();
return ResponseResult.errorResult(codeEnum);
}
}
