首页 > 其他分享 >新版Spring Cloud Alibaba与Springbooot3.0搭建后端架构

新版Spring Cloud Alibaba与Springbooot3.0搭建后端架构

时间:2023-04-15 20:33:27浏览次数:39  
标签:java CommonResp Spring train Alibaba member Springbooot3.0 return public

新增member会员模块

创建member模块,添加依赖

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <parent>
 6         <artifactId>train</artifactId>
 7         <groupId>com.jiawa</groupId>
 8         <version>0.0.1-SNAPSHOT</version>
 9     </parent>
10     <modelVersion>4.0.0</modelVersion>
11 
12     <artifactId>member</artifactId>
13 
14     <properties>
15         <maven.compiler.source>17</maven.compiler.source>
16         <maven.compiler.target>17</maven.compiler.target>
17     </properties>
18 
19     <dependencies>
20         <dependency>
21             <groupId>org.springframework.boot</groupId>
22             <artifactId>spring-boot-starter-web</artifactId>
23         </dependency>
24         <dependency>
25             <groupId>org.springframework.cloud</groupId>
26             <artifactId>spring-cloud-starter</artifactId>
27         </dependency>
28 
29         <dependency>
30             <groupId>org.springframework.boot</groupId>
31             <artifactId>spring-boot-devtools</artifactId>
32             <scope>runtime</scope>
33             <optional>true</optional>
34         </dependency>
35         <dependency>
36             <groupId>org.springframework.boot</groupId>
37             <artifactId>spring-boot-starter-test</artifactId>
38             <scope>test</scope>
39         </dependency>
40     </dependencies>
41 
42     <build>
43         <plugins>
44             <plugin>
45                 <groupId>org.springframework.boot</groupId>
46                 <artifactId>spring-boot-maven-plugin</artifactId>
47             </plugin>
48         </plugins>
49     </build>
50 
51 </project>
pom.xml

创建启动类MemberApplication.java

 1 package com.zihans.train.member.config;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.context.annotation.ComponentScan;
 6 
 7 @SpringBootApplication
 8 @ComponentScan("com.jiawa")
 9 public class MemberApplication {
10 
11     public static void main(String[] args) {
12         SpringApplication.run(MemberApplication.class, args);
13     }
14 
15 }
MemberApplication.java

实现日志的相关配置

在入口类中添加日志信息,提醒启动成功!

 1 @SpringBootApplication
 2 @ComponentScan("com.zihans")
 3 public class MemberApplication {
 4 
 5     private static final Logger LOG = LoggerFactory.getLogger(MemberApplication.class);
 6     public static void main(String[] args) {
 7         //SpringApplication.run(MemberApplication.class, args);
 8         SpringApplication app = new SpringApplication(MemberApplication.class);
 9         Environment env = app.run(args).getEnvironment();
10         LOG.info("启动成功!!");
11         LOG.info("测试地址: \thttp://127.0.0.1:{}/{}/hello", env.getProperty("server.port"), env.getProperty("server.servlet.context-path"));
12     }
13 }
MemberApplication.java

然后在配置文件中添加context-path路径,此时请求地址要改为127.0.0.1:8001/member/hello,这是为了后面做路由转发,在网关模块中,将接口带有"/member"的请求都转发到member模块。

1 server:
2   port: 8001
3   servlet:
4     context-path: /member
application.yml

resources中创建日志文件,方便查找日志

日志内容↓

分:秒:毫秒  日志级别  类名(30字符)  行号  线程  日志

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <configuration>
 3     <!-- 修改一下路径-->
 4     <property name="PATH" value="./log/member"></property>
 5 
 6     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
 7         <encoder>
 8             <!--            <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %blue(%-50logger{50}:%-4line) %thread %green(%-18X{LOG_ID}) %msg%n</Pattern>-->
 9             <Pattern>%d{mm:ss.SSS} %highlight(%-5level) %blue(%-30logger{30}:%-4line) %thread %green(%-18X{LOG_ID}) %msg%n</Pattern>
