SpringBoot+MyBatis-Plus+Redis+统一http返回+LomBook+log4j2+sl4j+自定义异常,环境搭建-------保姆教程

目录

SpringBoot项目初始搭建

1.快速构建SpringBoot项目:

2.结构介绍:

2.整合Mybatis-Plus:

3.LomBook插件

4.统一HTTP响应体

5.集成Redis

--------------------------------------------------更新-----------------------------------------------------------

5.集成log4j2+sl4j

6.自定义异常


SpringBoot项目初始搭建

SpringBoot+MyBatis-Plus+Redis+统一http返回+LomBook

1.快速构建SpringBoot项目:

1.打开idea
2.选择spring Initializr
3.构建包路径:
    3.1例:Group:com.baidu Artifact:model---即com.baidu.model
4.如有WEB需求,就选择web->spring web
  • 构建完成-->结构如图:

2.结构介绍:

1.pom.xml--->maven工程(jar包)管理配置文件
2.MyDemoApplication.java--->项目启动类
3.static--->静态文件存放地址
4.templates--->前端页面编写地址
5.application.properties--->配置文件

注释:application.properties默认为空,且端口号默认8080,可以加配置任意更改

2.整合Mybatis-Plus:

1.添加依赖:
 <!-- JDBC驱动包-->
 <dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <scope>runtime</scope>
 </dependency>
 <!-- lombok POJO注解-->
 <dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <optional>true</optional>
 </dependency>
 <!-- mybatis plus 代码生成器 -->
 <dependency>
 <groupId>com.baomidou</groupId>
 <artifactId>mybatis-plus-boot-starter</artifactId>
 <version>3.2.0</version>
 </dependency>
 <!-- 代码生成-->
 <dependency>
 <groupId>com.baomidou</groupId>
 <artifactId>mybatis-plus-generator</artifactId>
 <version>3.2.0</version>
 </dependency>
 <!-- 代码模板-->
 <dependency>
 <groupId>org.freemarker</groupId>
 <artifactId>freemarker</artifactId>
 <version>2.3.28</version>
 </dependency>
 <!-- json库-->
 <dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.47</version>
 </dependency>
2.添加配置
    2.1 yml配置
server:
  #服务端口
  port: 8001
  servlet:
    #默认访问
    context-path: /

spring:
  #数据源
  datasource:
    #数据源驱动
    driver-class-name: com.mysql.cj.jdbc.Driver
    #数据库连接
    url: jdbc:mysql://127.0.0.1:3306/mydemo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
    #数据库账号
    username: root
    #数据库密码
    password: 123456
  jackson:
    #全局设置@JsonFormat的格式pattern
    date-format: yyyy-MM-dd HH:mm:ss
    # 设置全局时区
    time-zone: GMT+8
    serialization:
      # 不返回java.util.date转换成timestamp
      write-dates-as-timestamps: false

mybatis-plus:
  configuration:
    # 开启驼峰命名
    map-underscore-to-camel-case: true
    # 代码生成自动映射表结构
    auto-mapping-behavior: full
    # 日志工厂
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # mapper文件位置
  #mapper-locations: classpath*:mapper/**/*Mapper.xml
  global-config:
    # 逻辑删除配置
    db-config:
      # 删除前
      logic-not-delete-value: 1
      # 删除后
      logic-delete-value: 0

    2.2 config 配置文件
package com.mylife.lifemodel.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * mybatis-plus 分页插件配置
 * */
@Configuration
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}
3.代码自生成
package com.mylife.lifemodel.config;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Scanner;

/**
 * 自动生成mybatisplus的相关代码
 */
public class GeneratorCodeConfig {
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("astupidcoder");
        gc.setOpen(false);
        //实体属性 Swagger2 注解
        gc.setSwagger2(false);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://127.0.0.1:3306/mydemo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
//        pc.setModuleName(scanner("模块名"));
        pc.setParent("com.mylife.lifemodel");
        pc.setEntity("model.domain");
        pc.setMapper("service.mapper");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        mpg.setPackageInfo(pc);

