谷粒学院(二)

一.搭建一个多模块Springboot项目

这里的建议 如果不会使用 idea 创建maven模块的 建议去复习一下哦,在这里本人浪费了很长时间:

先把 三级 按照层次 搭建起来之后 再引入相关配置

创建一个名字为guli_parent 的Springboot项目

  • 懂得都懂,删除src目录;

  • 设置 packaging

<packaging>pom</packaging>
  • 配置 properties 和 dependencyManagement
<properties>
        <java.version>1.8</java.version>
        <guli.version>0.0.1-SNAPSHOT</guli.version>
        <mybatis-plus.version>3.0.5</mybatis-plus.version>
        <velocity.version>2.0</velocity.version>
        <swagger.version>2.7.0</swagger.version>
        <aliyun.oss.version>2.8.3</aliyun.oss.version>
        <jodatime.version>2.10.1</jodatime.version>
        <poi.version>3.17</poi.version>
        <commons-fileupload.version>1.3.1</commons-fileupload.version>
        <commons-io.version>2.6</commons-io.version>
        <httpclient.version>4.5.1</httpclient.version>
        <jwt.version>0.7.0</jwt.version>
        <aliyun-java-sdk-core.version>4.3.3</aliyun-java-sdk-core.version>
        <aliyun-sdk-oss.version>3.1.0</aliyun-sdk-oss.version>
        <aliyun-java-sdk-vod.version>2.15.2</aliyun-java-sdk-vod.version>
        <aliyun-java-vod-upload.version>1.4.11</aliyun-java-vod-upload.version>
        <aliyun-sdk-vod-upload.version>1.4.11</aliyun-sdk-vod-upload.version>
        <fastjson.version>1.2.28</fastjson.version>
        <gson.version>2.8.2</gson.version>
        <json.version>20170516</json.version>
        <commons-dbutils.version>1.7</commons-dbutils.version>
        <canal.client.version>1.1.0</canal.client.version>
        <docker.image.prefix>zx</docker.image.prefix>
        <cloud-alibaba.version>0.2.2.RELEASE</cloud-alibaba.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!--Spring Cloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--mybatis-plus 持久层-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>${velocity.version}</version>
            </dependency>
            <!--swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <!--swagger ui-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <!--aliyunOSS-->
            <dependency>
                <groupId>com.aliyun.oss</groupId>
                <artifactId>aliyun-sdk-oss</artifactId>
                <version>${aliyun.oss.version}</version>
            </dependency>
            <!--日期时间工具-->
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
                <version>${jodatime.version}</version>
            </dependency>
            <!--xls-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>${poi.version}</version>
            </dependency>
            <!--xlsx-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>${poi.version}</version>
            </dependency>
            <!--文件上传-->
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>${commons-fileupload.version}</version>
            </dependency>
            <!--commons-io-->
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>${commons-io.version}</version>
            </dependency>
            <!--httpclient-->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>${httpclient.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>${gson.version}</version>
            </dependency>
            <!-- JWT -->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>${jwt.version}</version>
            </dependency>
            <!--aliyun-->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>${aliyun-java-sdk-core.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun.oss</groupId>
                <artifactId>aliyun-sdk-oss</artifactId>
                <version>${aliyun-sdk-oss.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-vod</artifactId>
                <version>${aliyun-java-sdk-vod.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-vod-upload</artifactId>
                <version>${aliyun-java-vod-upload.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-sdk-vod-upload</artifactId>
                <version>${aliyun-sdk-vod-upload.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>org.json</groupId>
                <artifactId>json</artifactId>
                <version>${json.version}</version>
            </dependency>
            <dependency>
                <groupId>commons-dbutils</groupId>
                <artifactId>commons-dbutils</artifactId>
                <version>${commons-dbutils.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.otter</groupId>
                <artifactId>canal.client</artifactId>
                <version>${canal.client.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

报错没事,是因为没有引用,只是配置里版本

创建service模块

  • 懂得都懂,删除src目录;

  • 导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>
    <!--hystrix依赖,主要是用  @HystrixCommand -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <!--服务注册-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--服务调用-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--mybatis-plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
    </dependency>
    <!--swagger-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
    </dependency>
    <!--lombok用来简化实体类:需要安装lombok插件-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <!--xls-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
    </dependency>
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
    </dependency>
    <!--httpclient-->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    <!--commons-io-->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
    </dependency>
    <!--gson-->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

点击 service 创建 service_edu 的子子模块

这个是具体的代码书写模块,先以这个讲师模块为例

整体原理:

在 guli_parent 中 配置所有的 properties 与 dependencyManagement

在 service 中进行 导入

在 service_edu 中进行 使用

配置applicatioin.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=034312
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

使用Mybatis-Plus自带的代码生成器,生成基本项目框架

public class CodeGeneratorUtil {
    @Test
    public void main1() {
        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        System.out.println(projectPath);
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("atguigu");
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖
        /*
         * mp生成service层代码,默认接口名称第一个字母有 I
         * UcenterService
         * */
        gc.setServiceName("%sService"); //去掉Service接口的首字母I
        gc.setIdType(IdType.ID_WORKER); //主键策略
        gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
        gc.setSwagger2(true);//开启Swagger2模式
        mpg.setGlobalConfig(gc);
        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);
        // 4、包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("serviceedu"); //模块名
        pc.setParent("com.atguigu");
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);
        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("edu_teacher");
        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
        mpg.setStrategy(strategy);
        // 6、执行
        mpg.execute();
    }
}

二.牛刀小试

controller

@RestController
//@RequestMapping("/service_edu/edu-teacher")
public class EduTeacherController {
    @Autowired
    private EduTeacherService eduTeacherService;

    @GetMapping("/teacher")
    public List<EduTeacher> selectAll(){
        return eduTeacherService.list(null);
}
}

service

@Service
public class EduTeacherServiceImpl extends ServiceImpl<EduTeacherMapper, EduTeacher> implements EduTeacherService {


}

使用自动生成mp结构,他把 service的实现类 也做了封装,在继承的父类 中 就已经对 mapper 进行了注入,并且封装了 常用的简单方法, 是开发更加方便了

config

@Configuration
@MapperScan("com.atguigu.service_edu.mapper")
public class MybatisPlusConfig {
}

application

@SpringBootApplication
public class EduApplication {
    public static void main(String[] args) {
        SpringApplication.run(EduApplication.class,args);
    }
}

在运行之前 需要把 pom 中 关于org.springframework.cloud 的依赖 注释一下 不然会报错

三.时间格式配置

返回的时间格式默认是 格林尼治格式的时间

在 application.properties 中进行配置

# 返回json的全局时间格式
spring.jackson.date-format=yy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT%2B8

四.配置逻辑删除

这个在 一 部分说过了哦,复习一下

  1. 在属性上 添加注解 @TableLogic

  2. 配置 逻辑删除插件 (如果Mybatis-Plus版本很高的话不用配置)

    @Configuration
    @MapperScan("com.atguigu.service_edu.mapper")
    public class MybatisPlusConfig {
        /*
    逻辑删除插件
     */
        @Bean
        public ISqlInjector sqlInjector() {
            return new LogicSqlInjector();
        }
    
    }
    
  3. 测试

    @DeleteMapping("/delete/{id}")
    @ApiOperation("删除操作")
    public Boolean deleteEduById( @ApiParam(name = "id",value = "老师ID",required = true) @PathVariable("id") String id){
        return eduTeacherService.removeById(id);
    }
    

    由于在浏览器 测试是看不出结果的,所以我们 配置 Swagger

五.Swagger配置

前后端分离开发模式中,api文档是最好的沟通方式

Swagger 是一个规范 和 完整的框架,用于生成、描述、调用和可视化RESTful风格的WEB服务

1.及时性(接口变更 ,更新及时)

2.规范性(接口地址,请求方式,参数及响应格式和错误信息)

3.一致性(与后端 接口保持一致)

4.可测性(方便测试)

在这里 我们新建一个在项目根目录下 创建一个 common的公共模块,在common引入相关依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <scope>provided </scope>
    </dependency>
    <!--mybatis-plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <scope>provided </scope>
    </dependency>
    <!--lombok用来简化实体类:需要安装lombok插件-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided </scope>
    </dependency>
    <!--swagger-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <scope>provided </scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <scope>provided </scope>
    </dependency>
    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- spring2.X集成redis所需common-pool2
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.6.0</version>
    </dependency>-->
</dependencies>

在common下,在创建一个service_base模块,并且创建包 src/main/java/com/atguigu/service_base/config,在其下创建SwaggerConfig

@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", "55317332@qq.com"))
                .build();
    }
}

目录结构如下

在这里插入图片描述

在service的pom中进行引入

<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>service_base</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

配置启动类,扫描组件 @ComponentScan(“com.atguigu”)


@SpringBootApplication
@ComponentScan("com.atguigu") // 扫描所有包下的配置
public class EduApplication {
    public static void main(String[] args) {
        SpringApplication.run(EduApplication.class,args);
    }
}

这样就基本配置好了

Swagger基本使用

@Api() 类

@ApiOperate 方法

@ApiParam 参数

@ApiModelProperty 实体类属性

@RestController
@RequestMapping("/service_edu/edu-teacher")
@Api("EduTeacher API 文档")
public class EduTeacherController {
    @Autowired
    private EduTeacherService eduTeacherService;

    @GetMapping("/selectAll")
    @ApiOperation("查询老师列表")
    public List<EduTeacher> selectAll() {
        return eduTeacherService.list(null);
    }

    @DeleteMapping("/delete/{id}")
    @ApiOperation("删除操作")
    public Boolean deleteEduById( @ApiParam(name = "id",value = "老师ID",required = true) @PathVariable("id") String id){
        return eduTeacherService.removeById(id);
    }
}

六.统一返回数据格式

项目中 我们会将响应封装成json返回,一般我们会将所有的接口的数据格式统一,使前端(IOS Android,Web)对数据的操作更一致,轻松。

一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但使一般都会包含状态码、返回消息、数据这几部分内容

{
  "success": 布尔, //响应是否成功
  "code": 数字, //响应码
  "message": 字符串, //返回消息
  "data": HashMap //返回数据,放在键值对中
}

抽取公共模块:在common下创建子模块 common_utils

创建接口定义返回值

创建包 com.atguigu.commono_utils 创建接口 ResultCode.java

public interface ResultCode {
    Integer SUCCESS = 20001;
    Integer ERROR = 20000;

}

封装结果类

@Data
public class Response {
    @ApiModelProperty(value = "是否成功")
    private Boolean success;
    @ApiModelProperty(value = "响应码")
    private Integer code;
    @ApiModelProperty(value = "返回消息")
    private String message;
    @ApiModelProperty(value = "返回数据")
    private Map<String, Object> data = new HashMap<>();
    private Response(){}

    public static Response ok(){
        Response response = new Response();
        response.setSuccess(true);
        response.setCode(ResultCode.SUCCESS);
        response.setMessage("成功");
        return response;
    }
    public static Response err(){
        Response response = new Response();
        response.setSuccess(false);
        response.setCode(ResultCode.ERROR);
        response.setMessage("失败");
        return response;

    }

    public Response success(Boolean success){
        this.setSuccess(success);
        return this;
    }
    public Response message(String message){
        this.setMessage(message);
        return this;
    }
    public Response code(Integer code){
        this.setCode(code);
        return this;
    }
    public Response data(Map<String,Object> map){
        this.setData(map);
        return this;
    }
    public Response data(String key,Object value){
        this.data.put(key,value);
        return this;
    }

}

在service 模块中 引入 使用 pom 添加

<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>common_utils</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

修改controller结果

@GetMapping("/selectAll")
@ApiOperation("查询老师列表")
public Response selectAll() {
    return Response.ok().data("item",eduTeacherService.list(null));
}

@DeleteMapping("/delete/{id}")
@ApiOperation("删除操作")
public Response deleteEduById( @ApiParam(name = "id",value = "老师ID",required = true) @PathVariable("id") String id){
    boolean isSuccess = eduTeacherService.removeById(id);
    if(isSuccess){
        return Response.ok();
    }
    return Response.err();
}

七.讲师分页查询

配置分页插件

/**
 * 分页插件
 */
@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}

添加分页方法

@GetMapping("/selectAllByPageHelper/{current}/{size}")
@ApiOperation("查询老师列表分页查询")
public Response selectAllByPageHelper(
        @PathVariable("current") Integer current,
        @PathVariable("size") Integer size
) {
    Page<EduTeacher> page = new Page<>(current, size);
    eduTeacherService.page(page,null);
    Map<String, Object> map = new HashMap<>();
    long total = page.getTotal();
    List<EduTeacher> records = page.getRecords();
    map.put("total",total);
    map.put("item",records);
    return Response.ok().data(map);
}

测试 swagger-ui.html

八.多条件分页查询

/**
* @Description: 多组合条件查询带分页
 *  name 模糊查询 ,是否相等 level  , create 是否大于某个时间
* @date 2021/10/18 下午9:58
*/
@PostMapping("/pageTeacherHelper/{current}/{size}")
public Response pageTeacherHelper(
        @PathVariable("current") Integer current,
        @PathVariable("size") Integer size,
        @RequestBody(required = false) TeacherQuery teacherQuery
        ){
    String name = teacherQuery.getName();
    Integer level = teacherQuery.getLevel();
    Date createTime = teacherQuery.getCreateTime();
    QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
    if(!StringUtils.isEmpty(name)){
        wrapper.like("name",name); // 注意 这里是表中的 字段名
    }
    if(!StringUtils.isEmpty(level)){
        wrapper.eq("level",level);
    }
    if(!StringUtils.isEmpty(createTime)){
        wrapper.ge("gmt_create",createTime);
    }
    Page<EduTeacher> page = new Page<>(current,size);
    eduTeacherService.page(page,wrapper);
    long total = page.getTotal();
    List<EduTeacher> records = page.getRecords();
    return Response.ok().data("total",total).data("item",records);

}

注意点

  • 使用 @RequstBody 接收对象,必须使用Post 请求

  • @RequestBody(required = false) 允许对象为空

  • 辨别几个注解

    @PathVariable 接收单个参数

    @RequestBody 接收一个VO对象

    @ResponseBody 返回一个对象

九.插入修改-自动填充

抽取公共模块:在service_base 下 src/main/java/com/atguigu/service_base 创建 handler,并创建类MyMetaObjectHandler

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // 属性 , 创建时间  , 属性名
        this.setFieldValByName("gmtCreate",new Date(),metaObject);
        this.setFieldValByName("gmtModified",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("gmtModified",new Date(),metaObject);

    }
}

启动类:扫描包@ComponentScan(“com.atguigu”)

实体类:@TableField

controller

@PostMapping("/addTeacher")
public Response addTeacher(
        @RequestBody(required = false) EduTeacher eduTeacher){
    boolean save = eduTeacherService.save(eduTeacher);
    if(save){
        return Response.ok();
    }
    return Response.err();
}

十.异常处理

我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,需要统一异常处理

统一异常处理:创建统一异常处理器

在service-base中的handler包中创建统一异常处理类GlobalExceptionHandler.java:

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class) // 捕获什么异常
    @ResponseBody                      // 返回响应体
    public Response error(Exception e){
        e.printStackTrace();
        return Response.err().message("触发类全局异常");
    }
}

