项目分为三篇:
谷粒学苑项目前置知识
谷粒学苑项目前台界面
谷粒学苑后台管理系统
资料链接:谷粒学苑 提取码:p6er
视频教程:
尚硅谷-谷粒学苑前端代码:前端代码
后端代码:后端代码
项目模块介绍:
项目技术点:
后端: SpringBoot,SpringCloud,MyBatisPlus,Spring SECURITY,redis,maven,easyExcel,jwt,OAuth2
前端: Vue,element-ui,axios,node.js
其他技术: 阿里云oss,阿里云视频点播服务,阿里云短信服务,微信支付和登录,docker,git,Jenkins
有关 MyBatis-Plus 的学习笔记:https://blog.csdn.net/aetawt/article/details/126105008
学习视频: BV1VP4y1c7j7
前后端分离概念:
准备数据库表:
完整项目工程结构总览 :
创建父工程:
父工程删除 src 文件夹,只留下 pom 文件。
父工程依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.1.RELEASEversion>
<relativePath/>
parent>
<groupId>com.atguigugroupId>
<artifactId>guli_parentartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>guli_parentname>
<packaging>pompackaging>
<properties>
<java.version>1.8java.version>
<guli.version>0.0.1-SNAPSHOTguli.version>
<velocity.version>2.0velocity.version>
<swagger.version>2.7.0swagger.version>
<aliyun.oss.version>2.8.3aliyun.oss.version>
<jodatime.version>2.10.1jodatime.version>
<poi.version>3.17poi.version>
<commons-fileupload.version>1.3.1commons-fileupload.version>
<commons-io.version>2.6commons-io.version>
<httpclient.version>4.5.1httpclient.version>
<jwt.version>0.7.0jwt.version>
<aliyun-java-sdk-core.version>4.3.3aliyun-java-sdk-core.version>
<aliyun-sdk-oss.version>3.1.0aliyun-sdk-oss.version>
<aliyun-java-sdk-vod.version>2.15.2aliyun-java-sdk-vod.version>
<aliyun-java-vod-upload.version>1.4.11aliyun-java-vod-upload.version>
<aliyun-sdk-vod-upload.version>1.4.11aliyun-sdk-vod-upload.version>
<fastjson.version>1.2.28fastjson.version>
<gson.version>2.8.2gson.version>
<json.version>20170516json.version>
<commons-dbutils.version>1.7commons-dbutils.version>
<canal.client.version>1.1.0canal.client.version>
<docker.image.prefix>zxdocker.image.prefix>
<cloud-alibaba.version>0.2.2.RELEASEcloud-alibaba.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.2.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>${swagger.version}version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>${swagger.version}version>
dependency>
<dependency>
<groupId>com.aliyun.ossgroupId>
<artifactId>aliyun-sdk-ossartifactId>
<version>${aliyun.oss.version}version>
dependency>
<dependency>
<groupId>joda-timegroupId>
<artifactId>joda-timeartifactId>
<version>${jodatime.version}version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
<version>${poi.version}version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>${poi.version}version>
dependency>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>${commons-fileupload.version}version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>${commons-io.version}version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>${httpclient.version}version>
dependency>
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
<version>${gson.version}version>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>${jwt.version}version>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-coreartifactId>
<version>${aliyun-java-sdk-core.version}version>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-vodartifactId>
<version>${aliyun-java-sdk-vod.version}version>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-vod-uploadartifactId>
<version>${aliyun-java-vod-upload.version}version>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-sdk-vod-uploadartifactId>
<version>${aliyun-sdk-vod-upload.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>${fastjson.version}version>
dependency>
<dependency>
<groupId>org.jsongroupId>
<artifactId>jsonartifactId>
<version>${json.version}version>
dependency>
<dependency>
<groupId>commons-dbutilsgroupId>
<artifactId>commons-dbutilsartifactId>
<version>${commons-dbutils.version}version>
dependency>
<dependency>
<groupId>com.alibaba.ottergroupId>
<artifactId>canal.clientartifactId>
<version>${canal.client.version}version>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
关于 aliyun-java-vod-upload 无法依赖爆红的原因:
由于 aliyun-java-vod-upload没有开源,所以不能直接在pom文件中直接引入依赖。
因此需要在官网下载 jar 包 :
下载链接
下载解压之后进入 lib 目录,进入 cmd 命令行
安装 jar 包:
mvn install:install-file -DgroupId=com.aliyun -DartifactId=aliyun-java-vod-upload -Dversion=1.4.14 -Dpackaging=jar -Dfile=aliyun-java-vod-upload-1.4.14.jar
第二种方法:
解压我的 maven 仓库,将 jar 包拷贝到你的仓库
链接:https://pan.baidu.com/s/1_RVsyZI8PlCI_paf0XOOVg
提取码:v4wf
创建 service 子模块 :
- 删除 src,只留下 pom 文件
- 由于还没有用到微服务,先将依赖注释掉,否则会报错
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>guli_parentartifactId>
<groupId>com.atguigugroupId>
<version>0.0.1-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>serviceartifactId>
<packaging>pompackaging>
<modules>
<module>service_edumodule>
modules>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
dependency>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
dependency>
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
project>
创建 service_edu 子子模块
依旧遵循五步走:
- 建 model
- 改 pom
- pom 使用 service 的 pom,无需在修改
- 写 yaml/properties
- 启动类
- 业务类
建 model:
写 properties
# 服务端口
server.port=8001
# 服务名
spring.application.name=service-edu
# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=1234
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
我使用的是 MyBatisplusX 插件自动生成的代码,并没有使用老师提供的代码生成器。
启动类:
@SpringBootApplication
@MapperScan("com.atguigu.demo.mapper")
public class EduTeacherApplication {
public static void main(String[] args) {
SpringApplication.run(EduTeacherApplication.class,args);
}
}
业务类:
@RestController
@RequestMapping("eduservice/teacher")
public class EduTeacherController {
@Autowired
private EduTeacherService eduTeacherService;
@GetMapping("/findAll")
public List<EduTeacher> findAll(){
return eduTeacherService.list();
}
}
测试:
修改返回数据的时间格式:
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
到此步基本的模块结构
Swagger 是什么?
前后端分离开发模式中,api 文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于
生成、描述、调用和可视化 RESTful 风格的 Web 服务
。特点:
- 及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
- 规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
- 一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
- 可测性 (直接在接口文档上进行测试,以方便理解业务)
在 guli_parent 下创建 common 子模块
,在 common 模块下创建 service_base 子子模块
,方便其他服务都能使用 Swagger
common 子模块pom 文件:
删除 src 文件夹,只留下 pom 文件
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
dependencies>
创建 service_base 模块:
- 注意父模块和路径关系
- 不用修改 pom ,使用 父模块 common 的依赖即可
创建配置类 SwaggerConfig 配置 Swagger:
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket webApiConfig(){ return new Docket(DocumentationType.SWAGGER_2) .groupName("webApi") .apiInfo(webApiInfo()) .select() .paths(Predicates.not(PathSelectors.regex("/admin/.*"))) .paths(Predicates.not(PathSelectors.regex("/error.*"))) .build(); } private ApiInfo webApiInfo(){ return new ApiInfoBuilder() .title("网站-课程中心API文档") .description("本文档描述了课程中心微服务接口定义") .version("1.0") .contact(new Contact("Helen", "http://atguigu.com", "[email protected]")) .build(); } }
在 service 模块中引入 service_base 依赖,使 service 模块下的所有模块都能够使用 Swagger:
<dependency> <groupId>com.atguigugroupId> <artifactId>service_baseartifactId> <version>0.0.1-SNAPSHOTversion> dependency>
配置启动类的包扫描:
由于我们在 service_base 模块中创建了 Swagger 配置类,需要在 service_edu 的中扫描 这个配置类
为什么不直接配置 com.atguigu.servicebase 包名呢?
这是因为 主启动类在 com.atguigu.demo 包下,ComponentScan 会覆盖 SpringBootApplication 的包扫描,因此会冲突。只好扫描他们的公共包了~~
访问:
http://localhost:8001/swagger-ui.html#
定义接口和参数说明:
- 定义在类上:@Api
- 定义在方法上:@ApiOperation
- 定义在参数上:@ApiParam
总结:
项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含
状态码、返回消息、数据
这几部分内容统一定义返回结果:
{ "success": 布尔, //响应是否成功 "code": 数字, //响应码 "message": 字符串, //返回消息 "data": HashMap //返回数据,放在键值对中 }
在 common 模块下创建子模块 common_utils ,并 定义返回状态码以及返回数据格式
返回状态码:
public interface ResultCode {
public static Integer SUCCESS = 20000; // 成功
public static Integer ERROR = 20001; // 失败
}
返回数据格式:
@Data
public class R {
@ApiModelProperty(value = "是否成功")
private Boolean success;
@ApiModelProperty(value = "返回码")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String message;
@ApiModelProperty(value = "返回数据")
private Map<String, Object> data = new HashMap<String, Object>();
// 把 构造方法私有化的作用: 除本类外不允许 new 本类
private R (){}
// 成功的返回方法
// 加上 static 可通过 类名. 的方式调用
public static R ok(){
R r = new R();
r.setCode(ResultCode.SUCCESS);
r.setMessage("成功");
r.setSuccess(true);
return r;
}
// 失败的返回方法
// 加上 static 可通过 类名. 的方式调用
public static R error(){
R r = new R();
r.setCode(ResultCode.ERROR);
r.setMessage("失败");
r.setSuccess(false);
return r;
}
public R success(Boolean success){
this.setSuccess(success);
return this;
}
public R message(String message){
this.setMessage(message);
return this;
}
public R code(Integer code){
this.setCode(code);
return this;
}
public R data(String key, Object value){
this.data.put(key, value);
return this;
}
public R data(Map<String, Object> map){
this.setData(map);
return this;
}
谁调用 this ,this 就代表谁~~~
private R (){}
已经将 类 私有化了,外部无法 new R() 这个对象,只能通过R.
的方式去调用方法。因此this
代表当前对象,也就是 R 对象
在 service_edu 模块中使用公共返回结果:
@Api("讲师管理")
@RestController
@RequestMapping("eduservice/teacher")
public class EduTeacherController {
@Autowired
private EduTeacherService eduTeacherService;
@ApiOperation("查询所有教师")
@GetMapping("/findAll")
public R findAll() {
List<EduTeacher> list = eduTeacherService.list();
return list.isEmpty() ? R.error() : R.ok().data("items", list);
}
// 逻辑删除
@ApiOperation("逻辑删除教师")
@DeleteMapping("{id}")
public R removeTeacher(
@ApiParam(name = "id", value = "讲师ID", required = true)
@PathVariable Integer id) {
boolean flag = eduTeacherService.removeById(id);
return flag ? R.ok() : R.error();
}
}
R.ok().data("items", list);
: 这种叫做 链式调用,返回对象都一样
**common_utils 目录结构 : **
结果测试:
在配置类里配置分页插件
@Configuration
public class MyConfig {
// 开启分页功能
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor (){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor() ;
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor ;
}
}
controller 层编写业务:
current : 当前页
size : 每一页显示内容条数
@ApiOperation("普通分页功能")
@GetMapping("/pageTeacher/{current}/{size}")
public R pageTeacher(
@PathVariable long current,
@PathVariable long size
){
// 创建分页对象
Page<EduTeacher> page = new Page<>(current, size);
eduTeacherService.page(page);
// 返回总页数 和 分页数据
return R.ok().data("rows",page.getRecords()).data("total",page.getTotal());
}
多条件组合查询带分页
比如前端的页面, 有四个查询条件,这四个查询条件任意组合进行查询。
一般的做法是将
前端的参数封装到一个vo对象中,然后将对象传到接口中
vo : 表示 View Object ,视图层对象,用于前端 --》 后端
DTO : 表示Data Transfer Object,数据传输对象,用于后端 – 》前端。
在 service_edu 模块中创建 vo 对象:
@Api("封装讲师查询条件对象")
@Data
public class TeacherQuery {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "教师名称,模糊查询")
private String name;
@ApiModelProperty(value = "头衔 1高级讲师 2首席讲师")
private Integer level;
@ApiModelProperty(value = "查询开始时间", example = "2019-01-01 10:10:10")
private String begin;//注意,这里使用的是String类型,前端传过来的数据无需进行类型转换
@ApiModelProperty(value = "查询结束时间", example = "2019-12-01 10:10:10")
private String end;
}
目录结构:
controller 层业务编写:
@ApiOperation("多条件组合查询分页功能")
@PostMapping("/pageQuery/{current}/{size}")
public R pageQuery(
@PathVariable(required = false) long current,
@PathVariable(required = false) long size,
@RequestBody(required = false) TeacherQuery teacherQuery
) {
// 创建分页对象
Page<EduTeacher> pageTeacher = new Page<>(current, size);
QueryWrapper<EduTeacher> queryWrapper = new QueryWrapper<>();
// 获取条件
String name = teacherQuery.getName();
Integer level = teacherQuery.getLevel();
// begin ~ end 课程创建时间
String begin = teacherQuery.getBegin();
String end = teacherQuery.getEnd();
// 使用 condition 组装条件
// 这种链式调用,MyBatisPlus 会自动使用 and 连接 SQL 语句,如果想用 or 连接,使用 .or() 方法
queryWrapper.like(StringUtils.isNotBlank(name), "name", name)
.eq(level != null, "level", level)
.ge(StringUtils.isNotBlank(begin), "gmt_create", begin)
.le(StringUtils.isNotBlank(end), "gmt_create", end);
eduTeacherService.page(pageTeacher, queryWrapper);
// 返回总页数 和 分页数据
return R.ok().data("rows", pageTeacher.getRecords()).data("total", pageTeacher.getTotal());
}
required = false : 这个属性表示这个参数不是必须要传的
@RequestBody : 获取请求体中的数据,只有POST 请求才会请求体。
和
@ResponseBody
区别:@ResponseBody 是 返回 JSON数据到前端
condition 参数介绍:
在 QueryWrapper 里面每一个方法都有 condition 参数,是一个 boolean 值,它表示是否使用后边的参数。
比如:
queryWrapper.like(StringUtils.isNotBlank(name), "name", "王")
- 如果
StringUtils.isNotBlank(name)
为 true :
- SQL 语句: select * from table where name like ‘%王%’ ;
- 如果
StringUtils.isNotBlank(name)
为 false :
- SQL 语句: select * from table ;
使用Swagger 测试:
首先将
gmt_create、gmt_modified
俩个字段设置自动填充
- 在 EduTeacher实体类中 增加 @TableField 注解
- 增加自动填充配置类
在 service_base 模块下,com.atguigu.servicebase 包下,创建 handler 包,创建 配置类 :
@Api("自动填充配置类")
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// fieldName : 是实体类的属性名,不是字段名
this.strictInsertFill(metaObject,"gmtCreate", Date.class,new Date());
this.strictInsertFill(metaObject,"gmtModified", Date.class,new Date());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictInsertFill(metaObject,"gmtModified", Date.class,new Date());
}
}
- 根据 ID 进行查询【用于页面回显数据】
- 根据 ID 修改讲师
根据ID查询:
@ApiOperation("根据ID查询讲师")
@GetMapping("/getTeacher/{id}")
public R getTeacher(@PathVariable Long id){
EduTeacher teacher = eduTeacherService.getById(id);
return teacher != null ? R.ok().data("teacher",teacher) : R.error().message("您查询的讲师不存在");
}
根据 ID 修改 :
@ApiOperation("根据修改讲师")
@PostMapping("/updateTeacher")
public R updateTeacher( @RequestBody EduTeacher eduTeacher){
return eduTeacherService.updateById(eduTeacher) ? R.ok() : R.error();
}
测试时,
gmt_create、gmt_modified
俩个字段不要写。
在逻辑删除字段 is_delete 属性上增加:@TableLogic
注解
根据 ID 删除讲师:
// 逻辑删除
@ApiOperation("逻辑删除教师")
@DeleteMapping("{id}")
public R removeTeacher(
@ApiParam(name = "id", value = "讲师ID", required = true)
@PathVariable String id) {
boolean flag = eduTeacherService.removeById(id);
return flag ? R.ok() : R.error();
}
在
com.atguigu.servicebase.handler
包下,创建 GlobalExceptionHandler 处理全局异常:
@ControllerAdvice // 增强 Controller
public class GlobalExceptionHandler{
@ExceptionHandler({Exception.class})
@ResponseBody //相应到浏览器上
public R handlerException(Exception e) {
e.printStackTrace();
return R.error().message("全局处理异常~~~");
}
}
service_base 中引入 common_utils 依赖,在 service 中可以把 common_utils 依赖删除。
演示结果:
特定异常处理:
@ApiOperation("特定异常处理~")
@ExceptionHandler({ArithmeticException.class})
@ResponseBody //响应到浏览器上
public R error(ArithmeticException e) {
e.printStackTrace();
return R.error().message("ArithmeticException异常处理~~~");
}
- 创建类自定义异常,继承 RunTimeException
- 全局统一异常处理自定义的异常
- 手动抛出自定义异常
自定义异常:
@Api("自定义异常~")
@Data
@AllArgsConstructor // 有参构造
@NoArgsConstructor // 无参构造
public class GuliException extends RuntimeException{
// 状态码
private Integer code ;
// 错误信息
private String msg ;
}
在
GlobalExceptionHandler
中处理自定义异常:
@ApiOperation("处理自定义异常~~")
@ExceptionHandler({GuliException.class})
@ResponseBody //响应到浏览器上
public R error(GuliException e) {
e.printStackTrace();
return R.error().code(e.getCode()).message(e.getMsg());
}
手动抛出异常:
由于是我们自定义的,系统不会抛出该异常,需要我们自己手动抛出 !
service_base 目录结构:
日志记录器(Logger)的行为是分等级的。如下表所示:
分为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL
后边的日志输出包含前面等级的日志输出
默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别
LogBack 和 log4j 类似,会比 log4j 有一些优点:https://blog.csdn.net/caisini_vc/article/details/48551287
- 删除 application 配置文件中的
日志配置
- 在 resources 目录下创建
logback-spring.xml
,并配置<configuration scan="true" scanPeriod="10 seconds"> <contextName>logbackcontextName> <property name="log.path" value="C:/18_gulixueyuan/log_info" /> <property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFOlevel> filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}Pattern> <charset>UTF-8charset> encoder> appender> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_info.logfile> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFOlevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_warn.logfile> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warnlevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_error.logfile> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERRORlevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <springProfile name="dev"> <logger name="com.guli" level="INFO" /> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> root> springProfile> <springProfile name="pro"> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="WARN_FILE" /> root> springProfile> configuration>
自动生成的日志文件
将错误信息打印到日志中去:
- 在全局处理异常的类
GlobalExceptionHandler
上 增加 @Slf4j 注解
- 使用
log.error()
方法将错误信息打印到日志中去测试结果:
如果想有更多的错误信息,使用以下的工具类:
- 在 common_utils 模块创建 ExceptionUtil 用来保存详细的错误信息
- 写完记得刷新一下调用该模块的 pom 文件,更新一下。
public class ExceptionUtil { public static String getMessage(Exception e) { StringWriter sw = null; PrintWriter pw = null; try { sw = new StringWriter(); pw = new PrintWriter(sw); // 将出错的栈信息输出到printWriter中 e.printStackTrace(pw); pw.flush(); sw.flush(); } finally { if (sw != null) { try { sw.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (pw != null) { pw.close(); } } return sw.toString(); } }
测试结果:
下载地址:Documentation for Visual Studio Code
安装中文界面:
- 首先安装中文插件:Chinese (Simplified) Language Pack for Visual Studio Code
- 右下角弹出是否重启vs,点击“yes”
- 有些机器重启后如果界面没有变化,则 点击 左边栏Manage -> Command Paletet…【Ctrl+Shift+p】
- 在搜索框中输入“configure display language”,回车
- 打开locale.json文件,修改文件下的属性 “locale”:“zh-cn”
安装常用插件:
ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)
3.1 如何定义一个变量
在 JS 中使用
var
在 ES6 中使用let
俩个的区别:
- var 没有局部作用域,let 有局部作用域
- var 可以定义多次,let 只能定义一次
<script>
{ // 代码块
var a = 1;
let b = 2;
}
console.log(a)
// // Uncaught ReferenceError: b is not defined
// b 没有被声明
console.log(b)
var m = 2 ;
var m = 3 ;
let n = 4 ;
let n = 5 ;
console.log(m)
//Uncaught SyntaxError: Identifier 'n' has already been declared
// n 变量已经被定义过了
console.log(n)
</script>
3.2 定义一个常量
const a = xxx ;
- 定义常量必须有初始值
- 常量只能赋一次值
const name = "李四" name = "王五" // ncaught TypeError: Assignment to constant variable. console.log(name) const sex ; // Uncaught SyntaxError: Missing initializer in const declaration console.log(sex)
3.3 数组结构赋值
<script>
// 传统方式
var a = 1, b = 2, c = 3;
console.log(a, b, c)
// ES6 方式
let [x, y, z] = [1, 2, 3];
console.log(x, y, z) // 1,2,3
</script>
3.4 对象结构赋值
<script>
// 定义对象
let user = { "name": "张三", "age": 20 };
// 传统方式
let name1 = user.name;
let age1 = user.age
console.log(name1 + "————" + age1)
// ES6 方式
// {} 里面的属性必须都是 user 对象的属性
let {name,age} = user
console.log(name + "------" + age)
</script>
3.5 模板字符串
模板字符串 : ``
- 支持换行
- 可以镶嵌
${ JavaScript代码 }
- 可以调用函数 :
${ function() }
<script>
// 1.模板字符串实现换行
let v1 = `hello,world !!
我是模板字符串~`
console.log(v1)
// 2. 模板字符串可以使用 ${} ,放入 Javascript 代码
let name = "李四"
let age = 20 ;
let v2 = `my name is ${name}, my age is ${age+1} next year`
console.log(v2)
// 3. ${} 调用函数
function a() {
return "hello"
}
let v3 = `${a()},jack`
console.log(v3)
</script>
3.6 定义方法
<script>
// 定义一个对象 p
const p = {
// 传统方式
method1: function () {
console.log("method1")
},
// ES6 方式
method2() {
}
}
// 调用
p.method1()
p.method2()
</script>
3.7 对象拓展运算符
作用:
- 拷贝对象
- 合并对象
<script>
// 1. 拷贝对象
const user = { "name": "zhangsan", "age": 20 }
const user1 = { ...user }
console.log(user1)
// 2.合并对象
const book = { "bookName": "活着", "id": 1 }
const book1 = { "bookName": "苏格拉底的申辩", "price": 100 }
const books = { ...book, ...book1 }
console.log(books)
</script>
当对象中有重复属性时,后面的会把前面的覆盖掉
3.8 箭头函数
语法:
参数 => 函数体
如果有多个参数使用 () 括起来,用 , 分割
<script>
// 传统方式
let m1 = function (a) {
return a
}
console.log(m1(1))
// 箭头函数
let m2 = a => a;
console.log(m2(2));
// 多个参数
let m3 = function (a, b) {
return a + b
}
console.log(m3(2, 2));
// 传统方式
let m4 = (a, b) => a + b
console.log(m4(2, 3));
</script>
Vue是 动态构建用户界面的渐进式 JavaScript 框架
特点:
- 遵循 MVVM 模式
- 编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发3. 它本身只关注 UI, 也可以引入其它第三方库开发项目
下载地址:安装 — Vue.js (vuejs.org)
使用开发版本可以使用 Vue 插件调试,插件直接在浏览器插件商店中搜索
Vue.js devtools
安装即可
4.1 Vue 入门
- 引入 Vue.js 文件
- 编写案例
<body>
<script src="js/vue.js">script>
<div id="app">
<h1>{{message}}h1>
div>
<script>
// 创建 Vue 实例对象
new Vue({
el: '#app',
data: {
message: 'hello,world!!'
},
// data的第二种写法
// data() {
// return {
// message: 'hello,world!!'
// }
// },
})
script>
body>
关于 Vue 的一些基础知识:
- 想让Vue工作,就必须创建一个Vue实例 【new Vue()】,且要传入一个配置对象
- 容器里的代码称为 【Vue模板】
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
- el : 指明当前Vue实例为哪个容器服务,值通常为 css 选择器字符串。
- 类选择器:
el:'.xxxx'
- ID 选择器:
el:'#xxxx'
- 标签选择器:
el:'xxxx'
- data : 用于存储数据,数据供 el 所指定的容器使用
- data 的第二种写法,return 返回的是需要的数据
4.2 抽取 Vue 公共的代码片段
Vue 实例的编写大多都是固定的,将固定的代码抽取出来设置快捷方式。
文件 => 首选项 => 用户代码片段 => 新建全局代码片段/或文件夹代码片段:
vue-html.code-snippets
, 后缀名不能变复制以下内容,快捷键就是: vuehtm
{
"vue htm": {
"scope": "html",
"prefix": "vuehtml",
"body": [
"DOCTYPE html>",
"",
"",
"<head>",
" ",
" ",
" ",
" <title>Documenttitle>",
"head>",
"",
"<body>",
" ",
"",
" div>",
"