谷粒学院(二) 讲师管理模块

news2025/1/26 5:59:01

一、讲师管理模块配置

1、在service下面service-edu模块中创建配置文件

 2. resources目录下创建文件 application.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=12345

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

3. 在test/java目录下创建包com.atguigu.eduservice,创建代码生成器:CodeGenerator.java

public class CodeGenerator {

    @Test
    public void run() {

        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir("D:/workspace_idea1/guli_parent/service/service-edu" + "/src/main/java");
        gc.setAuthor("chenyifan");
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖
        gc.setServiceName("%sService");	//去掉Service接口的首字母I
        gc.setIdType(IdType.ID_WORKER_STR); //主键策略
        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("12345");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();
        //包:com.atguigu.eduservice
        pc.setParent("com.atguigu");
        pc.setModuleName("eduservice"); //模块名
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("edu_comment");//表名,多张表逗号分隔
        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();
    }
}

4. 创建SpringBoot配置类

在edu包下创建config包,创建MyBatisPlusConfig.java

@EnableTransactionManagement 注解的主要作用是开启对事务的支持,等同于xml配置方式的 <tx:annotation-driven />

关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。

@Configuration
@EnableTransactionManagement
@MapperScan("com.atguigu.eduservice.mapper")
public class MyBatisPlusConfig {
    
}

 5. 配置SQL执行性能分析插件

/**
	 * SQL 执行性能分析插件
	 * 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
	 */
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(1000);//ms,超过此处设置的ms则sql不执行
    performanceInterceptor.setFormat(true);
    return performanceInterceptor;
}

6.统一返回的json时间格式

默认情况下json时间格式带有时区,并且是世界标准时间,和我们的时间差了八个小时

在application.properties中设置

#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

 

二、逻辑删除功能

EduTeacherController添加删除方法

@DeleteMapping("{id}")
public boolean removeById(@PathVariable String id){
    return teacherService.removeById(id);
}

配置逻辑删除插件

/**
 * 逻辑删除插件
 */
@Bean
public ISqlInjector sqlInjector() {
    return new LogicSqlInjector();
}

 

三、跨域配置

1、什么是跨域

浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域 。前后端分离开发中,需要考虑ajax跨域的问题。

这里我们可以从服务端解决这个问题

2、配置

在Controller类上添加注解

@CrossOrigin //跨域

四、配置Swagger

1、swagger2 介绍

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

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

  1. 及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
  2. 规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
  3. 一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
  4. 可测性 (直接在接口文档上进行测试,以方便理解业务)

2、配置Swagger2

在guli-parent下创建模块common,在common下面创建子模块service-base,在模块service-base中,创建swagger的配置类

创建包com.atguigu.servicebase.config,创建类SwaggerConfig

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    /*
     Swagger 有自己的实例 Docket,如果我们想要自定义基本信息,可以使用 docket 来配置 swagger 的基本信息
    */
    @Bean
    public Docket webApiConfig(Environment environment){
        
        //在开发和生产环境开启 swagger
        Profiles profiles = Profiles.of("dev", "test");
        boolean flag = environment.acceptsProfiles(profiles);

        return new Docket(DocumentationType.SWAGGER_2)
                .enable(flag)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();
    }

    /*
    基本信息的设置在 ApiInfo 这个对象中。
     */
    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("谷粒学院API文档")
                .description("本文档描述了课程中心微服务接口定义")
                .version("1.0")
                .contact(new Contact("cyf", "http://atguigu.com", "3207215962@qq.com"))
                .build();
    }
}

可以参考这篇文章:Swagger-的使用(详细教程)_多加点辣也没关系的博客-CSDN博客

3、在模块service模块中引入service-base

4、在service-edu启动类上添加注解,进行测试

改变包扫描的规则,使能够加载service_base里配置的swagger

因为我的service-edu模块包名为:com.atguigu.eduservice

我的service-base模块包名为:com.atguigu.servicebase

@SpringBootApplication
@ComponentScan(basePackages = {"com.atguigu"})
//改变包扫描的规则,使能够加载service_base里配置的swagger
public class EduApplication8001 {
    public static void main(String[] args) {
        SpringApplication.run(EduApplication8001.class,args);
    }
}

5、API模型

可以添加一些自定义设置,例如:定义样例数据

@ApiModelProperty(value = "创建时间", example = "2019-01-01 8:00:00")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;

@ApiModelProperty(value = "更新时间", example = "2019-01-01 8:00:00")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;

定义接口说明和参数说明 

定义在类上:@Api

定义在方法上:@ApiOperation

定义在参数上:@ApiParam

@Api(description="讲师管理")
@RestController
@RequestMapping("/admin/edu/teacher")
public class TeacherAdminController {