然后导入 依赖 就可以了哦

特殊异常处理:与统一异常处理一样,只是换一下 异常类,

处理机制:当异常处理类中 触发了特殊异常 就不再执行统一异常了

自定义异常处理:需要手动抛出

  1. 定义一个异常

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class GuliException extends RuntimeException{
    
        private Integer code;
        private String message;
    }
    
  2. 配置异常处理器

    @ControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(Exception.class) // 捕获什么异常
        @ResponseBody                      // 返回响应体
        public Response error(Exception e){
            e.printStackTrace();
            return Response.err().message("触发类全局异常");
        }
    
        @ExceptionHandler(GuliException.class) // 捕获什么异常
        @ResponseBody                      // 返回响应体
        public Response guliError(GuliException e){
            e.printStackTrace();
            return Response.err().code(e.getCode()).message(e.getMessage());
        }
    }
    
  3. 测试

    @GetMapping("/teacher/{id}")
    @ApiOperation("查询功能")
    public Response selectTeacherById(@PathVariable("id") Integer id){
        try {
            System.out.println(10 / 0);
        } catch (Exception e) {
            throw new GuliException(20001,"触发了自定义异常"); // 手动抛出
        }
        return Response.ok().data("item", eduTeacherService.getById(id));
    
    }
    

十一.日志

