Spring Boot分布式系统实践【基础模块构建3.3】注解轻松实现操作日志记录
日志注解
前言
spring切面的编程,spring中事物处理、日志记录常常与pointcut相结合
Pointcut 是指那些方法需要被执行"AOP",是由"Pointcut Expression"来描述的.
Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合.
Spring AOP支持的AspectJ切入点指示符如下:
- execution:用于匹配方法执行的连接点;
- within:用于匹配指定类型内的方法执行;
- this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
- target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
- args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
- @within:用于匹配所以持有指定注解类型内的方法;
- @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
- @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
- @annotation:用于匹配当前执行方法持有指定注解的方法;
- bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
- reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
AspectJ切入点支持的切入点指示符还有: call、get、set、preinitialization、staticinitialization、initialization、handler、adviceexecution、withincode、cflow、cflowbelow、if、@this、@withincode;但Spring AOP目前不支持这些指示符,使用这些指示符将抛出IllegalArgumentException异常。这些指示符Spring AOP可能会在以后进行扩展。
AspectJ类型匹配的通配符:
*: 匹配任何数量字符
..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
示例:
- pointcutexp包里的任意类.
within(com.test.spring.aop.pointcutexp.*)
- pointcutexp包和所有子包里的任意类.
within(com.test.spring.aop.pointcutexp..*)
- 实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类.
this(com.test.spring.aop.pointcutexp.Intf)
当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型.
- 带有@Transactional标注的所有类的任意方法.
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)
- 带有@Transactional标注的任意方法.
@annotation(org.springframework.transaction.annotation.Transactional)
***> @within和@target针对类的注解,@annotation是针对方法的注解
- 参数带有@Transactional标注的方法.
@args(org.springframework.transaction.annotation.Transactional)
- 参数为String类型(运行是决定)的方法.
args(String)
其中 execution 是用的最多的,其格式为:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
`returning type pattern,name pattern, and parameters pattern是必须的.
ret-type-pattern:可以为*表示任何返回值,全路径的类名等.
name-pattern:指定方法名,代表所以,set,代表以set开头的所有方法.
parameters pattern:指定方法参数(声明的类型),(..)代表所有参数,()代表一个参数,(,String)代表第一个参数为任何值,第二个为String类型.
`
举例说明:
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service..(..))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service...(..))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
*> 最靠近(..)的为方法名,靠近.(..))的为类名或者接口名,如上例的JoinPointObjP2.(..))
注意上面两中方法的不同点出了 将 || 改成了 or ,还有就是 每个execution都被 ()包含起来,建议为了区分不同的表达式 最好都是用()包装。
@Aspect 需要 引入该bean 否则 spring将不识别。如上@Component或者xml引入
表达式中拦截符合条件的的方法,当执行行该方法时 执行相应的拦截方法,pointcut只负责 切入方法,并未执行方法体。
Aspect 几个通知注解(advice)
@Pointcut 拦截的切入点方法,注解的在方法级别之上,但是不执行方法体,只表示切入点的入口。
@Before 顾名思义 是在 切入点 之前执行 方法。
@AfterReturning 返回拦截方法的返回值
@AfterThrowing 拦截的方法 如果抛出异常 加执行此方法 throwing="ex" 将异常返回到参数列表
@After 在之上方法执行后执行结束操作
@Around 方法执行前后
自动日志记录实现
/**
* @Description: (系统日志注解)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
/**
* 日志简介
*/
String value() default "";
/**
* 日志类型
*/
String type() default "sys";
}
定义切面与切点
/**
* @Description: TODO(系统日志,切面处理类)
* @date 2019-4-3 15:07
*/
@Aspect
@Component
public class SysLogAspect {
@Reference(version = "1.0.0")
private ISysLogServiceFacade sysLogService;
private ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("SysLog-pool-%d").build();
private ExecutorService singleThreadPool = new ThreadPoolExecutor(5, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
@Pointcut("@annotation( com.halburt.site.sys.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = null ;
Throwable ex = null;
try {
result = point.proceed();
} catch (Throwable throwable) {
ex = throwable;
}
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time , ex);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time ,Throwable ex ) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLog sysLog = new SysLog();
com.halburt.site.sys.annotation.SysLog log = method.getAnnotation(com.halburt.site.sys.annotation.SysLog.class);
if(log != null){
//注解上的描述
sysLog.setOperation(log.value());
sysLog.setType(log.type());
}
if(ex == null){
sysLog.setFlag(SysLog.SUCCESS);
}else{
sysLog.setEx(ex.toString());
sysLog.setFlag(SysLog.ERROR);
}
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
//获取request
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
sysLog.setUserAgent(request.getHeader("user-agent"));
sysLog.setRequestUri(request.getRequestURI());
//请求的参数
try{
JSONObject json = new JSONObject();
request.getParameterMap().forEach((key, value) -> {
json.put(key, value[0]);
});
sysLog.setParams(json.toJSONString());
}catch (Exception e){
}
//用户名
try {
Principal p = ((Principal)SecurityUtils.getSubject().getPrincipal());
if(p != null){
sysLog.setUserName(p.getRealname());
sysLog.setUserId(p.getId());
}
} catch (Exception e) {
e.printStackTrace();
}
sysLog.setTime(time);
sysLog.setCreateDate(new Date());
singleThreadPool.execute(()-> sysLogService.save(sysLog));
}
}
使用
@SysLog(value="我要记录日志",type = "sys")
@ResponseBody
public String add(String key , HttpServletRequest request ) {
return "ok";
}
前端访问之后生成日志记录