	@Autowired
	private TeacherService teacherService;

	@ApiOperation(value = "所有讲师列表")
	@GetMapping
	public List<Teacher> list(){
		return teacherService.list(null);
	}

	@ApiOperation(value = "根据ID删除讲师")
	@DeleteMapping("{id}")
	public boolean removeById(
			@ApiParam(name = "id", value = "讲师ID", required = true)
			@PathVariable String id){
		return teacherService.removeById(id);
	}
}

五、统一返回数据格式

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

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

例如,我们的系统要求返回的基本数据格式如下:

列表

{
  "success": true,
  "code": 20000,
  "message": "成功",
  "data": {
    "items": [
      {
        "id": "1",
        "name": "刘德华",
        "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"
      }
    ]
  }
}

分页:

{
  "success": true,
  "code": 20000,
  "message": "成功",
  "data": {
    "total": 17,
    "rows": [
      {
        "id": "1",
        "name": "刘德华",
        "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"
      }
    ]
  }
}

没有返回数据:

{
  "success": true,
  "code": 20000,
  "message": "成功",
  "data": {}
}

失败:

{
  "success": false,
  "code": 20001,
  "message": "失败",
  "data": {}
}

因此,我们定义统一结果

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

在common_utils模块添加返回格式

public interface ResultCode {
    Integer SUCCESS = 20000;

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

    //构造方法私有
    private R() {}

    //链式编程

    //成功静态方法
    public static R ok(){
        R r = new R();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        return r;
    }

    //失败静态方法
    public static R error(){
        R r = new R();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        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;
    }

}

在controller中就可以这样定义

@ApiOperation(value = "所有讲师列表")
@GetMapping
public R list(){
    List<Teacher> list = teacherService.list(null);
    return R.ok().data("items", list);
}

六、分页和条件查询接口开发

配置分页插件

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

TeacherAdminController中添加分页方法

@ApiOperation(value = "分页讲师列表")
@GetMapping("{page}/{limit}")
public R pageList(
    @ApiParam(name = "page", value = "当前页码", required = true)
    @PathVariable Long page,

    @ApiParam(name = "limit", value = "每页记录数", required = true)
    @PathVariable Long limit){

    Page<Teacher> pageParam = new Page<>(page, limit);

    teacherService.page(pageParam, null);
    List<Teacher> records = pageParam.getRecords();
    long total = pageParam.getTotal();

    return  R.ok().data("total", total).data("rows", records);
}

七、条件查询

根据讲师名称name,讲师头衔level、讲师入驻时间gmt_create(时间段)查询

创建查询对象

@ApiModel(value = "Teacher查询对象", description = "讲师查询对象封装")
@Data
public class TeacherQuery implements Serializable {

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

service

public interface TeacherService extends IService<Teacher> {

	void pageQuery(Page<Teacher> pageParam, TeacherQuery teacherQuery);
}

实现

@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherMapper, Teacher> implements TeacherService {

	@Override
	public void pageQuery(Page<Teacher> pageParam, TeacherQuery teacherQuery) {

		QueryWrapper<Teacher> queryWrapper = new QueryWrapper<>();
		queryWrapper.orderByAsc("sort");

		if (teacherQuery == null){
			baseMapper.selectPage(pageParam, queryWrapper);
			return;
		}

		String name = teacherQuery.getName();
		Integer level = teacherQuery.getLevel();
		String begin = teacherQuery.getBegin();
		String end = teacherQuery.getEnd();

		if (!StringUtils.isEmpty(name)) {
			queryWrapper.like("name", name);
		}

		if (!StringUtils.isEmpty(level) ) {
			queryWrapper.eq("level", level);
		}

		if (!StringUtils.isEmpty(begin)) {
			queryWrapper.ge("gmt_create", begin);
		}

		if (!StringUtils.isEmpty(end)) {
			queryWrapper.le("gmt_create", end);
		}

		baseMapper.selectPage(pageParam, queryWrapper);
	}
}

八、自动填充封装

在service-base模块中添加,在实体类添加自动填充注解

@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);

    }
}

九、统一异常处理

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

1. 自定义异常类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GuliException extends RuntimeException{
    private Integer code; //状态码
    private String msg; //异常信息
}

2. 业务中需要的位置抛出GuliException

try {
    int a = 10/0;
}catch(Exception e) {
    throw new GuliException(20001,"出现自定义异常");
}

3. 添加异常处理方法

@ControllerAdvice ,这是一个非常有用的注解,顾名思义,这是一个增强的 Controller。使用这个 Controller ,可以实现三个方面的功能:

  1. 全局异常处理
  2. 全局数据绑定
  3. 全局数据预处理

灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这是 SpringMVC 提供的功能,在 Spring Boot 中可以直接使用。