        // 自定义配置
//        InjectionConfig cfg = new InjectionConfig() {
//            @Override
//            public void initMap() {
//                // to do nothing
//            }
//        };

        // 如果模板引擎是 freemarker
//        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定义输出配置
//        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
//        focList.add(new FileOutConfig(templatePath) {
//            @Override
//            public String outputFile(TableInfo tableInfo) {
//                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
//                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
//                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
//            }
//        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判断自定义文件夹是否需要创建
                checkDir("调用默认方法创建的目录");
                return false;
            }
        });
        */
//        cfg.setFileOutConfigList(focList);
//        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定义输出模板
        //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setSuperEntityClass("com.baomidou.mybatisplus.extension.activerecord.Model");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);

        strategy.setEntityLombokModel(true);
        // 公共父类
//        strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
        // 写于父类中的公共字段
//        strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}
4.启动GeneratorCodeConfig,并测试生成
5.LambdaQueryWrapper常见用法

3.LomBook插件

1.pom.xml
    <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
2.lombook常用注解
 /**注解在类上,相当于同时使用了
 @Setter
 @Getter
 @EqualsAndHashCode
 @NoArgsConstructor
 @ToString,对于POJO类十分有用*/
 @Data 
 
 /**注解在属性/方法参数上,
 如果方法内对该参数进行是否为空的校验,
 如果为null值,
 则抛出NPE(NullPointerException)*/
 @NonNull
 
 /**自动管理资源,
 用在局部变量之前,
 在当前变量范围内即将执行完毕退出之前会自动清理资源,
 自动生成try-finally这样的代码来关闭流*/
 @Cleanup
 
 /**注解在属性上,
 自动生成生成setter/getter方法,
 final变量不包含,
 还可以指定访问范围*/
 @Getter
 @Setter
 
 /**注解在类上,
 可以自动覆写toString方法,
 当然还可以加其他参数,
 例如@ToString(exclude=”id”)排除id属性,或者
 @ToString(callSuper=true, includeFieldNames=true)
 调用父类的toString方法,包含所有属性*/
 @ToString
 
 /**注解在类上,自动生成equals()方法和hashCode方法*/
 @EqualsAndHashCode
 
 /**注解在类上,自动生成空参构造方法*/
 @NoArgsConstructor
 
 /**注解在类上,自动生成全部参数构造方法*/
 @AllArgsConstructor
 
 /**注解在类上,
 将标记为@NoNull的属性自动生成构造方法
 (如果运行中标记为@NoNull的属性为null,会抛出空指针异常)*/
 @RequiredArgsConstructor
 
 /**注解在类上,
 相当于同时使用了
 @ToString
 @EqualsAndHashCode
 @Getter
 @Setter
 @RequiredArgsConstrutor
 这些注解,对于POJO类十分有用*/
 @Data
 
 /**注解在类上,
 是@Data的不可变形式,
 两个主要区别就是如果变量不加@NonFinal ,
 @Value会给所有的弄成final的。
 当然如果是final的话,
 就没有set方法了。
 用于注解final类*/
 @Value
 
 /**用在类、构造器、方法上,
 为你提供复杂的builder APIs,
 让你可以像如下方式一样调用
 Person.builder()
       .name("name")
       .city("shanghai")
       .build();
 更多说明参考Builder*/
 @Builder
 
 /**自动抛受检异常,
 而无需显式在方法上使用throws语句。
 自动调用close方法关闭资源。*/
 @SneakyThrows
 
 /**用在方法上,
 将方法声明为同步的,
 并自动加锁,
 而锁对象是一个私有的属性$lock或$LOCK,
 而java中的synchronized关键字锁对象是this,
 锁在this或者自己的类对象上存在副作用,
 就是你不能阻止非受控代码去锁this或者类对象,
 这可能会导致竞争条件或者其它线程错误*/
 @Synchronized
 
 /**可以替代经典的Double Check Lock样板代码*/
 @Getter(lazy=true)
 
 /**注解在类上,
 根据不同的注解生成不同类型的log对象,
 但是实例名称都是log,有六种可选实现类*/
 @Log
 
 @CommonsLog 
 Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
 
 @Log 
 Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
 
 @Log4j 
 Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
 
 @Log4j2 
 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
 
 @Slf4j 
 Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
 
 @XSlf4j 
 Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