普通日志配置

日志记录器(Logger)的行为是分等级的。如下表示:

分为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL

默认情况下,spring boot 从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别

# 设置日志级别

logging.level.root = WARN

这种方式,只能将日志打印到控制台

LogBack日志配置

Spring boot内部使用的Logback作为日志实现的框架

  1. 配置Logback日志

    • 删除 application.properties中的日志配置

    • resources 中创建logback-spring.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <configuration  scan="true" scanPeriod="10 seconds">
          <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
          <!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
          <!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
          <!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
          <contextName>logback</contextName>
          <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
          <property name="log.path" value="D:/guli_log/edu" />
          <!-- 彩色日志 -->
          <!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 -->
          <!-- magenta:洋红 -->
          <!-- boldMagenta:粗红-->
          <!-- cyan:青色 -->
          <!-- white:白色 -->
          <!-- magenta:洋红 -->
          <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">
              <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
              <!-- 例如:如果此处配置了INFO级别,则后面其他位置即使配置了DEBUG级别的日志,也不会被输出 -->
              <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                  <level>INFO</level>
              </filter>
              <encoder>
                  <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
                  <!-- 设置字符集 -->
                  <charset>UTF-8</charset>
              </encoder>
          </appender>
          <!--输出到文件-->
          <!-- 时间滚动输出 level为 INFO 日志 -->
          <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
              <!-- 正在记录的日志文件的路径及文件名 -->
              <file>${log.path}/log_info.log</file>
              <!--日志文件输出格式-->
              <encoder>
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
                  <charset>UTF-8</charset>
              </encoder>
              <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
              <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                  <!-- 每天日志归档路径以及格式 -->
                  <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                  <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                      <maxFileSize>100MB</maxFileSize>
                  </timeBasedFileNamingAndTriggeringPolicy>
                  <!--日志文件保留天数-->
                  <maxHistory>15</maxHistory>
              </rollingPolicy>
              <!-- 此日志文件只记录info级别的 -->
              <filter class="ch.qos.logback.classic.filter.LevelFilter">
                  <level>INFO</level>
                  <onMatch>ACCEPT</onMatch>
                  <onMismatch>DENY</onMismatch>
              </filter>
          </appender>
          <!-- 时间滚动输出 level为 WARN 日志 -->
          <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
              <!-- 正在记录的日志文件的路径及文件名 -->
              <file>${log.path}/log_warn.log</file>
              <!--日志文件输出格式-->
              <encoder>
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
                  <charset>UTF-8</charset> <!-- 此处设置字符集 -->
              </encoder>
              <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
              <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                  <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                  <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                      <maxFileSize>100MB</maxFileSize>
                  </timeBasedFileNamingAndTriggeringPolicy>
                  <!--日志文件保留天数-->
                  <maxHistory>15</maxHistory>
              </rollingPolicy>
              <!-- 此日志文件只记录warn级别的 -->
              <filter class="ch.qos.logback.classic.filter.LevelFilter">
                  <level>warn</level>
                  <onMatch>ACCEPT</onMatch>
                  <onMismatch>DENY</onMismatch>
              </filter>
          </appender>
          <!-- 时间滚动输出 level为 ERROR 日志 -->
          <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
              <!-- 正在记录的日志文件的路径及文件名 -->
              <file>${log.path}/log_error.log</file>
              <!--日志文件输出格式-->
              <encoder>
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
                  <charset>UTF-8</charset> <!-- 此处设置字符集 -->
              </encoder>
              <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
              <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                  <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                  <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                      <maxFileSize>100MB</maxFileSize>
                  </timeBasedFileNamingAndTriggeringPolicy>
                  <!--日志文件保留天数-->
                  <maxHistory>15</maxHistory>
              </rollingPolicy>
              <!-- 此日志文件只记录ERROR级别的 -->
              <filter class="ch.qos.logback.classic.filter.LevelFilter">
                  <level>ERROR</level>
                  <onMatch>ACCEPT</onMatch>
                  <onMismatch>DENY</onMismatch>
              </filter>
          </appender>
          <!--
              <logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。
              <logger>仅有一个name属性,
              一个可选的level和一个可选的addtivity属性。
              name:用来指定受此logger约束的某一个包或者具体的某一个类。
              level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
                    如果未设置此属性,那么当前logger将会继承上级的级别。
          -->
          <!--
              使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
              第一种把<root level="INFO">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
              第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别:
           -->
          <!--开发环境:打印控制台-->
          <springProfile name="dev">
              <!--可以输出项目中的debug日志,包括mybatis的sql日志-->
              <logger name="com.guli" level="INFO" />
              <!--
                  root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
                  level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG
                  可以包含零个或多个appender元素。
              -->
              <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>
      

      这样 基本上就配置好了

  2. 将错误日志输出到文件

    在 全局异常处理器类 上加上注解@Slf4j

    @ControllerAdvice
    @Slf4j
    public class GlobalExceptionHandler {}
    

    异常输出语句

    log.error(e.getMessage())
    
  3. 将日志堆栈信息输出到文件

    定义工具类

    guli-framework-common下创建 util包,创建 ExceptionUtil.java工具类

    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();
        }
    }
    

    调用:log.error(ExceptionUtil.getMessage(e));

    GuliException中创建toString方法

    @Override
    public String toString() {
        return "GuliException{" +
            "message=" + this.getMessage() +
            ", code=" + code +
            '}';
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值