10         </encoder>
11     </appender>
12 
13     <appender name="TRACE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
14         <file>${PATH}/trace.log</file>
15         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
16             <FileNamePattern>${PATH}/trace.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
17             <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
18                 <maxFileSize>10MB</maxFileSize>
19             </timeBasedFileNamingAndTriggeringPolicy>
20         </rollingPolicy>
21         <layout>
22             <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-50logger{50}:%-4line %green(%-18X{LOG_ID}) %msg%n</pattern>
23         </layout>
24     </appender>
25 
26     <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
27         <file>${PATH}/error.log</file>
28         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
29             <FileNamePattern>${PATH}/error.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
30             <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
31                 <maxFileSize>10MB</maxFileSize>
32             </timeBasedFileNamingAndTriggeringPolicy>
33         </rollingPolicy>
34         <layout>
35             <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-50logger{50}:%-4line %green(%-18X{LOG_ID}) %msg%n</pattern>
36         </layout>
37         <filter class="ch.qos.logback.classic.filter.LevelFilter">
38             <level>ERROR</level>
39             <onMatch>ACCEPT</onMatch>
40             <onMismatch>DENY</onMismatch>
41         </filter>
42     </appender>
43 
44     <root level="ERROR">
45         <appender-ref ref="ERROR_FILE" />
46     </root>
47 
48     <root level="TRACE">
49         <appender-ref ref="TRACE_FILE" />
50     </root>
51 
52     <root level="INFO">
53         <appender-ref ref="STDOUT" />
54     </root>
55 </configuration>
logback-spring.xml

使用HTTP Client完成测试接口

 IDEA自带Http Client插件(Tools -> HTTP Client -> Create Request in HTTP Client),可以在IDEA中直接发起http调用

或者直接创建一个http文件

1 GET http://localhost:8001/member/hello
2 Accept: application/json
member-test.http

增加AOP打印请求参数和返回结果 

每个请求都要打印日志,方便维护和检查。最简单的方法是每一个接口都增加一个返回日志,但是这样会更改每一个接口,有侵入性,很不方便。最简单的方法是使用AOP或拦截器进行请求拦截。AOP可以拦截controller,也可以拦截service等其他类,拦截器只能拦截controller。

在根目录以及member目录增加依赖

 1 <dependency>
 2         <groupId>com.alibaba</groupId>
 3         <artifactId>fastjson</artifactId>
 4         <version>1.2.70</version>
 5 </dependency>
 6 
 7 <dependency>
 8         <groupId>cn.hutool</groupId>
 9         <artifactId>hutool-all</artifactId>
10         <version>5.6.3</version>
11 </dependency>
pom.xml

增加类LogAspect

 1 @Aspect
 2 @Component
 3 public class LogAspect {
 4     public LogAspect() {
 5         System.out.println("LogAspect");
 6     }
 7 
 8     private final static Logger LOG = LoggerFactory.getLogger(LogAspect.class);
 9 
10     /**
11      * 定义一个切点
12      */
13     @Pointcut("execution(public * com.zihans..*Controller.*(..))")
14     public void controllerPointcut() {
15     }
16 
17     @Before("controllerPointcut()")
18     public void doBefore(JoinPoint joinPoint) {
19 
20         // 增加日志流水号
21         MDC.put("LOG_ID", System.currentTimeMillis() + RandomUtil.randomString(3));
22 
23         // 开始打印请求日志
24         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
25         HttpServletRequest request = attributes.getRequest();
26         Signature signature = joinPoint.getSignature();
27         String name = signature.getName();
28 
29         // 打印请求信息
30         LOG.info("------------- 开始 -------------");
31         LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
32         LOG.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);
33         LOG.info("远程地址: {}", request.getRemoteAddr());
34 
35         // 打印请求参数
36         Object[] args = joinPoint.getArgs();
37         // LOG.info("请求参数: {}", JSONObject.toJSONString(args));
38 
39         // 排除特殊类型的参数,如文件类型
40         Object[] arguments = new Object[args.length];
41         for (int i = 0; i < args.length; i++) {
42             if (args[i] instanceof ServletRequest
43                     || args[i] instanceof ServletResponse
44                     || args[i] instanceof MultipartFile) {
45                 continue;
46             }
47             arguments[i] = args[i];
48         }
49         // 排除字段,敏感字段或太长的字段不显示:身份证、手机号、邮箱、密码等
50         String[] excludeProperties = {"mobile"};
51         PropertyPreFilters filters = new PropertyPreFilters();
52         PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
53         excludefilter.addExcludes(excludeProperties);
54         LOG.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter));
55     }
56 
57     @Around("controllerPointcut()")
58     public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
59         long startTime = System.currentTimeMillis();
60         Object result = proceedingJoinPoint.proceed();
61         // 排除字段,敏感字段或太长的字段不显示:身份证、手机号、邮箱、密码等
62         String[] excludeProperties = {"mobile"};
63         PropertyPreFilters filters = new PropertyPreFilters();
64         PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
65         excludefilter.addExcludes(excludeProperties);
66         LOG.info("返回结果: {}", JSONObject.toJSONString(result, excludefilter));
67         LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
68         return result;
69     }
70 
71 }
LogAspect.java