具体可参考:SpringMVC 中 @ControllerAdvice 注解的三种使用场景! - 江南一点雨 - 博客园

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

   //全局异常
   @ExceptionHandler(Exception.class)
   @ResponseBody //为了返回数据
   public R error(Exception e){
      e.printStackTrace();
      return R.error().message("执行了全局异常处理");
   }

   //特定异常
   @ExceptionHandler(ArithmeticException.class)
   @ResponseBody //为了返回数据
   public R error(ArithmeticException e){
      e.printStackTrace();
      return R.error().message("执行了ArithmeticException异常处理");
   }

   //自定义异常
   @ExceptionHandler(GuliException.class)
   @ResponseBody //为了返回数据
   public R error(GuliException e){
      log.error(e.getMsg());
      e.printStackTrace();
      return R.error().code(e.getCode()).message(e.getMsg());
   }
}

4. 测试

 

十、统一日志处理

1、配置日志级别

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

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

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

# 设置日志级别
logging.level.root=WARN

这种方式只能将日志打印在控制台上

2、Logback日志

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

Logback和log4j非常相似,如果你对log4j很熟悉,那对logback很快就会得心应手。

logback相对于log4j的一些优点:

比log4j更好的logback、简介和优点详解_小雄哥的博客-CSDN博客

3、配置logback日志

删除application.properties中的日志配置

安装idea彩色日志插件:grep-console

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>

4、将错误日志输出到文件

GlobalExceptionHandler.java 中

类上添加注解

@Slf4j

异常输出语句

log.error(e.getMessage());

 

5、将日志堆栈信息输出到文件

定义工具类

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 +
        '}';
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/19034.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java基于ssm网上超市购物商城-计算机毕业设计

项目介绍 网上超市是商业贸易中的一条非常重要的道路&#xff0c;可以把其从传统的实体模式中解放中来&#xff0c;网上购物可以为消费者提供巨大的便利。通过网上超市这个平台&#xff0c;可以使用户足不出户就可以了解现今的流行趋势和丰富的商品信息&#xff0c;为用户提供…

MySQL8.0优化 - 锁 - 从数据操作的粒度划分:表级锁、页级锁、行锁

文章目录学习资料锁的不同角度分类锁的分类图如下从数据操作的粒度划分&#xff1a;表级锁、页级锁、行锁表锁&#xff08;Table Lock&#xff09;表级别的S锁、X锁意向锁&#xff08;intention lock&#xff09;自增锁&#xff08;AUTO-INC锁&#xff09;元数据锁&#xff08;…

算法刷题介绍

算法刷题介绍 课程大纲 Python 作为实现语言。 快速排序算法 0 (nlogn) 实际工作中要根据实际情况选择最优解。 有可能没有完美方案&#xff0c;需要做平衡。 数据有啥特征&#xff1f; 举例&#xff1a;对一组数据进行排序 是否包含大量重复元素&#xff08;三路快排&#x…

【微服务治理之监控APM】系统监控架构概述

APM 简介 APM 通常认为是 Application Performance Management 的简写&#xff0c;它主要有三个方面的内容&#xff0c;分别是 Logs(日志)、Traces(链路追踪)Metrics(报表统计)。 以后大家接触任何一个 APM 系统的时候&#xff0c;都可以从这三个方面去分析它到底是什么样的…

vue中scoped原理与样式穿透原理

是什么 在vue组件中的style标签上有一个特殊属性&#xff1a;”scoped“ 为什么 当一个style标签拥有scoped属性时&#xff0c;它的CSS样式就只能作用于当前的组件&#xff0c;通过该属性&#xff0c;可以使得组件之间的样式不互相污染。 怎么样实现的 1.为组件实例生成一个唯一…

[附源码]SSM计算机毕业设计智能视频推荐网站JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

codeforces:F. All Possible Digits【贪心 + 模拟进位】

目录题目截图题目分析ac code总结题目截图 题目分析 注意是只能再最后一位加我们要使得0到p - 1都出现至少一次统计出现的数字aset考虑最后一位pivot情况1&#xff1a;如果pivot前都出现了&#xff0c;就不用进位了&#xff0c;pivot只需要加到最大的未出现在aset的num即可情况…

【编程题】【Scratch二级】2022.09 小老鼠偷面包

小老鼠偷面包 1. 准备工作 &#xff08;1&#xff09;背景&#xff1a;Stars&#xff0c;绘制如下图所示的迷宫&#xff1b; &#xff08;2&#xff09;角色&#xff1a;Cat 2、Mouse1、Bread。 2. 功能实现 &#xff08;1&#xff09;Cat 2、Mouse1和Bread初始位置和方向如…