Spring Boot分布式系统实践【基础模块构建3.3】注解轻松实现操作日志记录的更多相关文章
- Spring Boot分布式系统实践【1】-架构设计
前言 [第一次尝试去写一个系列,肯定会有想不到的地方,欢迎大家留言指正] 本系列将介绍如果从零构建一套分布式系统.同时也是对自己过去工作的一个梳理过程. 本文先整理出构建系统的主要技术选型,以及技术框 ...
- Spring Boot分布式系统实践【扩展1】shiro+redis实现session共享、simplesession反序列化失败的问题定位及反思改进
前言 调试之前请先关闭Favicon配置 spring: favicon: enabled: false 不然会发现有2个请求(如果用nginx+ 浏览器调试的话) 序列化工具类[ ...
- Spring Boot分布式系统实践【2】-框架搭建
前言 技术选型已经做完,那就来搭建框架了. 首先基于mvc思想,设计这套框架也是基于此,也会设计Dao层.Service层.Controller层.视图层等,同时也要考虑到dubbo的调用原理. ...
- Spring Boot 2.x基础教程:使用Swagger2构建强大的API文档
随着前后端分离架构和微服务架构的流行,我们使用Spring Boot来构建RESTful API项目的场景越来越多.通常我们的一个RESTful API就有可能要服务于多个不同的开发人员或开发团队:I ...
- Spring Boot 入门之基础构建篇(一)
博客地址:http://www.moonxy.com 一.前言 随着 Spring 的功能越来越强,在使用 Spring 的时候,门槛也变得高了起来,诸如搭建一个基于 Spring 的 Web 程序却 ...
- Spring Boot 2.x基础教程:JSR-303实现请求参数校验
请求参数的校验是很多新手开发非常容易犯错,或存在较多改进点的常见场景.比较常见的问题主要表现在以下几个方面: 仅依靠前端框架解决参数校验,缺失服务端的校验.这种情况常见于需要同时开发前后端的时候,虽然 ...
- Spring Boot 2.x基础教程:Swagger接口分类与各元素排序问题详解
之前通过Spring Boot 2.x基础教程:使用Swagger2构建强大的API文档一文,我们学习了如何使用Swagger为Spring Boot项目自动生成API文档,有不少用户留言问了关于文档 ...
- Spring Boot 2.x 基础案例:整合Dubbo 2.7.3+Nacos1.1.3(配置中心)
本文原创首发于公众号:Java技术干货 1.概述 本文将Nacos作为配置中心,实现配置外部化,动态更新.这样做的优点:不需要重启应用,便可以动态更新应用里的配置信息.在如今流行的微服务应用下,将应用 ...
- Spring Boot 2.x基础教程:Swagger静态文档的生成
前言 通过之前的两篇关于Swagger入门以及具体使用细节的介绍之后,我们已经能够轻松地为Spring MVC的Web项目自动构建出API文档了.如果您还不熟悉这块,可以先阅读: Spring Boo ...
随机推荐
- 向combobox控件中添加元素
函数定义: bool FillComboBox(CComboBox* pc, CStringList& slValues, bool bOnlyUniqueValues = false); 函 ...
- 删除外部dwg中指定的块定义
本例实现删除外部图纸中指定的块定义,在外部图纸当前模型空间中是没有该块定义的块参照存在. 代码如下: void CBlockUtil::DeleteBlockDefFormOtherDwg(const ...
- Hadoop问题:DataNode进程不见了
DataNode进程不见了 问题描述 最近配置Hadoop的时候出现了这么一个现象,启动之后,使用jps命令之后是这样的: 看不到DataNode进程,但是能够正常的工作,是不是很神奇啊? 在一番 ...
- BZOJ_1257_ [CQOI2007]余数之和sum_数学
BZOJ_1257_ [CQOI2007]余数之和sum_数学 题意:给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值. 分 ...
- BZOJ_5249_Luogu_P4364_[2018多省省队联测]_IIIDX_九省联考2018_JLOI2018_线段树
BZOJ_5249_[2018多省省队联测]IIIDX_线段树 Description [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐 ...
- 深度学习之Batch Normalization
在机器学习领域中,有一个重要的假设:独立同分布假设,也就是假设训练数据和测试数据是满足相同分布的,否则在训练集上学习到的模型在测试集上的表现会比较差.而在深层神经网络的训练中,当中间神经层的前一层参数 ...
- Java数据结构之堆和优先队列
概述 在谈堆之前,我们先了解什么是优先队列.我们每天都在排队,银行,医院,购物都得排队.排在队首先处理事情,处理完才能从这个队伍离开,又有新的人来排在队尾.但仅仅这样就能满足我们生活需求吗,明显不能. ...
- FOFA爬虫大法——API的简单利用
FOFA是一款网络空间搜索引擎,它通过进行网络空间测绘,帮助研究人员或者企业迅速进行网络资产匹配,例如进行漏洞影响范围分析.应用分布统计.应用流行度等. 何为API?如果你在百度百科上搜索,你会得到如 ...
- Akka实践一些总结
最近在一些服务中使用了akka,主要用来做异步解耦和本地消息分发(路由),这里简单总结一下用法. 与spring集成 网上有不少集成的例子,要使用到spring的扩展. 我这边没有这样处理,而是简单把 ...
- 『Zap Möbius反演』
Zap Description FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a ,y<=b,并且gcd(x,y)=d.作为F ...