此时发出请求输出

 

 项目中增加通用模块

父pom用于更新版本号,其余pom不用写版本号,交给父pom统一管理。

增加common模块,用于放公共代码。

增加公共配置文件,必须放在/config/目录下,否则无效(不是覆盖关系);优先读公共配置下的配置。

项目中增加网关模块

网关模块非常重要,可以用来做路由转发,权限校验等。可以理解为一个关卡,所有的请求到达业务模块前都要经过这个关卡,验证身份才能访问。

前端请求全部访问网关模块,后端对应的模块由它去做路由。
gateway是基于netty的,只有一个依赖,不能引入common,也不能引入starter-web

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

生产发布时,只有gateway需要配置外网IP,其它模块都只开放内网访问,外网访问不了,保证应用安全
要输出请求日志需要增加启动参数:
-Dreactor.netty.http.server.accessLogEnabled=true

 数据库准备

  • Mysql 8.0
  • 本地数据库 or  云数据库
  • 重点:专库专用,忌用root

(1)  本地数据库

  • 新增数据库train
  • 新增用户train,配置权限只能操作train数据库

(2)  云数据库

好处:

  • 免去环境搭建,版本任选

  • 方便多台电脑协作开发

  • 相当于雇了—帮运维

  • 总结:花钱买时间

(3)  使用IDEA配置数据库连接

好处:

  • 可直接执行sql脚本
  • mybatis xml 文件可以识别数据库表
  • 可以IDEA里直接操作数据库,增删改查等。

集成Mybatis持久层框架

在主配置和common配置中引入依赖

 1 <dependency>
 2         <groupId>org.mybatis.spring.boot</groupId>
 3         <artifactId>mybatis-spring-boot-starter</artifactId>
 4         <version>2.1.3</version>
 5 </dependency>
 6 
 7  <dependency>
 8         <groupId>mysql</groupId>
 9         <artifactId>mysql-connector-java</artifactId>
10         <version>8.0.30</version>
11 </dependency>

本地新增数据库train-member。新增用户train_member,配置权限只能操作train-member数据库。

每个模块都有对应数据库

1 drop table if exists member;
2 create table member (
3     id bigint not null comment 'id',
4     mobile varchar(11) comment '手机号',
5     primary key (id),
6     unique key mobile_unique (mobile)
7 ) engine=innodb default charset=utf8mb4 comment='会员';
member.sql

模块member中配置yml语句用于连接数据库

#数据库连接
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost_3306/train-member?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Nanjing
    username: member
    password: member
application.yml

 member以及其中的resources中新建包mapper,持久层代码写在mapper

 启动类添加注解

@MapperScan("com/zihans/train/member/mapper")

 创建接口MemberMapper.java以及配置文件MemberMapper.xml

1 package com.zihans.train.member.mapper;
2 
3 public interface MemberMapper {
4     int count();
5 }
MemberMapper.java
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
3 <mapper namespace="com.zihans.train.member.mapper.MemberMapper" >
4 
5     <select id="count" resultType="int">
6         select count(1) from member
7     </select>
8 
9 </mapper>
MemberMapper.xml

 继续配置yml添加mybatis  xml路径

 1 # mybatis xml路径
 2 mybatis:
 3   mapper-locations: classpath:/mapper/**/*.xml
 4   logging:
 5     level:
 6       com:
 7         zihans:
 8           train:
 9             member:
10               mapper: trace
application.yml

理解:新增一个mapper接口,在启动类添加@MapperScan注解使项目扫描该接口,然后通过xml文件写sql语句,同时通过yml配置文件使mybatis可以读到xml,通过xml中的命名空间将mapper接口和xml关联。

新建服务层包service,新建服务类MemberService,添加注解@Service使SpringBoot知道这是service类,使用注解@Resource注入MemberMapper。

 1 package com.zihans.train.member.service;
 2 import com.zihans.train.member.mapper.MemberMapper;
 3 import jakarta.annotation.Resource;
 4 import org.springframework.stereotype.Service;
 5 
 6 @Service
 7 public class MemberService {
 8     @Resource
 9     private MemberMapper memberMapper;
10     
11     public int count() {
12         return memberMapper.count();
13 
14     }
15 }
MemberService.java