4.统一HTTP响应体

1.定义响应参数-实体类
2.定义响应枚举

5.集成Redis

1.pom.xml
<!--redis启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--进行redisTemplate配置时需要此包-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
2.Redis配置及configer
    2.1 yml
redis:
    host: localhost # Redis服务器地址
    database: 6 # Redis数据库索引(默认为0)
    port: 6379 # Redis服务器连接端口
    password: # Redis服务器连接密码(默认为空)
    jedis:
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 8 # 连接池中的最大空闲连接
        min-idle: 0 # 连接池中的最小空闲连接
    timeout: 3000ms # 连接超时时间(毫秒)
  cache:
    # spring cache 缓存类型为redis  也可以是其他的实现
    type: redis
    2.2 config
package com.mylife.lifemodel.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;

/**
 * Redis的核心配置类,这个类提供了两个方法(其实提供了两个bean,这两个bean会加载到spring的context里边,供使用者进行调用)
 */
@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);

        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);

        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);

        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }
}
3.RedisUtils工具类
package com.mylife.lifemodel.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 */
@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 写入缓存
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 写入缓存设置时效时间
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 批量删除对应的value
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 删除对应的value
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }
    /**
     * 判断缓存中是否有对应的value
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }
    /**
     * 读取缓存
     * @param key
     * @return
     */
    public <T> T get(final String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        if (null == result) {
            return null;
        }
        return (T)result;
    }
    /**
     * 哈希 添加
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmSet(String key, Object hashKey, Object value) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key,hashKey,value);
    }

    /**
     * 哈希获取数据
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.get(key,hashKey);
    }

    /**
     * 列表添加
     * @param k
     * @param v
     */
    public void lPush(String k, Object v) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(k,v);
    }

    /**
     * 列表获取
     * @param k
     * @param l
     * @param l1
     * @return
     */
    public List<Object> lRange(String k, long l, long l1) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k,l,l1);
    }

    /**
     * 集合添加
     * @param key
     * @param value
     */
    public void add(String key, Object value) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key,value);
    }

    /**
     * 集合获取
     * @param key
     * @return
     */
    public Set<Object> setMembers(String key) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合添加
     * @param key
     * @param value
     * @param scoure
     */
    public void zAdd(String key, Object value, double scoure) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key,value,scoure);
    }

    /**
     * 有序集合获取
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        return zset.rangeByScore(key, scoure, scoure1);
    }

    /**
     * redis原子型自增
     * */
    public Long incr(String key){
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
        Long increment = entityIdCounter.getAndIncrement();
        return increment;
    }
}
4.编写测试
package com.mylife.lifemodel.model;

import com.mylife.lifemodel.utils.RedisUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
* RedisUtils 测试
  */
  @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
  public class Test01 {
  @Autowired
  private RedisUtils redisUtils;

  @Test
  public void redisConnectTest(){
  String testKey = "test";
  //        Long time = (long)10;
  //        redisUtils.set(testKey, "***Test01",time);
  //        redisUtils.remove(testKey);
  String result = redisUtils.get(testKey);
  System.out.println("获取redis测试数据:" + result);
  }
  }
  • 展示: 

--------------------------------------------------更新-----------------------------------------------------------

5.集成log4j2+sl4j

1.pom.xml
<!--log4j2 + sl4j-->
        <!-- log4j2的api、core和web包 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.16.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.16.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-web</artifactId>
            <version>2.16.0</version>
        </dependency>
        <!-- slf4j与log4j2的连接包 注意,不可同时使用log4j-slf4j-impl和log4j-to-slf4j,
        否则会引发循环依赖。详情见log4j官方文档。-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.16.0</version>
        </dependency>
        <!-- slf4j本身的api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <!-- log4j与log4j2的连接包 -->
        <!--        <dependency>-->
        <!--            <groupId>org.apache.logging.log4j</groupId>-->
        <!--            <artifactId>log4j-1.2-api</artifactId>-->
        <!--            <version>2.14.0</version>-->
        <!--        </dependency>-->
        <!-- log4j2支撑完全异步模式的关键api -->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>
