篇幅有限 完整内容及源码关注公众号:ReverseCode,发送 冲
SpringCloudAlibaba Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。 Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。 Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。 Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。 Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。 Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。 Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。 虚拟机 安装virtualbox 6
vagrant 安装好后进入镜像仓库 搜索需要安装的镜像
mkdir centos7 && vagrant init centos/7 && vagrant up && vagrant ssh
创建并开启镜像,修改网络桥接
如果太慢,进入centos/7 仓库下载,手动加载vagrant box add centos/7 CentOS-7-x86_64-Vagrant-2004_01.VirtualBox.box
vim Vagrantfile 修改配置并重启vagrant reload
,保存虚拟机与主机可ping通,sudo passwd root
设置root权限
1 2 # config.vm.network "private_network", ip: "192.168.56.10" host-only config.vm.network "public_network" 桥接
安装nodejs 并配置淘宝源npm config set registry http://registry.npm.taobao.org/
安装docker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine sudo yum install -y yum-utils sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io yum list docker-ce --showduplicates | sort -r sudo systemctl start docker sudo docker images sudo systemctl enable docker
镜像加速器
1 2 3 4 5 6 7 8 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://3gki6pei.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker
mysql
1 2 3 4 5 6 7 8 9 10 docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysal \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 docker exec -it mysql /bin/bash mysql -uroot -proot use mysql; alter user 'root'@'%' identified with mysql_native_password by 'root'; flush privileges;
vi /mydata/mysql/conf/my.cnf 修改编码配置
1 2 3 4 5 6 7 8 9 10 11 [client] default-character-set=utf8 [mysql] default-character-set=utf8 [mysqld] init_connect='SET collation_connection = utf8_unicode_ci' init_connect='SET NAMES utf8' character-set-server=utf8 collation-server=utf8_unicode_ci skip-character-set-client-handshake skip-name-resolve
docker restart mysql && cat /etc/mysql/my.cnf && docker update mysql –restart=always
redis
vi /mydata/redis/conf/redis.conf 配置appendonly yes
持久化硬盘
1 2 3 4 5 docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \ -v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \ -d redis:3.2 redis-server /etc/redis/redis.conf --appendonly yes docker exec -it redis redis-cli docker update redis --restart=always
maven
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror> <profile> <id>jdk18</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile>
idea安装lombok,mybatisX插件
vscode安装Auto Close Tag,Auto Rename Tag,Chinese,Eslint,HTML CSS Support,HTML Snippets,JavaScript(ES6),Live Server,open in browser,Vetur,Vue 2 Snippets插件
git配置
1 2 3 4 5 git config --global user.name "username" //(名字) # 配置邮箱 git config --global user.email "username@email.com" //(注册账号时用的邮箱) ssh-keygen -t rsa -C "xxxxx@xxxxx.com" 进入git bash生成公私钥 cat ~/.ssh/id_rsa.pub ssh -T git@gitee.com 测试是否成功即可
Nacos注册中心 新建Spring Initializr作为provider项目jdk1.8并导入Web中的Spring Web和Spring Cloud Routing的OpenFeign组件,设置-Xmx100m 启动Application使用最大内存
启动nacos-server-1.3.1.zip 访问nacos
修改依赖如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR3</spring-cloud.version> <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
添加application.yml配置
1 2 3 4 5 6 7 8 9 spring: application: name: provider cloud: nacos: discovery: server-addr: 127.0.0.1:8848 server: port: 8000
ProviderApplication启动类添加@EnableDiscoveryClient
,启动后在nacos控制台 查看已经上线的服务
Nacos配置中心 默认配置读取 application.properties添加配置
1 2 user.name=onejane user.age=20
Controller实现读取
1 2 3 4 5 6 7 8 9 10 11 12 @RestController @RequestMapping("test") public class UserController { @Value("${user.name}") private String name; @Value("${user.age}") private Integer age; @RequestMapping("/user") public void test(){ System.out.println(name+":"+age); } }
Nacos配置 项目添加config依赖
1 2 3 4 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
Provider项目创建bootstrap.properties
1 2 spring.application.name=provider spring.cloud.nacos.config.server-addr=127.0.0.1:8848
Nacos中配置provider.properties,默认规则,取public命名空间的应用名.properties,类型为Properties,相同与application.properties时优先使用配置中心的配置。
1 2 user.name=onejane1 user.age=23
多环境配置 配置命名空间provider,命名空间ID为f7d49148-09a8-4ca1-8a40-c52e9cb627b5,在该命名空间下配置provider.properties,Group为prod
1 2 user.name=onejane2 user.age=23
配置bootstrap.properties
1 2 3 4 spring.application.name=provider spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.namespace=f7d49148-09a8-4ca1-8a40-c52e9cb627b5 spring.cloud.nacos.config.group=prod
命名空间,用于配置隔离,默认新增的所有配置都在public空间。
1、开发,测试,生产:利用命名空间来做环境隔离。在bootstrap.properties;配置spring.cloud.nacos.config.namespace需要使用哪个命名空间下的配置
2、每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
每个微服务创建自己的命名空间,使用配置分组区分环境,dev,test,prod
多文件配置 在Nacos中provider微服务命名空间配置datasource.yml
1 2 3 4 5 6 spring: datasource: username: root password: root url: jdbc:mysql://172.20.1.185:3306/user driver-class-name: com.mysql.jdbc.Driver
配置mybatis.yml
1 2 3 4 5 mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml global-config: db-config: id-type: auto
配置service.yml
1 2 3 4 5 6 7 8 9 spring: application: name: provider cloud: nacos: discovery: server-addr: 127.0.0.1:8848 server: port: 7000
注释application.yml中所有配置并在bootstrap.properties中实现多文件配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 spring.application.name=provider spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.namespace=f7d49148-09a8-4ca1-8a40-c52e9cb627b5 spring.cloud.nacos.config.group=prod spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml spring.cloud.nacos.config.ext-config[0].group=dev spring.cloud.nacos.config.ext-config[0].refresh=true spring.cloud.nacos.config.ext-config[1].data-id=mybatis.yml spring.cloud.nacos.config.ext-config[1].group=dev spring.cloud.nacos.config.ext-config[1].refresh=true spring.cloud.nacos.config.ext-config[2].data-id=other.yml spring.cloud.nacos.config.ext-config[2].group=dev spring.cloud.nacos.config.ext-config[2].refresh=true
Mybatis-Plus持久化 引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.17</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> </dependency>
配置数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 spring: datasource: username: root password: root url: jdbc:mysql://172.20.1.185:3306/user driver-class-name: com.mysql.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss # 输出日期自动转换 mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml global-config: db-config: id-type: auto logic-delete-value: 1 # 逻辑删除 logic-not-delete-value: 0
配置扫描包
1 @MapperScan("com.onejane.demo.provider.dao")
添加实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Data @TableName("user_info") public class UserInfoEntity implements Serializable { private static final long serialVersionUID = 1L; @TableId private Long id; private String name; private Long age; /** * 是否显示[0-不显示,1显示] 用于逻辑删除 */ @TableLogic(value = "1",delval = "0") private Integer status; }
逻辑删除实现
1 2 3 4 5 6 7 @Autowired UserInfoSerivce userInfoSerivce; @RequestMapping("/delete") public R delete(Long[] ids){ userInfoSerivce.removeByIds(Arrays.asList(ids)); return R.ok(); }
分页查询实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public interface UserInfoSerivce extends IService<UserInfoEntity> { PageUtils queryPage(Map<String, Object> params); } @Service("userInfoService") public class UserInfoServiceImpl extends ServiceImpl<UserInfoDao, UserInfoEntity> implements UserInfoSerivce { @Override public PageUtils queryPage(Map<String, Object> params) { IPage<UserInfoEntity> page = this.page( new Query<UserInfoEntity>().getPage(params), new QueryWrapper<UserInfoEntity>() ); return new PageUtils(page); } } @RequestMapping("/list") public R list(@RequestParam Map<String, Object> params){ PageUtils page = userInfoSerivce.queryPage(params); return R.ok().put("page", page); }
分页配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Configuration @EnableTransactionManagement //开启事务 @MapperScan("com.onejane.demo.provider.dao") public class MyBatisConfig { //引入分页插件 @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false paginationInterceptor.setOverflow(true); // 设置最大单页限制数量,默认 500 条,-1 不受限制 paginationInterceptor.setLimit(1000); return paginationInterceptor; } }
Feign远程调用 新建Spring Initializr作为consumer项目jdk1.8并导入Web中的Spring Web和Spring Cloud Routing的OpenFeign组件,设置-Xmx100m 启动Application使用最大内存
provider提供api,启动类配置@EnableDiscoveryClient后启动应用
consumer中引入nacos依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR3</spring-cloud.version> <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version> </properties> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Member作为consumer获取feign调用,定义feign方法
1 2 3 4 5 6 7 @FeignClient("provider") public interface ProviderFeignService { @RequestMapping("/test/list") public void test(); }
consumer在控制器中申请远程调用,并在启动类中开启feign调用@EnableFeignClients(basePackages = "com.onejane.demo.consumer.feign")
并将consumer加入nacos注册中心@EnableDiscoveryClient
1 2 3 4 5 @FeignClient("provider") public interface ProviderFeignService { @RequestMapping("/test/user") public void test(); }
在application.yml中配置nacos
1 2 3 4 5 6 7 8 9 spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848 application: name: consumer server: port: 7000
开启feign远程调用
1 2 3 4 5 6 7 8 9 10 @RestController @RequestMapping("test") public class UserInfoController { @Autowired ProviderFeignService providerFeignService; @RequestMapping("/user") public void test(){ providerFeignService.test(); } }
Gateway网关 修改provider项目新增server.servlet.context-path: /provider
新建Spring Initializr作为网关项目项目jdk1.8并导入Spring Cloud Routing的Gateway组件
pom依赖修改
1 2 3 4 5 6 7 8 9 10 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR3</spring-cloud.version> </properties>
application.properties添加配置
1 2 3 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 spring.application.name=gateway server.port=88
在nacos中配置微服务命名空间gateway,ID为7601cb5a-a9dd-43d8-a1b9-a37952674df2,添加配置文件gateway.yml
1 2 3 spring: application: name: gateway
bootstrap.properties配置命名空间
1 2 3 spring.application.name=gateway spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.namespace=7601cb5a-a9dd-43d8-a1b9-a37952674df2
application.yml中添加网关配置,访问http://localhost:88/?url=qq 则跳转到https://www.qq.com?url=qq, 访问http://localhost:88/?url=baidu 则跳转到https://www.baidu.com?url=baidu
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 spring: cloud: gateway: routes: - id: test_route uri: https://www.baidu.com predicates: - Query=url,baidu - id: qq_route uri: https://www.qq.com predicates: - Query=url,qq - id: provider_route uri: lb://provider predicates: - Path=/api/provider/** filters: - RewritePath=/api/(?<segment>.*),/$\{segment} application: name: gateway # http://localhost:88/api/provider/test/user ==> http://localhost:8000/provider/test/user
在启动类中配置@EnableDiscoveryClient,并过滤掉数据库配置@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
跨域 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Configuration public class GulimallCorsConfiguration { @Bean public CorsWebFilter corsWebFilter(){ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); //1、配置跨域 corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.setAllowCredentials(true); source.registerCorsConfiguration("/**",corsConfiguration); return new CorsWebFilter(source); } }
阿里云oss 引入阿里云oss依赖
1 2 3 4 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alicloud-oss</artifactId> </dependency>
application.yml配置阿里云AccessKey
1 2 3 4 5 6 7 8 spring: cloud: alicloud: access-key: *** secret-key: *** oss: endpoint: oss-cn-beijing.aliyuncs.com bucket: onejane-opencv
上传图片一般先通过服务端签名后前端直传
JSR303 通过自定义校验,分组校验及全局校验实现对Spring MVC入参的参数校验。
加入validation依赖
1 2 3 4 5 <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency>
加入校验注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 /** * 品牌id */ @NotNull(message = "修改必须指定品牌id",groups = {UpdateGroup.class}) @Null(message = "新增不能指定id",groups = {AddGroup.class}) @TableId private Long brandId; /** * 品牌名 */ @NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class}) private String name; /** * 品牌logo地址 */ @NotBlank(groups = {AddGroup.class}) @URL(message = "logo必须是一个合法的url地址",groups={AddGroup.class,UpdateGroup.class}) private String logo; /** * 介绍 */ private String descript; /** * 显示状态[0-不显示;1-显示] */ @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class}) @ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class}) private Integer showStatus; /** * 检索首字母 */ @NotEmpty(groups={AddGroup.class}) @Pattern(regexp="^[a-zA-Z]$",message = "检索首字母必须是一个字母",groups={AddGroup.class, UpdateGroup.class}) private String firstLetter; /** * 排序 */ @NotNull(groups={AddGroup.class}) @Min(value = 0,message = "排序必须大于等于0",groups={AddGroup.class,UpdateGroup.class}) private Integer sort;
建立分组校验类
1 2 3 public interface AddGroup {} public interface UpdateGroup {} public interface UpdateStatusGroup {}
自定义异常注解
1 2 3 4 5 6 7 8 9 10 11 12 13 @Documented @Constraint(validatedBy = { ListValueConstraintValidator.class }) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) public @interface ListValue { String message() default "{com.onejane.demo.provider.valid.ListValue.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; int[] vals() default { }; }
自定义异常实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> { private Set<Integer> set = new HashSet<>(); //初始化方法 @Override public void initialize(ListValue constraintAnnotation) { int[] vals = constraintAnnotation.vals(); for (int val : vals) { set.add(val); } } //判断是否校验成功 /** * * @param value 需要校验的值 * @param context * @return */ @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { return set.contains(value); } }
控制器层加入字段校验配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @RequestMapping("/save") public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand/*,BindingResult result*/){ // if(result.hasErrors()){ // Map<String,String> map = new HashMap<>(); // //1、获取校验的错误结果 // result.getFieldErrors().forEach((item)->{ // //FieldError 获取到错误提示 // String message = item.getDefaultMessage(); // //获取错误的属性的名字 // String field = item.getField(); // map.put(field,message); // }); // // return R.error(400,"提交的数据不合法").put("data",map); // }else { // 为避免每次请求都判断异常输出,通过ExceptionControllerAdvice实现全局异常控制 brandService.save(brand); return R.ok(); } /** * 修改 */ @RequestMapping("/update") public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand){ brandService.updateById(brand); return R.ok(); } /** * 修改状态 */ @RequestMapping("/update/status") public R updateStatus(@Validated(UpdateStatusGroup.class) @RequestBody BrandEntity brand){ brandService.updateById(brand); return R.ok(); }
全局异常控制配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Slf4j //@ResponseBody //@ControllerAdvice(basePackages = "com.onejane.demo.provider.controller") @RestControllerAdvice(basePackages = "com.onejane.demo.provider.controller") public class ExceptionControllerAdvice { @ExceptionHandler(value= MethodArgumentNotValidException.class) public R handleVaildException(MethodArgumentNotValidException e){ log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass()); BindingResult bindingResult = e.getBindingResult(); Map<String,String> errorMap = new HashMap<>(); bindingResult.getFieldErrors().forEach((fieldError)->{ errorMap.put(fieldError.getField(),fieldError.getDefaultMessage()); }); return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap); } @ExceptionHandler(value = Throwable.class) public R handleException(Throwable throwable){ log.error("错误:",throwable); return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg()); } }