目录
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测试