1、Slf4j+logback 日志
SpringBoot框架的默认日志实现:slf4j + logback;
默认日志级别:info,对应了实际生产环境日志级别;
1.1 日志级别
# 常见的日志框架中,日志级别都包含五种,优先级从低到高:
trace < debug < info < warn < error
#日志输出规则是:
自动设置的日志级及更高级别,进行日志输出;
比如当前日志级别是info,那么日志输出的级别包含:info,warn,error,凡是业务的这三个级别,都会输出到日志文件
1.1.1 trace 日志级别
trace 日志级别,了解,实际开发中,几乎不会使用此级别;(配置文件指定为 trace 级别才会输出);
logger.trace("------------ trace 日志级别,了解,实际开发中,几乎不会使用此级别 ------------------");
1.1.2 debug 日志级别
- debug 日志级别,掌握,实际开发中,此日志级别作为调试日志使用,是线上调试问题定位的有效手段;
- 强调:线上一般默认是不开debug日志,因此日志级别记录的日志极为详情,会产生大量的日志内容及文件;
- 发现线上问题,不好定位时,临时开启debug;
logger.debug("------------ {} 日志级别,{},实际开发中,此日志级别作为调试日志使用,是线上调试问题定位的有效手段 ------------------","debug","掌握");
1.1.3 info 日志级别
info 日志级别,掌握,实际开发中,此日志级别是核心业务环境日志,不需要记录特别详细,一般都是接口 入和出,方便版本上线查看业务是否正常使用;
logger.info("------------ {} 日志级别,{},实际开发中,此日志级别时核心业务环境日志,不需要记录特别详细 ------------","info","掌握");
1.1.4 warn 日志级别
warn 日志级别,掌握,实际开发中,此日志级别是业务警告日志,警告日志不一定是错误,可能业务异常流程,或者数据错误判断;
logger.warn("------------ {} 日志级别,{},实际开发中,此日志级别时业务警告日志 ------------","warn","掌握");
1.1.5 error 日志级别
error 日志级别,掌握,实际开发中,此日志级别是核心业务错误,凡是系统中出现了异常或者程序错误,都必须使用error日志,级别最高,确保必须输出,可以有效的记录线上业务的错误;
logger.error("------------ {} 日志级别,{},实际开发中,此日志级别是核心业务错误 ------------","error","掌握");
1.2 日志使用
1.2.1 创建日志对象
//创建日志对象
Logger logger = LoggerFactory.getLogger(getClass());
logger.trace("");
logger.debug("");
logger.info("");
logger.warn("");
logger.error("");
1.2.2 @Slf4j 注解
@Slf4j
public class ChargeResultNotifySchedule{
log.trace("");
log.debug("");
log.info("");
log.warn("");
log.error("");
}
1.3 配置文件配置日志 信息
1.3.1 测试默认日志级别
1.3.1.1 直接运行测试类

1.3.1.2 测试类增加 @RunWith(SpringRunner.class) 注解
测试类增加 @RunWith(SpringRunner.class) 注解 运行的是SpringBoot项目测试,会读取到配置文件;

1.3.2 修改配置文件信息
1.3.2.1 修改默认日志级别为 debug
# 默认日志级别为 info ,更改默认日志级别debug
logging:
level:
com:
kgc:
sbt: debug

1.4 指定日志输出
1.4.1 指定日志输出到指定文件
- 默认日志只输出到控制台;
- 指定输出到指定文件,默认会加载到根路径下;
- 所有的日志,都是追加记录,不会执行覆盖;
logging:
file: kh96-logging.log
日志输出到根目录下的指定文件名下:

1.4.2 指定日志输入到指定目录下
不指定文件名,SpringBoot中的logback会由默认的日志名spring.log;
logging:
path: D:/KEGONGCHANG/DaiMa/IDEA/KH96/SpringBoot/SpringBoot/TempFile/kh96-logging2
默认名:

输出文件:

1.4.3 指定输出格式
# 了解 %d日期,%thread 线程名称,%-5leavel 日志级别 %logger{50} 日志类路径 %msg 日志内容
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} +++ [%thread] +++ %-5level +++ %logger{100} +++ %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} +++ [%thread] +++ %-5level +++ %logger{100} +++ %msg%n

1.4.4 指定输入格式文件
网上由很多;
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{60} - %msg%n
${LOG_HOME}/${appName}.log
${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log
30
30MB
%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{60} : %line ] - %msg%n
2、异步请求
2.1 异步请求处理实现类
@Service
@Slf4j
public class ChargeServiceImpl implements ChargeService{
@Override
@Async
//开启异步线程注解,如果是同一个类中的其他方法,添加此异步处理注解,异步是不生效的(不能再同一个类中调用异步方法,解决方法,异步方法单独放在一个类中)
//使用的是Spring内置的线程池
public void executesAsynCharge(String chargeTel, Double chargeMoney) {
try {
TimeUnit.MILLISECONDS.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
log.info("********* 异步渠道 充值成功,充值手机号:{},充值金额:{} ************",chargeTel,chargeMoney);
}
}
2.2 主启动类
主启动类上必须增加@EnableAsync 注解,开启异步处理功能;
@SpringBootApplication
@EnableAsync //开启异步处理功能
public class Springboot03AsyztimerApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot03AsyzyimerApplication.class, args);
}
}
2.3 请求调用异步处理方法
@RestController
@Slf4j
public class ChargeController {
@Autowired
ChargeService chargeService;
@GetMapping("/asyncCharge")
public String asyncCharge(@RequestParam("chargeTel") String chargeTel,@RequestParam("chargeMoney") Double chargeMoney){
log.info("------ 开始充值,充值手机号:{},充值金额:{} 开始调用充值渠道充值 --------- ",chargeTel,chargeMoney);
// log startTimeMillis = System.currentTimeMillis(); //旧的获取当前时间毫秒数
long startTimeMillis = Instant.now().toEpochMilli();
//调用充值渠道 异步 充值
chargeService.executesAsynCharge(chargeTel,chargeMoney);
long finishTimeMillis = Instant.now().toEpochMilli();
log.info("------ 结束充值,充值手机号:{},充值金额:{} ,充值总耗时:{}-----------",chargeTel,chargeMoney,finishTimeMillis-startTimeMillis);
return String.format("%s充值%s成功!",chargeTel,chargeMoney);
}
}
3、定时任务
3.1 场景
异步充值结果,定时批量回调订单;
public void chargeResultNotifyMethodOne(){
//模拟从数据库获取5笔需要回调的充值订单,进行批量回调结果
List chargeOrderList = Arrays.asList("KH001","KH002","KH003","KH004","KH005");
//循环处理需要回调的5笔订单
log.info("---------- 开始 执行批量回调充值结果------------");
chargeOrderList.forEach(chargeOrderNo->{
log.info("***** 充值订单:{},回调重接成功! ******");
try{
TimeUnit.MILLISECONDS.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
log.info("---------- 结束 执行批量回调充值结果 ------------");
});
}
3.2 注解
3.2.1 @Scheduled(fixedDelay = 5 * 1000)
fixedDelay :计时规则: 从 上一次执行结束 开始计时 到 下一次定时任务开始 , 不关心前一次定时任务耗时多久;
3.2.2 @Scheduled(fixedRate = 5 * 1000)
fixedRate: 计时规则:从 上一次定时任务执行开始 开始计时 到 下一次定时任务开始,如果 上一次定时任务 超过定时,上一次 执行完后,下一次 立即执行;
3.2.3 @Scheduled(cron = “/5 * * * ?”)
cron表达式:既可以是实现循环时间间隔,执行定时任务,也可以执行某个时刻的定时任务,通过指定表达式实现的,灵活度是三种最高的
1)循环定时: 每次执行的定时任务时间点,是由cron表达是决定的,其实都是预置好的;比如5秒,5秒执行一次定时
2)定点定时:在指定的某个时刻,执行一次定时任务
秒 分 时 日 月 周
@Scheduled(cron = "*/5 * * * * ?")
3.2.4 @EnableScheduling
@SpringBootApplication
@EnableScheduling //开启定时任务功能
public class Springboot03AsyztimerApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot03AsyzyimerApplication.class, args);
}
}
Original: https://www.cnblogs.com/xiaoqigui/p/16790630.html
Author: 化羽羽
Title: SpringBoot(三) – Slf4j+logback 日志,异步请求,定时任务
相关阅读
Title: JAVA中的注解可以继承吗?
前言
注解想必大家都用过,也叫元数据,是一种代码级别的注释,可以对类或者方法等元素做标记说明,比如Spring框架中的 @Service
, @Component
等。那么今天我想问大家的是类被继承了,注解能否继承呢?可能会和大家想的不一样,感兴趣的可以往下看。
简单注解继承演示
我们不妨来验证下注解的继承。
- 自定义一个注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String value();
}
-
注解可以被标记在类或者方法上
-
使用自定义注解
@TestAnnotation(value = "Class")
static class Parent {
@TestAnnotation(value = "Method")
public void method() {
}
}
static class Child extends Parent {
@Override
public void method() {
}
}
Parent
类和里面的方法使用了注解-
Child
类继承了Parent类, 重写了父类的方法 -
验证是否存在注解
public static void main(String[] args) throws NoSuchMethodException {
Parent parent = new Parent();
log.info("ParentClass: {}", getAnnoValue(parent.getClass().getAnnotation(TestAnnotation.class)));
log.info("ParentMethod: {}", getAnnoValue(parent.getClass().getMethod("method").getAnnotation(TestAnnotation.class)));
Child child = new Child();
log.info("ChildClass: {}", getAnnoValue(child.getClass().getAnnotation(TestAnnotation.class)));
log.info("ChildMethod: {}", getAnnoValue(child.getClass().getMethod("method").getAnnotation(TestAnnotation.class)));
}
private static String getAnnoValue(TestAnnotation annotation) {
if(annotation == null) {
return "未找到注解";
}
return annotation.value();
}
输出结果如下:

可以看到,父类的类和方法上的注解都可以正确获得,但是子类的类和方法却不能。这说明, 默认情况下,子类以及子类的方法,无法自动继承父类和父类方法上的注解。
使用@Inherited演示
查了网上资料以后,在注解上标记 @Inherited
元注解可以实现注解的继承。那么,把 @TestAnnotation
注解标记了@Inherited,就可以一键解决问题了吗?

重新运行,得到结果如下:

可以看到,子类可以获得父类类上的注解;子类方法虽然是重写父类方法,并且注解本身也支持继承,但还是无法获得方法上的注解。
如何重写方法继承注解?
实际上, @Inherited
只能实现类上的注解继承。要想实现方法上注解的继承,你可以通过反射在继承链上找到方法上的注解。是不是听起来很麻烦,好在Spring框架中提供了 AnnotatedElementUtils
类,来方便我们处理注解的继承问题。
调用 AnnotatedElementUtils
的 findMergedAnnotation()
方法,可以帮助我们找出父类和接口、父类方法和接口方法上的注解,实现一键找到继承链的注解:

输出结果如下图:

总结
自定义注解可以通过标记元注解 @Inherited
实现注解的继承,不过这只适用于类。如果要继承定义在接口或方法上的注解,可以使用Spring的工具类 AnnotatedElementUtils
。
如果本文对你有帮助的话,请留下一个赞吧
欢迎关注个人公众号——JAVA旭阳
更多学习资料请移步:程序员成神之路Original: https://www.cnblogs.com/alvinscript/p/16979274.html
Author: JAVA旭阳
Title: JAVA中的注解可以继承吗?
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/363525/
转载文章受原作者版权保护。转载请注明原作者出处!