新建MemberController类

 1 package com.zihans.train.member.controller;
 2 
 3 
 4 import com.zihans.train.member.service.MemberService;
 5 import jakarta.annotation.Resource;
 6 import org.springframework.web.bind.annotation.GetMapping;
 7 import org.springframework.web.bind.annotation.RequestMapping;
 8 import org.springframework.web.bind.annotation.RestController;
 9 
10 @RestController
11 @RequestMapping("/member")
12 public class MemberController {
13 
14     @Resource
15     private MemberService memberService;
16 
17     @GetMapping("/count")
18     public int count() {
19         return memberService.count();
20     }
21 }
MemberController.java

用http client测试

1 GET http://localhost:8000/member/member/count
2 Accept: application/json

输出下图,测试成功!

 

 集成Mybatis官方生成器

只能生成单表增删改查

上一部分中,几个模块都是手动生成的,开发速度太慢,所以用代码生成器生成。这里没有使用MybatisPlus,而是使用Mabstis+官方生成器(Mybatis Generator),没有必要再多用一层框架。

 创建一个generator模块,该模块不光写Mybatis官方生成器,后期还打算写一套自己的代码生成器。

在generator的依赖中引入mybatis generator插件

 1 <build>
 2         <plugins>
 3             <!-- mybatis generator 自动生成代码插件 -->
 4             <plugin>
 5                 <groupId>org.mybatis.generator</groupId>
 6                 <artifactId>mybatis-generator-maven-plugin</artifactId>
 7                 <version>1.4.0</version>
 8                 <configuration>
 9                     <configurationFile>src/main/resources/generator-config-member.xml</configurationFile>
10                     <overwrite>true</overwrite>
11                     <verbose>true</verbose>
12                 </configuration>
13                 <dependencies>
14                     <dependency>
15                         <groupId>mysql</groupId>
16                         <artifactId>mysql-connector-java</artifactId>
17                         <version>8.0.22</version>
18                     </dependency>
19                 </dependencies>
20             </plugin>
21         </plugins>
22     </build>
pom.xml

在resources中新建xml文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE generatorConfiguration
 3         PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
 4         "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
 5 
 6 <generatorConfiguration>
 7     <context id="Mysql" targetRuntime="MyBatis3" defaultModelType="flat">
 8 
 9         <!-- 自动检查关键字,为关键字增加反引号 -->
10         <property name="autoDelimitKeywords" value="true"/>
11         <property name="beginningDelimiter" value="`"/>
12         <property name="endingDelimiter" value="`"/>
13 
14         <!--覆盖生成XML文件-->
15         <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
16         <!-- 生成的实体类添加toString()方法 -->
17         <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
18 
19         <!-- 不生成注释 -->
20         <commentGenerator>
21             <property name="suppressAllComments" value="true"/>
22         </commentGenerator>
23 
24         <!-- 配置数据源,需要根据自己的项目修改 -->
25         <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
26                         connectionURL="jdbc:mysql://localhost:3306/train-member?serverTimezone=Asia/Shanghai"
27                         userId="train_member"
28                         password="member">
29         </jdbcConnection>
30 
31         <!-- domain类的位置 targetProject是相对pom.xml的路径-->
32         <javaModelGenerator targetProject="..\member\src\main\java"
33                             targetPackage="com.zihans.train.member.domain"/>
34 
35         <!-- mapper xml的位置 targetProject是相对pom.xml的路径 -->
36         <sqlMapGenerator targetProject="..\member\src\main\resources"
37                          targetPackage="mapper"/>
38 
39         <!-- mapper类的位置 targetProject是相对pom.xml的路径 -->
40         <javaClientGenerator targetProject="..\member\src\main\java"
41                              targetPackage="com.zihans.train.member.mapper"
42                              type="XMLMAPPER"/>
43 
44         <table tableName="member" domainObjectName="Member"/>
45     </context>
46 </generatorConfiguration>
generator-config-member.xml

配置好后执行mybatis-generator:generate,将生成四个文件,domain目录下的Member.java、MemberExample.java,mapper目录下的MemberMapper.interface以及资源目录下的MemberMapper.xml。这四个文件不能手动更改,因为容易被重新覆盖。自定义SQL需要写到自己的mapper中,不能放在生成的mapper。