2.log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--1.全异步-->
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--status:用于设置log4j2自身内部日志的信息输出级别(所有日志级别筛选),默认是OFF。当设置成trace时,你会看到log4j2内部各种详细输出
   monitorinterval:用于指定log4j自动重新配置的监测间隔时间,自动检测配置文件的变更和重新配置本身,单位是s,最小是5s。 -->
<configuration status="error" monitorInterval="30">
    <Properties>
        <!--自定义一些常量,之后使用 ${变量名} 引用-->
        <Property name="baseLogDir">logs</Property>
        <Property name="pattern">%d{yyyyMMdd-HHmmss.SSS} [%level] %c{1} - %msg%n</Property>
    </Properties>
    <!-- appenders:定义输出内容,输出格式,输出方式,日志保存策略等,常用其下三种标签[console,File,RollingFile]-->
    <appenders>
        <!--Console节点用来定义输出到控制台的Appender。
        name:指定Appender的名字.
        target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT。
        PatternLayout:输出日志的格式,不设置默认为:%m%n。-->
        <Console name="Console" target="SYSTEM_OUT">
            <!--level="info" :自定义日志输出级别,
                onMatch="ACCEPT" :级别在info之上则接受,
                onMismatch="DENY" :级别在info之下则拒绝-->
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${pattern}"/>
        </Console>

        <!--异步日志配置1,LOG4J有三种日志模式,全异步日志,混合模式,同步日志,性能从高到底,线程越多效率越高,也可以避免日志卡死线程情况发生;
            采用了ArrayBlockingQueue来保存需要异步输出的日志事件.
            bufferSize: 队列中可存储的日志事件的最大数量,默认为128。
            blocking: 默认为true。如果为true,appender将一直等待直到queue中有空闲;如果为false,当队列满的时候,日志事件将被丢弃。
            (如果配置了error appender,要丢弃的日志事件将由error appender处理)
            AppenderRef: 异步调用的Appender的名字,可以配置多个。
            -->
        <Async name="ASYNC" bufferSize="128" includeLocation="true">
            <AppenderRef ref="RollingFileWarn"/>
            <AppenderRef ref="RollingFileError"/>
        </Async>

        <!--File:节点用来定义同步输出到指定位置的文件的Appender。
            name:指定Appender的名字。
            fileName:指定输出日志的目的文件带全路径的文件名。
            PatternLayout:输出格式,不设置默认为:%m%n。
            文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
        <File name="test1" fileName="${baseLogDir}/test1.log" append="false">
            <PatternLayout pattern="${pattern}"/>
        </File>

        <!--日志文件配置,filePattern为日志文件名称的格式 ${sys:user.home} :项目路径-->
        <RollingFile name="RollingFile" fileName="${baseLogDir}/infoall.log"
                     filePattern="${baseLogDir}/infoall.log.%d{yyyy-MM-dd}">
            <!--日志内容格式-->
            <PatternLayout pattern="%d %5p %c:%L - %m %throwable{separator( --> )}%n"/>
            <!--依据时间创建新的日志文件:1d-->
            <TimeBasedTriggeringPolicy interval="1"/>
            <DefaultRolloverStrategy>
                <!-- 在轮转时,删除7天之前的,命名符合规则的文件 -->
                <Delete basePath="${baseLogDir}">
                    <IfFileName glob="infoall.log.*"/>
                    <IfLastModified age="7d"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>
        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileInfo" fileName="${baseLogDir}/info.log"
                     filePattern="${baseLogDir}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <!-- Policies :日志滚动策略-->
            <Policies>
                <!-- TimeBasedTriggeringPolicy :时间滚动策略,默认0点小时产生新的文件,
                     interval="6" : 自定义文件滚动时间间隔,每隔6小时产生新文件,
                     modulate="true" : 产生文件是否以0点偏移时间,即6点,12点,18点,0点-->
                <TimeBasedTriggeringPolicy interval="6" modulate="true"/>
                <!-- SizeBasedTriggeringPolicy :文件大小滚动策略-->
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <RollingFile name="RollingFileWarn" fileName="${baseLogDir}/warn.log"
                     filePattern="${baseLogDir}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>

        </RollingFile>
        <RollingFile name="RollingFileError" fileName="${baseLogDir}/error.log"
                     filePattern="${baseLogDir}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--    自定义的log输出级别    -->
        <!-- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 -->
        <root level="trace">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
        </root>
        <!--这里配置 过滤日志 用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
        <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <!--Logger节点用来单独指定日志的形式,name为包路径,比如要为org.springframework包下所有日志指定为INFO级别等。 -->
        <logger name="org.springframework" level="INFO"/>
        <logger name="org.mybatis" level="INFO"/>
        <logger name="org.hibernate.validator" level="ERROR"/>
        <!-- 异步日志配置2,使用了Disruptor框架来实现高吞吐
             additivity="false" : additivity设置事件是否在root logger输出,为了避免重复输出,可以在Logger 标签下设置additivity为”false”-->
        <AsyncLogger name="AsyncLogger" level="trace" includeLocation="true" additivity="false">
            <AppenderRef ref="RollingFile"/>
            <AppenderRef ref="RollingFileInfo"/>
        </AsyncLogger>
    </loggers>
