快速搭建一个SpringCloud、SpringBoot项目 || 项目搭建要点
1. 基本结构
- 建立springcloud项目从表入手,分析好需求建立表结构后,使用mybatis-plux生成POJO类,在对应的model模块中。
2. 微服务部分架构
2.1 依赖
- service 微服务模块的依赖仅包含如下,数据库等依赖包含在model中,service引入了model模块,因此也可以调用数据库服务。
<dependencies>
<!-- 引入依赖模块,有model和common -->
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-leadnews-model</artifactId>
</dependency>
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-leadnews-common</artifactId>
</dependency>
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-leadnews-feign-api</artifactId>
</dependency>
<!-- Spring boot starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
2.2 配置
- 建立UserApplication启动类
@SpringBootApplication 注解是在 Spring Boot 应用程序的入口类上使用的,它组合了多个注解的功能,包括 @Configuration、@EnableAutoConfiguration 和 @ComponentScan。
@EnableDiscoveryClient 注解用于在 Spring Cloud 应用中启用服务发现功能,它会向服务注册中心注册服务,并且能够从服务注册中心获取其他服务的信息。
@MapperScan 注解是 MyBatis 框架提供的,在 Spring Boot 项目中使用它可以指定要扫描的 MyBatis Mapper 接口的包路径,以便 MyBatis 能够找到这些接口并创建对应的代理对象。具体来说,当使用 @MapperScan 注解指定了要扫描的 Mapper 接口所在的包路径后,MyBatis 在启动时会扫描这些包,找到其中的接口,并根据接口中的方法定义和注解来自动生成对应的实现类(代理对象)。这些代理对象会通过动态代理的方式将接口中的方法映射为对数据库的具体操作,从而实现了接口方法与 SQL 操作的绑定。
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.user.mapper")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
-
建立微服务配置文件bootstrap.yml
bootstrap.yml 是 Spring Cloud 微服务架构中用于进行应用程序初始化的配置文件。与 application.yml 或 application.properties 不同,bootstrap.yml 主要用于以下两个方面:-
用于连接配置服务器:在微服务架构中,通常会使用配置中心(如Spring Cloud Config Server)来集中管理各个微服务的配置信息。bootstrap.yml 中的配置将会在应用程序启动时最先被加载,用于连接配置服务器并获取应用程序所需的配置信息。
-
用于加载外部配置:除了连接配置服务器外,bootstrap.yml 中还可以定义一些应用程序启动时所需的基础配置,例如连接数据库、设置日志级别等。这些配置项将在应用程序启动时优先加载,以确保应用程序能够正常初始化。
-
server:
port: 51801
spring:
application:
name: leadnews-user
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yml
bootstrap.yml 仅仅加了基本的配置,在nacos中继续配置,这里面的mysql、mybatis-plus的依赖就是2.1说的依赖
后期取出依赖使用@Value注解取出,例如下面
2.3 客户端通过post请求访问后端程序(controller)
在微服务的controller层中进行对应配置,让外界能够访问通提供的服务。注意四个注解
前端发送的必须是Post请求的请求体,且必须要是json格式(@RequestBody注解用于从HTTP请求体中获取数据,并将其映射到方法的参数上。通常与Spring框架中的RESTful风格的控制器方法一起使用,用于接收来自客户端的JSON或XML格式的数据。)
@RestController
@RequestMapping("/api/v1/login")
public class UserController {
@PostMapping("/login_auth")
public ResponseResult login(@RequestBody LoginDto loginDto){
String phone = loginDto.getPhone();
String password = loginDto.getPassword();
System.out.println(phone + " , " + password);
return ResponseResult.okResult("token");
}
}
查看bootstrap中配置文件配置的服务端口:51801
访问:
2.4 使用mybatis_plus访问数据库(sercvice、mapper)
- 在model部分构建实体类,以及实体类的Dto对象。实体类其实就是表中的接口,可以直接使用mp生成代码,而dto就是data transform object,仅保留实际需要交互的。如用户登入仅需要前端传输 手机号、密码 即可。
- 在2.3 部分,前端已经可以访问通controller层了,现在需要访问service层和mapper层。其实可以直接代码生成,这里细致一点。
mapper:
@Mapper
public interface ApUserMapper extends BaseMapper<ApUser> {}
serviceInterface:
public interface ApUserLoginService extends IService<ApUser> {
public ResponseResult login(LoginDto loginDto);
}
serviceImpl:
@Service
@Transactional
@Slf4j //加了之后可以直接使用log对象来输出日志,而无需手动创建和初始化Logger对象。
public class ApUserLoginServiceImpl extends ServiceImpl<ApUserMapper,ApUser> implements ApUserLoginService {
@Override
public ResponseResult login(LoginDto loginDto) {...}
}
- 在service中如何访问数据库?原理是什么?
简单一句话访问数据库
ApUser dbUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));
能直接调用getOne的原因:serviceInterface中 extends IService,IService中有getOne 方法,而Interface的实现类Impl那必然也能够直接访问这个子类方法。
下面是IService中的源码
参数: Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, dto.getPhone())
其中Wrappers 是一个类
-
Wrappers.:Wrappers是 MyBatis-Plus 提供的用于构建查询条件的工具类,部分表示指定操作的实体类为ApUser。这里使用了泛型的语法,用于在运行时指定实体类的类型。
-
lambdaQuery():这是 MyBatis-Plus 中的一种链式调用方式,用于创建一个 LambdaQueryWrapper 对象,LambdaQueryWrapper 是用于构建带有 Lambda 表达式的查询条件的工具类。
-
.eq(ApUser::getPhone, dto.getPhone()):这是 LambdaQueryWrapper 的方法之一,表示添加一个等于条件。在这个例子中,它表示查询ApUser表中phone字段等于dto.getPhone()的记录。
3. common模块部分
application.yml
# 全局端口定义server:
# 全局字符集设置
spring:
http:
encoding:
force: true
charset: utf-8
enabled: true
aop:
proxy-target-class: true
server:
tomcat:
uri-encoding: utf-8
3.1 springboot自动装配,使用 META-INF/spring.factories
springboot自动配置(简单来说就是自动把第三方组件的bean装载到IOC容器中,不需要开发人员再去写bean相关的一些配置,只要在启动类中加上@SpringbootApplication注解就可以实现自动装配。约定是第三方的starter启动依赖组件里面,必须有一个configuration配置类,在配置类里面声明@Bean代表是要被注册到IOC容器中的bean)
- 当使用 META-INF/spring.factories 来自动加载 Swagger 配置时,我们需要遵循以下步骤:
创建 Swagger 配置类:编写一个配置类,用于配置 Swagger 相关的信息,例如扫描的包路径、API 文档信息、文档页面的访问路径等。这个配置类通常使用 @EnableSwagger2 或 @EnableSwagger3 注解来启用 Swagger。
package com.heima.common.swagger;
@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();
}
}
将配置类添加到 META-INF/spring.factories 文件中:在 META-INF 目录下创建一个名为 spring.factories 的文件,然后将配置类的全限定名添加到文件中,以实现自动加载和执行 Swagger 相关的配置。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.config.SwaggerConfig
在上面的示例中,com.example.config.SwaggerConfig 是配置类的全限定名,通过将它添加到 spring.factories 文件中,Spring Boot 在启动时会自动加载和执行这个配置类,从而实现对 Swagger 的自动配置和集成。
总的来说,使用 META-INF/spring.factories 来自动加载 Swagger 配置的过程包括创建配置类并将其添加到 spring.factories 文件中,这样在应用程序启动时就会自动加载并执行 Swagger 相关的配置,无需手动初始化和配置,提高了集成的便捷性和效率。
- 如果不使用 META-INF/spring.factories 来自动加载 Swagger 配置,你可以手动加载 SwaggerConfig 配置类。以下是一般的手动加载步骤:
在主应用程序类上添加 @Import 注解:打开主应用程序类(通常是带有 @SpringBootApplication 注解的类),并在类上方添加@Import(SwaggerConfig.class) 注解,将 SwaggerConfig 手动引入到应用程序中。
@SpringBootApplication
@Import(SwaggerConfig.class)
public class YourApplication {
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
创建 Swagger 配置类:创建一个配置类,用于配置 Swagger 相关的信息,例如扫描的包路径、API 文档信息、文档页面的访问路径等。这个配置类应该使用 @Configuration 和 @EnableSwagger2 注解来标识。
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.controllers"))
.paths(PathSelectors.any())
.build();
}
}
启动应用程序:启动应用程序后,Swagger 将会根据手动引入的 SwaggerConfig 类进行配置,并生成 API 文档信息。
通过以上步骤,你可以手动加载 SwaggerConfig 配置类,实现对 Swagger 的手动配置和集成。这种方式能够明确地控制配置类的加载过程,并且适用于一些特定的场景和需求。
3.2 接口工具 swagger、knife4j
Spring已经将Swagger纳入自身的标准,建立了Spring-swagger项目,现在叫Springfox。通过在项目中引入Springfox ,即可非常简单快捷的使用Swagger。
- 引入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
- knife4j配置
package com.heima.common.swagger;
@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();
}
}
- 在工程中进行配置(common模块下)
文件:resources/META-INF/Spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.heima.common.exception.ExceptionCatch,\
com.heima.common.swagger.SwaggerConfiguration,\
com.heima.common.swagger.Swagger2Configuration
- 在微服务模块对应controller与Model中的Dto中加上对应的注解
我们在ApUserLoginController中添加Swagger注解,代码如下所示:
@RestController
@RequestMapping("/api/v1/login")
@Api(value = "app端用户登录", tags = "ap_user", description = "app端用户登录API")
public class ApUserLoginController {
@Autowired
private ApUserService apUserService;
@PostMapping("/login_auth")
@ApiOperation("用户登录")
public ResponseResult login(@RequestBody LoginDto dto){
return apUserService.login(dto);
}
}
LoginDto
@Data
public class LoginDto {
/**
* 手机号
*/
@ApiModelProperty(value="手机号",required = true)
private String phone;
/**
* 密码
*/
@ApiModelProperty(value="密码",required = true)
private String password;
}
启动user微服务,访问地址:http://localhost:51801/swagger-ui.html
3.3 全局异常处理
- 全局异常处理:@ControllerAdvice 注解可以配合 @ExceptionHandler 注解,用于统一处理应用程序中抛出的异常。通过在@ControllerAdvice类中定义@ExceptionHandler方法,可以捕获特定类型的异常,并进行统一的处理,比如返回特定的错误页面或者 JSON 响应。
@ControllerAdvice //控制器增强类,就是标明,这个类就是用来统一处理异常的一个类
@Slf4j
public class ExceptionCatch {
@ExceptionHandler(Exception.class) //里面加上想要捕获的异常
@ResponseBody
public ResponseResult exception(Exception e){
e.printStackTrace();
log.error("catch exception:{}",e.getMessage());
return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
}
/**
* 处理可控异常 自定义异常
* @param e
* @return
*/
@ExceptionHandler(CustomException.class) //想捕获的异常是自定义异常
@ResponseBody
public ResponseResult exception(CustomException e){
log.error("catch exception:{}",e);
return ResponseResult.errorResult(e.getAppHttpCodeEnum());
}
}
当遇到异常被try catch抛出的时候会被自定义类全局异常类捕获,进行我们自定义的操作,就不会返回一个500错误页面,而是自定义的页面
4. 网关部分
4.1 pom文件导入配置类
4.2 在heima-leadnews-gateway下创建heima-leadnews-app-gateway微服务,写好Application启动类,bootstrap.yml 配置文件,在nacos中写yml配置(如下)
spring:
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true
corsConfigurations:
'[/**]':
allowedHeaders: "*"
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- DELETE
- PUT
- OPTION
routes:
# 平台管理
- id: user
uri: lb://leadnews-user
predicates:
- Path=/user/**
filters:
- StripPrefix= 1
- id: user:给这个路由规则指定一个唯一的标识符。
- uri: lb://leadnews-user:将匹配到的请求转发到名为leadnews-user的负载均衡服务,lb://表示使用负载均衡。
- predicates:定义路由规则的谓词(predicate),这里使用Path=/user/**表示只有请求路径以/user/开头的请求会匹配到这个路由规则。
- filters:配置过滤器,这里使用StripPrefix=1表示去除请求路径中的第一个路径段,例如将/user/foo转发到目标服务时会变成/foo。
通过上述配置,当有请求路径以/user/开头时,Spring Cloud Gateway会将该请求转发到leadnews-user服务,并去除请求路径中的第一个路径段。你可以根据实际需求添加更多的路由规则和过滤器来构建复杂的网关功能。
客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则会将其发送到网关Web处理程序。此处理程序通过特定于请求的筛选器链来运行请求。过滤器被虚线分隔的原因是过滤器可以在发送代理请求之前和之后运行逻辑。执行所有“预”过滤器逻辑。然后进行代理请求。在发出代理请求后,将运行“post”过滤器逻辑。
4.3 全局过滤器JWT
在Spring Cloud Gateway中,你可以使用两种方式来定义过滤器链:
filters属性:通过在路由配置中使用filters属性,你可以直接在路由配置中指定过滤器集合。这些过滤器仅适用于特定的路由。示例如下:
spring:
cloud:
gateway:
routes:
- id: user
uri: lb://leadnews-user
predicates:
- Path=/user/**
filters:
- StripPrefix=1
# 其他过滤器...
在上述示例中,filters属性定义了一个过滤器集合,其中包含了StripPrefix过滤器。
GlobalFilter接口:通过实现GlobalFilter接口来编写全局过滤器,全局过滤器会应用于所有进入网关的请求。全局过滤器能够对所有路由生效,并且可以在请求和响应阶段进行修改或添加额外的处理逻辑。
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
// 实现过滤器逻辑...
}
- 总结一下两者的区别:
filters属性用于在路由配置中为特定路由指定过滤器集合,仅对指定路由生效。
GlobalFilter接口用于定义全局过滤器,对所有路由生效。
你可以根据具体需求选择使用哪种方式来配置和定义过滤器。如果需要对所有路由应用相同的过滤逻辑,可以使用全局过滤器。如果只需对特定路由应用特定的过滤器链,可以使用filters属性。