修改MemberService.java文件进行测试

 1 package com.zihans.train.member.service;
 2 import com.zihans.train.member.mapper.MemberMapper;
 3 import jakarta.annotation.Resource;
 4 import org.springframework.stereotype.Service;
 5 
 6 @Service
 7 public class MemberService {
 8     @Resource
 9     private MemberMapper memberMapper;
10 
11     public int count() {
12         return Math.toIntExact(memberMapper.countByExample(null));
13 
14     }
15 }
MemberService.java

测试成功!

 

 

 完成会员注册接口的开发

MemberService.java新增方法

public long register(String mobile) {
MemberExample memberExample = new MemberExample();
memberExample.createCriteria().andMobileEqualTo(mobile);
List<Member> list = memberMapper.selectByExample(memberExample);

if (CollUtil.isNotEmpty(list)) {
//return list.get(0).getId();
throw new RuntimeException("手机号已注册");
}

Member member = new Member();
member.setId(System.currentTimeMillis());
member.setMobile(mobile);

memberMapper.insert(member);
return member.getId();

}

MemberController.java新增

    @PostMapping("/register")
    public long register(String mobile) {
        return memberService.register(mobile);
    }

测试

POST http://localhost:8000/member/member/register
Content-Type: application/x-www-form-urlencoded

mobile = 18888888888

成功!

 

 

 封装请求参数和返回结果

前后端分离项目,需要对后端接口做统一封装,这样便于前端做统—处理。


 

普通封装方法

在模块member中创建新的包req用于封装。

创建MemberRegisterReq.java类

 1 package com.zihans.train.member.req;
 2 
 3 public class MemberRegisterReq {
 4     private String mobile;
 5 
 6     public String getMobile() {
 7         return mobile;
 8     }
 9 
10     public void setMobile(String mobile) {
11         this.mobile = mobile;
12     }
13 
14     @Override
15     public String toString() {
16         return "MemberRegisterReq{" +
17                 "mobile='" + mobile + '\'' +
18                 '}';
19     }
20 }
MemberRegisterReq.java

修改service和controller

 1 public long register(MemberRegisterReq req) {
 2         String mobile = req.getMobile();
 3         MemberExample memberExample = new MemberExample();
 4         memberExample.createCriteria().andMobileEqualTo(mobile);
 5         List<Member> list = memberMapper.selectByExample(memberExample);
 6 
 7         if (CollUtil.isNotEmpty(list)) {
 8             //return list.get(0).getId();
 9             throw new RuntimeException("手机号已注册");
10         }
11 
12         Member member = new Member();
13         member.setId(System.currentTimeMillis());
14         member.setMobile(mobile);
15 
16         memberMapper.insert(member);
17         return member.getId();
18 
19     }
MemberService.java
1     @PostMapping("/register")
2     public long register(MemberRegisterReq req) {
3         return memberService.register(req);
4     }
MemberController.java

 

为了方便前端统一处理,后端接口要统一包装

在common模块新建包resp,新增CommonResp.java类

 1 package com.zihans.train.common.resp;
 2 
 3 public class CommonResp<T> {
 4 
 5     /**
 6      * 业务上的成功或失败
 7      */
 8     private boolean success = true;
 9 
10     /**
11      * 返回信息
12      */
13     private String message;
14 
15     /**
16      * 返回泛型数据,自定义类型
17      */
18     private T content;
19 
20     public CommonResp() {
21     }
22 
23     public CommonResp(T content) {
24         this.content = content;
25     }
26 
27     public boolean getSuccess() {
28         return success;
29     }
30 
31     public void setSuccess(boolean success) {
32         this.success = success;
33     }
34 
35     public String getMessage() {
36         return message;
37     }
38 
39     public void setMessage(String message) {
40         this.message = message;
41     }
42 
43     public T getContent() {
44         return content;
45     }
46 
47     public void setContent(T content) {
48         this.content = content;
49     }
50 
51     @Override
52     public String toString() {
53         final StringBuffer sb = new StringBuffer("CommonResp{");
54         sb.append("success=").append(success);
55         sb.append(", message='").append(message).append('\'');
56         sb.append(", content=").append(content);
57         sb.append('}');
58         return sb.toString();
59     }
60 }
CommonResp.java