</configuration>
        <!--%d{HH:mm:ss.SSS} 表示输出到毫秒的时间
            %t 输出当前线程名称
            %-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
            %logger 输出logger名称,因为Root Logger没有名称,所以没有输出
            %msg 日志文本
            %n 换行
          其他常用的占位符有:
            %F 输出所在的类文件名,如Log4j2Test.java
            %L 输出行号
            %M 输出所在方法名
            %l 输出语句所在的行数, 包括类名、方法名、文件名、行数-->
        <!--2.日志分类-->
        <!-- monitorInterval配置成一个正整数,则每隔这么久的时间(秒),log4j2会刷新一次配置。如果不配置则不会动态刷新 -->
        <!--<Configuration status="INFO" monitorInterval="30">-->
        <!--<Properties>-->
        <!--     应用需要修改为合适的log路径 -->
        <!--    <Property name="baseLogDir">logs</Property>-->
        <!--    <Property name="pattern">%d{yyyyMMdd-HHmmss.SSS} [%level] %c{1} - %msg%n</Property>-->
        <!--</Properties>-->
        <!-- 先定义所有的appender -->
        <!--<Appenders>-->
        <!--     这个输出控制台的配置 -->
        <!--    <Console name="Console" target="SYSTEM_OUT">-->
        <!--        <PatternLayout>-->
        <!--            <Pattern>${pattern}</Pattern>-->
        <!--        </PatternLayout>-->
        <!--    </Console>-->
        <!--     系统日志,可以作为root logger的appender,供打印一些中间件的日志 -->
        <!--    <RollingRandomAccessFile name="SYS_APPENDER" fileName="${baseLogDir}/server.log"-->
        <!--                             filePattern="${baseLogDir}/server.log.%d{yyyyMMddHH}.%i.gz">-->
        <!--        <PatternLayout>-->
        <!--            <Pattern>${pattern}</Pattern>-->
        <!--        </PatternLayout>-->
        <!--        <Policies>-->
        <!--            <SizeBasedTriggeringPolicy size="200MB" />-->
        <!--            <TimeBasedTriggeringPolicy interval="1" modulate="true" />-->
        <!--        </Policies>-->
        <!--        <Filters>-->
        <!--            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />-->
        <!--        </Filters>-->
        <!--         max=6标识一小时内最多产生6个日志文件 -->
        <!--        <DefaultRolloverStrategy max="6">-->
        <!--            对于指定的路径下的指定后缀的文件,只保留1天的日志文件,那么最多会有24小时*6个日志文件 -->
        <!--            <Delete basePath="${baseLogDir}" maxDepth="1">-->
        <!--                <IfFileName glob="*.gz" />-->
        <!--                <IfLastModified age="1d" />-->
        <!--            </Delete>-->
        <!--        </DefaultRolloverStrategy>-->
        <!--    </RollingRandomAccessFile>-->
        <!--     应用info日志 -->
        <!--    <RollingRandomAccessFile name="APPINFO_APPENDER" fileName="${baseLogDir}/appinfo.log"-->
        <!--                             filePattern="${baseLogDir}/appinfo.log.%d{yyyyMMddHH}.%i.gz">-->
        <!--        <PatternLayout>-->
        <!--            <Pattern>${pattern}</Pattern>-->
        <!--        </PatternLayout>-->
        <!--        <Policies>-->
        <!--            <SizeBasedTriggeringPolicy size="500MB" />-->
        <!--            <TimeBasedTriggeringPolicy interval="1" modulate="true" />-->
        <!--        </Policies>-->
        <!--        <Filters>-->
        <!--          当前appender只打印info日志,warn及以上日志忽略,由后面的appender决定是否需要打印 -->
        <!--            <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL" />-->
        <!--            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />-->
        <!--        </Filters>-->
        <!--        max=20标识一小时内最多产生20个日志文件 -->
        <!--        <DefaultRolloverStrategy max="20">-->
        <!--            对于指定的路径下的指定后缀的文件,只保留3天的日志文件,那么最多会有3天*24小时*20个日志文件 -->
        <!--            注意应用需要根据业务需求和磁盘大小评估需要保留的日志个数,对于500M的日志文件来说,要根据应用日志的情况,观察单个日志压缩后文件大小,并计算总大小需要的空间 -->
        <!--            <Delete basePath="${baseLogDir}" maxDepth="1">-->
        <!--                <IfFileName glob="*.gz" />-->
        <!--                <IfLastModified age="3d" />-->
        <!--            </Delete>-->
        <!--        </DefaultRolloverStrategy>-->
        <!--    </RollingRandomAccessFile>-->
        <!--    应用错误日志 -->
        <!--    <RollingRandomAccessFile name="APPERROR_APPENDER" fileName="${baseLogDir}/apperror.log"-->
        <!--                             filePattern="${baseLogDir}/apperror.log.%d{yyyyMMddHH}.%i.gz">-->
        <!--        <PatternLayout>-->
        <!--            <Pattern>${pattern}</Pattern>-->
        <!--        </PatternLayout>-->
        <!--        <Policies>-->
        <!--            <SizeBasedTriggeringPolicy size="500MB" />-->
        <!--            <TimeBasedTriggeringPolicy interval="1" modulate="true" />-->
        <!--        </Policies>-->
        <!--        <Filters>-->
        <!--            <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY" />-->
        <!--        </Filters>-->
        <!--         max=10标识一小时内最多产生10个日志文件 -->
        <!--        <DefaultRolloverStrategy max="10">-->
        <!--            对于指定的路径下的指定后缀的文件,只保留3天的日志文件,那么最多会有3*24小时*10个日志文件 -->
        <!--            <Delete basePath="${baseLogDir}" maxDepth="1">-->
        <!--                <IfFileName glob="*.gz" />-->
        <!--                <IfLastModified age="3d" />-->
        <!--            </Delete>-->
        <!--        </DefaultRolloverStrategy>-->
        <!--    </RollingRandomAccessFile>-->
        <!--</Appenders>-->

        <!--<Loggers>-->
        <!--     root是默认的logger,也就是公共的logger,供记录一些不常打印的系统参数或者其他组件参数 -->
        <!--    <AsyncRoot level="WARN">-->
        <!--        <AppenderRef ref="Console" />-->
        <!--        <AppenderRef ref="SYS_APPENDER" />-->
        <!--    </AsyncRoot>-->
        <!--     常打印的应用日志,建议独立配置,并采用异步模式。name根据实际的包名修改,生产环境中additivity建议设置为false以避免在root logger中重复打印 -->
        <!--    <AsyncLogger name="com.unionpay" level="INFO" includeLocation="false" additivity="false">-->
        <!--        <AppenderRef ref="APPINFO_APPENDER" />-->
        <!--        <AppenderRef ref="APPERROR_APPENDER" />-->
        <!--    </AsyncLogger>-->
        <!--</Loggers>-->
        <!--</Configuration>-->