机器人C++库(12) Robotics Library 之路径规划算法:PRM、RRT、EET算法

机器人C库&#xff08;12&#xff09;Robotics Library 之路径规划算法&#xff1a;PRM、RRT、EET算法 RL库的运动规划(rl::plan)模块集成了以下经典的路径规划算法&#xff1a; PRM算法:概率路线图算法RRT算法&#xff1a;快速探索随机树算法EET算法&#xff1a;搜索树算法-基…

牧场系统设计与实现-计算机毕业设计源码+LW文档

关键代码 package com.example.controller; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.huto…

japonensisjava乱码_Java乱码问题原因及解决方案

一. java乱码乱码问题解决思路,java乱码分为以下几种 1. 请求乱码 如果是在请求过程中发生的乱码,有可能是以下几种情况: 参数放在 URL 地址中乱码; 参数在请求体中乱码; 数据库本身乱码; 数据从 Java 应用程序传入数据库的过程中乱码。 2. 响应乱码 如果是响应乱码,那…

Go语言学习(八)-- Gin入门

Gin 是一个 Go (Golang) 编写的轻量级 http web 框架&#xff0c;运行速度非常快。Gin 最擅长的就是 Api 接口的高并发&#xff0c;如果项目的规模不大&#xff0c;业务相对简单&#xff0c;这个时候我们 也推荐您使用 Gin。 当某个接口的性能遭到较大挑战的时候&#xff0c;这…

Prometheus监控案例

一&#xff1a;环境规划&#xff1a; 主机名主机地址角色node4192.168.188.114prometheus客户端node5192.168.188.115prometheus服务端 二. 监控远程linux主机&#xff1a; 1. 解压node_exporter压缩包&#xff1a; [rootnode4 ~]# tar xf node_exporter-1.3.1.linux-amd64…

RT-Thread 下的文件内容对比 MSH shell cmd 命令实现方法

前言 在使用 RT-Thread 时&#xff0c;需要对两个文件的内容进行比较&#xff0c;顺手写了一个测试的 MSH shell 命令&#xff0c;经过优化&#xff0c;发现功能可以使用 RT-Thread 下支持多种文件系统&#xff0c;如FAT等&#xff0c;可以通过 USB、串口 的 Ymodem 等协议把文…

YOLOv5-seg数据集制作、模型训练以及TensorRT部署

YOLOv5-seg数据集制作、模型训练以及TensorRT部署版本声明一、数据集制作&#xff1a;图像 Json转txt二、分割模型训练三 tensorRT部署版本声明 yolov5-seg:官方地址&#xff1a;https://github.com/ultralytics/yolov5/tree/v6.2 TensorRT&#xff1a;8.x.x 语言&#xff1a;…

基于stm32单片机体重秤电子秤超重提醒

资料编号&#xff1a;107 下面是相关功能视频演示&#xff1a; 107-基于stm32单片机体重秤电子秤称重超重报警Proteus仿真&#xff08;源码仿真全套资料&#xff09;功能介绍&#xff1a; 采用stm32单片机&#xff0c;可以设置称重上限制&#xff0c;LCD1602显示重量&#xf…

04_tcp

知识点1【多播】 多播地址&#xff1a; 多播地址向以太网MAC地址的映射 UDP多播工作过程&#xff1a; 多播地址结构体&#xff1a; 多播套接口选项&#xff1a; 知识点2【TCP面向链接编程】 1、创建tcp套接字 2、做为客户端需要具备的条件 3、connect链接服务器的函数…

地图下载白嫖神器!你该怎么用好它

今天介绍一下做数据可视化网站比较好的两个平台。一个是阿里云的Datav&#xff0c;另一个是易智微easyv. 一、DataV.GeoAtlas 前段时间 我们就给大家分享过阿里云的DataV.GeoAtlas地理小工具系列。我们可以通过这个平台下载高德比较新的地图数据&#xff0c;数据的时效性是有较…

如何查看SAP版本及HANA版本?

目录 一、查SAP NetWeaver版本 二、查看S/4 HANA版本 在SAP运维及系统集成时&#xff0c;经常外面公司问及本公司的SAP版本及HANA版本。其实是每一个登录SAP的用户都可以查到的。方法如下&#xff1a; 一、查SAP NetWeaver版本 SAP界面上选择菜单&#xff1a;系统-状态&am…

哈夫曼树与哈夫曼编码

哈夫曼树&#xff1a;结点中赋予一个某种意义的值&#xff0c;称为结点的权值&#xff0c;从根结点开始&#xff0c;到目标结点经过的边数&#xff0c;称为路径长度&#xff0c;路径长度乘以权值&#xff0c;称为带权路径长度&#xff1b; 例如&#xff1a;根结点代表着快递集…