修改MemberController.java

 1 package com.zihans.train.member.controller;
 2 
 3 
 4 import com.zihans.train.common.resp.CommonResp;
 5 import com.zihans.train.member.req.MemberRegisterReq;
 6 import com.zihans.train.member.service.MemberService;
 7 import jakarta.annotation.Resource;
 8 import org.springframework.web.bind.annotation.GetMapping;
 9 import org.springframework.web.bind.annotation.PostMapping;
10 import org.springframework.web.bind.annotation.RequestMapping;
11 import org.springframework.web.bind.annotation.RestController;
12 
13 @RestController
14 @RequestMapping("/member")
15 public class MemberController {
16 
17     @Resource
18     private MemberService memberService;
19 
20     @GetMapping("/count")
21     public CommonResp<Integer> count() {
22         int count = memberService.count();
23 //        CommonResp<Integer> commonResp = new CommonResp<>();
24 //        commonResp.setContent(count);
25 //        return commonResp;
26         return new CommonResp<>(count);
27     }
28 
29     @PostMapping("/register")
30     public CommonResp<Long> register(MemberRegisterReq req) {
31         long register = memberService.register(req);
32 //        CommonResp<Long> commonResp = new CommonResp<>();
33 //        commonResp.setContent(register);
34 //        return commonResp;
35         return new CommonResp<>(register);
36     }
37 }
MemberController.java

测试成功!

 

 

 为项目增加统一异常处理

common模块中新增controller包,用于拦截接口,创建ControllerExceptionHandler.java

 1 package com.jiawa.train.common.controller;
 2 
 3 import com.jiawa.train.common.resp.CommonResp;
 4 import org.slf4j.Logger;
 5 import org.slf4j.LoggerFactory;
 6 import org.springframework.web.bind.annotation.ControllerAdvice;
 7 import org.springframework.web.bind.annotation.ExceptionHandler;
 8 import org.springframework.web.bind.annotation.ResponseBody;
 9 
10 /**
11  * 统一异常处理、数据预处理等
12  */
13 @ControllerAdvice
14 public class ControllerExceptionHandler {
15 
16     private static final Logger LOG = LoggerFactory.getLogger(ControllerExceptionHandler.class);
17 
18     /**
19      * 所有异常统一处理
20      * @param e
21      * @return
22      */
23     @ExceptionHandler(value = Exception.class)
24     @ResponseBody
25     public CommonResp exceptionHandler(Exception e) throws Exception {
26         CommonResp commonResp = new CommonResp();
27         LOG.error("系统异常:", e);
28         commonResp.setSuccess(false);
29         // commonResp.setMessage("系统出现异常,请联系管理员");
30         commonResp.setMessage(e.getMessage());
31         return commonResp;
32     }
33 
34 }
ControllerExceptionHandler.java

使用自定义异常来解决业务异常

common模块中新增exception包,用于自定义异常。新增枚举类BusinessExceptionEnum类用于枚举异常

 1 package com.zihans.train.common.exception;
 2 
 3 public enum BusinessExceptionEnum {
 4 
 5     MEMBER_MOBILE_EXIST("手机号已注册");
 6 
 7     private String desc;
 8 
 9     BusinessExceptionEnum(String desc) {
10         this.desc = desc;
11     }
12 
13     public String getDesc() {
14         return desc;
15     }
16 
17     public void setDesc(String desc) {
18         this.desc = desc;
19     }
20 
21     @Override
22     public String toString() {
23         return "BusinessExceptionEnum{" +
24                 "desc='" + desc + '\'' +
25                 "} " + super.toString();
26     }
27 }
BusinessExceptionEnum.java

新建一个BusinessException类

 1 package com.jiawa.train.common.exception;
 2 
 3 public class BusinessException extends RuntimeException {
 4 
 5     private BusinessExceptionEnum e;
 6 
 7     public BusinessException(BusinessExceptionEnum e) {
 8         this.e = e;
 9     }
10 
11     public BusinessExceptionEnum getE() {
12         return e;
13     }
14 
15     public void setE(BusinessExceptionEnum e) {
16         this.e = e;
17     }
18 
19 }
BusinessException.java

MemberService.java调整以下字段

         if (CollUtil.isNotEmpty(list)) {
             // return list.get(0).getId();
-            throw new RuntimeException("手机号已注册");
+            throw new BusinessException(BusinessExceptionEnum.MEMBER_MOBILE_EXIST);
         }