3.测试
package com.mylife.lifemodel;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class log4j2Test {

    private static Logger logger= LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

    @Test
    public void log4j2Test() {
        for(int i=0;i<3;i++){
            // 记录trace级别的信息
            logger.trace("log4j2日志输出:This is trace message.");
            // 记录debug级别的信息
            logger.debug("log4j2日志输出:This is debug message.");
            // 记录info级别的信息
            logger.info("log4j2日志输出:This is info message.");
            // 记录error级别的信息
            logger.error("log4j2日志输出:This is error message.");
        }
    }
}
输出:

6.自定义异常

1.错误枚举类
package com.mylife.lifemodel.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
* 自定义异常枚举
  */
  @Getter
  @AllArgsConstructor
  public enum ErrorType {
  /**
    * 错误类型
      */
      OBJECT_NOT_FOUND(0,"对象不存在"),

  INVALID_PARAMS(1,"参数不正确"),

  result_not_exist(2,"记录不存在")

           ;

  /**
    * 错误码
      */
      private int code;

  /**
    * 提示信息
      */
      private String msg;
      }
2.自定义异常
package com.mylife.lifemodel.api;

import com.mylife.lifemodel.enums.ErrorType;
import lombok.Getter;
/**
* 自定义异常类
* */
  @Getter
  public class MyLifeException extends RuntimeException{
  private Integer code;

  /**
    * 使用已有的错误类型
    * @param type 枚举类中的错误类型
      */
      public MyLifeException(ErrorType type){
      super(type.getMsg());
      this.code = type.getCode();
      }

  /**
    * 自定义错误类型
    * @param code 自定义的错误码
    * @param msg 自定义的错误提示
      */
      public MyLifeException(Integer code, String msg){
      super(msg);
      this.code = code;
      }
      }