ControllerExceptionHandler.java的未知异常全部返回“系统出现异常,请联系管理员”(正式开发出现这句话说明系统有bug)

 1 /**
 2      * 全部异常统一处理
 3      * @param e
 4      * @return
 5      */
 6     @ExceptionHandler(value = Exception.class)
 7     @ResponseBody
 8     public CommonResp exceptionHandler(Exception e) {
 9         CommonResp commonResp = new CommonResp();
10         LOG.error("系统异常:", e);
11         commonResp.setSuccess(false);
12         commonResp.setMessage("系统出现异常,请联系管理员");
13         //commonResp.setMessage(e.getMessage());
14         return commonResp;
15     }
16 
17     /**
18      * 业务异常统一处理
19      * @param e
20      * @return
21      */
22     @ExceptionHandler(value = BusinessException.class)
23     @ResponseBody
24     public CommonResp exceptionHandler(BusinessException e) {
25         CommonResp commonResp = new CommonResp();
26         LOG.error("业务异常:", e);
27         commonResp.setSuccess(false);
28         //commonResp.setMessage("系统出现异常,请联系管理员");
29         commonResp.setMessage(e.getE().getDesc());
30         return commonResp;
31     }
ControllerExceptionHandler.java

BusinessException.java新增以下语句,业务一场不写入堆栈信息,提高性能。

    @Override
    public Throwable fillInStackTrace() {
        return this;
    }

集成校验框架Validation

Spring Boot Validation是Spring Boot整合了Hibernate Validation的一个框架,其核心是HibernateValidation,
此框架的作用:检验客户端向服务器端提交的请求参数的基本格式是否合法
常用的检查注解有:

  • @NotNull:不允许为null值可用于任何类型的参数
  • @NotEmpty:不允许为空字符串,即长度为0的字符串仅用于检查字符串类型的参数
  • @NotBlank:不允许为空白的字符串,即仅由空格或TAB制表位或换行组成的值,仅用于检查字符串类型的参数
  • @Length:限制字符串的长度
  • @Pattern:通过正则表达式检查字符串的格式,此注解的regexp属性就是定义正则表达式的属性,仅用于检查字符串类型的参数
  • @Min:限制整型数值的最小值,仅用于检查整型数值参数
  • @Max:限制整型数值的最大值,仅用于检查整型数值参数
  • @Range:限制整型数值的取值区间,默认最小值为0,最大值为long的上限值,仅用于检查整型数值参数

所有检查注解都有message属性,用于配置检查失败时的提示文本。
每个被检查参数可以同时添加多个检查注解!


 

common模块添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

在MemberRegisterReq.java添加注解

    @NotBlank(message = "【手机号】不能为空")
    private String mobile;

同时MemberController.java增加@Valid注解