3.响应参数--一般除了我代码里贴出来的
还有success状态码,成功或失败
响应时间,分页等,我这里从简了
其实就算不写也行,返回的比较难看而已
package com.mylife.lifemodel.api;

import lombok.Data;

/**
 * 响应
 * */
@Data
public class Response<T> {
    /**
     * 状态码
     */
    private Integer code;

    /**
     * 请求成功时返回的对象
     */
    private T data;

    /**
     * 提示信息
     */
    private String msg;

}
4.工具类
package com.mylife.lifemodel.api;

/**
 * 工具类
 * */
public class MyLifeUtils {
    /**
     * 调用成功
     */
    private static final String SUCCESS = "调用成功!";

    public static Response success(Object obj){
        Response res = new Response();
        res.setCode(200);
        res.setData(obj);
        res.setMsg(SUCCESS);
        return res;
    }

    public static Response success(){
        return success(null);
    }

    public static Response error(Integer code, String msg){
        Response res = new Response();
        res.setCode(code);
        res.setMsg(msg);
        return res;
    }

}
5.异常处理类
package com.mylife.lifemodel.api;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* 异常处理
* */
  @ControllerAdvice
  public class ServiceExceptionHandler {
  /**
    * @ExceptionHandler相当于controller的@RequestMapping
    * 如果抛出的的是ServiceException,则调用该方法
    * @param me 业务异常
    * @return
      */
      @ExceptionHandler(MyLifeException.class)
      @ResponseBody
      public Response handle(MyLifeException me){
      return MyLifeUtils.error(me.getCode(),me.getMessage());
      }
      }
  • 测试
1.main方法测试

2.@SpringBootTest测试

3.Controller测试

以上就是springBoot项目完整搭建的过程

上面是最基础的也是最简化的模拟企业级开发搭建的框架

实际开发中比这搭建的要更加底层,更加复杂得多

而且有很多关键的分布式技术我都没有进行搭建

例如:zookeeper分布式协调服务,RabbitMQ消息列队,MongDB海量存储

这些技术只有用户基数大,数据体量巨大的时候才能用到

一般中小型企业,考虑到开发成本维护成本等,不会选择

当然有志向进大厂,拿高薪的小伙伴可以自行摸索评论区探讨

---------------------------------------测试------------------------------------------------------------------------------------

今天更新下项目的测试

按照企业级开发环境

我做了个分包

 

接下来开始测试

1.创建user表

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `name` varchar(255) DEFAULT '用户' COMMENT '用户名称',
  `age` int(11) DEFAULT NULL COMMENT '用户年龄',
  `avatar` varchar(255) DEFAULT NULL COMMENT '头像',
  `email` varchar(30) CHARACTER SET utf8 DEFAULT NULL COMMENT '邮箱',
  `singn` varchar(255) DEFAULT '这个人很懒什么也没留下~' COMMENT '个性签名',
  `username` varchar(15) CHARACTER SET utf8 DEFAULT NULL COMMENT '账号',
  `password` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '密码',
  `real_name` varchar(10) CHARACTER SET utf8 DEFAULT NULL COMMENT '真实姓名',
  `birthday` varchar(10) CHARACTER SET utf8 DEFAULT NULL COMMENT '生日',
  `user_address` varchar(10) CHARACTER SET utf8 DEFAULT NULL COMMENT '所在地',
  `phone` varchar(11) CHARACTER SET utf8 DEFAULT NULL COMMENT '手机号',
  `ip` varchar(15) CHARACTER SET utf8 DEFAULT NULL COMMENT '注册ip',
  `status` tinyint(1) DEFAULT '0' COMMENT '状态0,正常;1,禁用',
  `level` tinyint(1) DEFAULT '0' COMMENT '等级0,1,2,3,4,5,6',
  `is_del` tinyint(1) DEFAULT '0' COMMENT '是否删除',
  `create_time` datetime DEFAULT NULL COMMENT '注册时间',
  `update_time` datetime DEFAULT NULL COMMENT '最后一次操作时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

2.创建user实体

package com.mylife.lifemodel.model.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;


@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("user")
public class User extends Model<User> {

    private static final long serialVersionUID = 1L;

    /**
     * 用户id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 用户名称
     */
    private String name;

    /**
     * 用户年龄
     */
    private Integer age;

    /**
     * 头像
     */
    private String avatar;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 个性签名
     */
    private String singn;

    /**
     * 账号
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 真实姓名
     */
    private String realName;

    /**
     * 生日
     */
    private String birthday;

    /**
     * 所在地
     */
    private String userAddress;

    /**
     * 手机号
     */
    private String phone;

    /**
     * 注册ip
     */
    private String ip;

    /**
     * 状态0,正常;1,禁用
     */
    private Boolean status;

    /**
     * 等级0,1,2,3,4,5,6
     */
    private Boolean level;

    /**
     * 是否删除 0为不删除 1为删除
     */
    private Boolean isDel;

    /**
     * 注册时间
     */
    private LocalDateTime createTime;

    /**
     * 最后一次操作时间
     */
    private LocalDateTime updateTime;


}

3.service

package com.mylife.lifemodel.model.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.mylife.lifemodel.model.domain.User;

/**
 *  服务类
 */
public interface IUserService extends IService<User> {

}

4.impl

package com.mylife.lifemodel.model.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mylife.lifemodel.model.domain.User;
import com.mylife.lifemodel.model.service.IUserService;
import com.mylife.lifemodel.model.service.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 *  服务实现类
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

}

4.mapper

package com.mylife.lifemodel.model.service.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mylife.lifemodel.model.domain.User;
import org.springframework.stereotype.Repository;

/**
 *  Mapper 接口
 */
@Repository
public interface UserMapper extends BaseMapper<User> {

}

5.controller

package com.mylife.lifemodel.rest;


import com.mylife.lifemodel.api.ApiResult;
import com.mylife.lifemodel.api.MyLifeException;
import com.mylife.lifemodel.model.domain.User;
import com.mylife.lifemodel.model.service.IUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 *  前端控制器
 */
@Slf4j
@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserController {

    private final IUserService userService;

    /**
     * 查询用户
     */
    @GetMapping("/getUser")
    public ApiResult<User> getUserById(@RequestParam(value = "id",defaultValue = "-1") int id) {
        if (id == -1){
            throw new MyLifeException(500,"请输入用户id");
        }
        return ApiResult.success(userService.getById(id));
    }
}

6.postman测试

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值