-    public CommonResp<Long> register(MemberRegisterReq req) {
+    public CommonResp<Long> register(@Valid MemberRegisterReq req)

ControllerExceptionHandler.java新增校验异常处理

 1 /**
 2      * 校验异常处理
 3      * @param e
 4      * @return
 5      */
 6     @ExceptionHandler(value = BindException.class)
 7     @ResponseBody
 8     public CommonResp exceptionHandler(BindException e) {
 9         CommonResp commonResp = new CommonResp();
10         LOG.error("校验异常:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
11         commonResp.setSuccess(false);
12         //commonResp.setMessage("系统出现异常,请联系管理员");
13         commonResp.setMessage(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
14         return commonResp;
15     }
ControllerExceptionHandler.java

 雪花算法生成id

 精确到毫秒不能满足高并发下多请求状况。

自增ID不适合分布式数据库、分表分库场景,适合小型项目
UUID会影响索引效率,因为UUID是无序的,用一堆无序的ID来构建一个有序的索引目录,性能上肯定有问题的
雪花算法由Twitter公司在2014年开源scala语言版本

 雪花算法在同一毫秒时间戳下,可以生成2^12=4096个不重复id

 

雪花算法问题

  • 数据中心,机器ID怎么设置

          1.利用redis自增序列

          2.利用数据库,为每台机器分配workId,保存ip和workId的关系

  • 时钟回拨

          —个应用─般是多节点,可停止节点,等时间过了,再启动


创建通用SnowUtil.java类,用来封装hutool雪花算法

 1 package com.zihans.train.common.util;
 2 
 3 import cn.hutool.core.util.IdUtil;
 4 
 5 /**
 6  * 封装hutool雪花算法
 7  */
 8 public class SnowUtil {
 9 
10     private static long dataCenterId = 1;  //数据中心
11     private static long workerId = 1;     //机器标识
12 
13     public static long getSnowflakeNextId() {
14         return IdUtil.getSnowflake(workerId, dataCenterId).nextId();
15     }
16 
17     public static String getSnowflakeNextIdStr() {
18         return IdUtil.getSnowflake(workerId, dataCenterId).nextIdStr();
19     }
20 }
SnowUtil.java

MemberService设置id

member.setId(SnowUtil.getSnowflakeNextId());

  

 

标签:java,CommonResp,Spring,train,Alibaba,member,Springbooot3.0,return,public
From: https://www.cnblogs.com/szhNJUPT/p/17306113.html

相关文章

  • spring boot创建非web项目
    我们如何启动一个main方法去运行它呢使用也非常简单,我们只需要对springboot生成的代码做略微的修改即可。使用SpringApplicationBuilder来创建SpringApplication,并且配置WebApplicationType为NONE,这样即使有tomcat依赖也不会创建httpserver,执行run方法之后我们就得到了spring......
  • Spring04_Aop
    一、AOP概述(一)AOP简介​ 面向切面编程是一种通过横切关注点(Cross-cuttingConcerns)分离来增强代码模块性的方法,它能够在不修改业务主体代码的情况下,对它添加额外的行为。(二)为何需要AOP​ 面向对象编程OOP可以通过对业务的分析,然后抽象出一系列具有一定属性与行为的类,并通......
  • SpringBoot中实现自定义start
    本文主要通过模拟实现redis的功能来自定义start,具体实现口可以往下看1、新建SpringBoot项目,引入依赖<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId>&l......
  • SpringCloud
    1.nacos除了配置中心还能干什么除了作为配置中心之外,nacos还可以实现服务注册和发现功能。服务提供者可以在启动时将自己注册到nacos中,并声明自己提供的服务名、ip地址和端口等信息。而服务消费者则可以通过nacos查询到相应的服务提供者,并直接调用其提供的服务。此外,nacos还具备......
  • SpringBoot 集成 MybatisPlus 十——数据自动填充
    1自动填充功能介绍自动填充功能可以在插入或修改时为对象属性自动赋值。之前学习了逻辑删除字段,在向数据库插入数据时,都需要设置isDeleted=0,这在进行频繁地数据插入时就显得有些繁琐,于是MybatisPlus就为我们提供了自动填充的功能。修改实体类,为需要自动填充的字段在注解@Table......
  • Spring Boot Configuration Annotation Processor not configured(最简单的解决办法)
    在使用@ConfigurationProperties是报红:SpringBootConfigurationAnnotationProcessornotconfigured,如下图所示:其实这个不影响程序运行,但作为程序员就是看着不舒服,网上也有解决办法,其中最多的就是说在pom.xml中加入以下依赖:<dependency><groupId>org.springframework......
  • SpringMVC中的字符编码问题
    字符编码问题目录字符编码问题一、背景二、排查思路2.1、查看idea默认编码方式2.2、查看接口代码2.3、查看linux编码三、解决思路3.1、修改远程调用编码四、SpringMVC对字符编码的配置4.1、字符编码自动配置类HttpEncodingAutoConfiguration4.2、配置类中属性说明4.3、过滤器中设......
  • java——微服务——spring cloud——前言导读
                       黑马课程连接:https://www.bilibili.com/video/BV1LQ4y127n4?p=1&vd_source=79bbd5b76bfd74c2ef1501653cee29d6 ......
  • SpringBoot文件上传大小限制修改
    springboot默认文件上传大小限制为10M如需修改可以参考下面的配置文件spring.servlet.multipart.max-file-size=20MBspring.servlet.multipart.max-request-size=20MB配置说明:Thisconfigurationsetsthemaximumallowedsizeforbothindividualfilesandtheentirere......
  • springboot项目打成jar包后 ,配置文件加载的优先级顺序
    SpringBoot会按照以下顺序来加载配置文件:1、内置默认值:SpringBoot会首先加载内置的默认值,这些默认值定义在SpringBoot的代码中,例如,内置的默认端口号为8080。2、应用级别的配置文件:SpringBoot会从以下位置加载应用级别的配置文件,这些位置按照优先级逐一检查:当前目录下的/c......