700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 自定义注解-aop实现日志记录

自定义注解-aop实现日志记录

时间:2024-04-15 03:55:56

相关推荐

自定义注解-aop实现日志记录

关于注解,平时接触的可不少,像是 @Controller、@Service、@Autowried 等等,不知道你是否有过这种疑惑,使用 @Service 注解的类成为我们的业务类,使用 @Controller 注解的类就成了请求的控制器,使用 @Autowried 注解的类就会帮我们实现自动注入…

以前,我们只知道使用注解,今天我们要手写一个注解。

一、以日志记录为例

在没有使用注解实现记录日志之前,我们往往自己去调用日志记录的 Service,然后写入数据库表。

今天我们将从方法上添加自定义注解实现日志自动记录,如下:

52e77c79b07d49c6554ff2a0185d7f02.png

二、了解关于注解知识

JDK 提供了 meta-annotation 用于自定义注解的时候使用,这四个注解为:@Target,@Retention,@Documented 和 @Inherited。

以 @Controller 为例,其源码也是如此:

fc6b30adb15c78f562e0de8e503e6881.png

我们来看一下上边提到的四个注解:

三、开始我们的自定义注解

两个类:

SystemLog:自定义注解类,用于标记到方法、类上,如@SystemLog

SystemLogAspect:AOP实现切点拦截。

关于AOP的补充:

关于AOP面向切面编程概念啥的就不啰嗦了,还不了解的可以自定百度了

描述AOP常用的一些术语有:

通知(Adivce)、连接点(Join point)、切点(Pointcut)、切面(Aspect)、引入(Introduction)、织入(Weaving)

关于术语的部分可参考:/niceyoo/p/10162077.html

需要明确的核心概念:切面 = 切点 + 通知。

@Aspect 注解形式是 AOP 的一种实现,如下看一下我们要写的两个类吧。

1、@SystemLog

定义我们的自定义注解类

/**

*系统日志自定义注解

*/

@Target({ElementType.PARAMETER,ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public@interfaceSystemLog{

/**

*日志名称

*@return

*/

Stringdescription()default"";

/**

*日志类型

*@return

*/

LogTypetype()defaultLogType.OPERATION;

}

2、@SystemLogAspect

AOP拦截@SystemLog注解

/**

*SpringAOP实现日志管理

*@authorExrickx

*/

@Aspect

@Component

@Slf4j

publicclassSystemLogAspect{

privatestaticfinalThreadLocal<Date>beginTimeThreadLocal=newNamedThreadLocal<Date>("ThreadLocalbeginTime");

@Autowired

privateLogServicelogService;

@Autowired

privateUserServiceuserService;

@Autowired(required=false)

privateHttpServletRequestrequest;

/**

*定义切面,只置入带@SystemLog注解的方法或类

*Controller层切点,注解方式

*@Pointcut("execution(**..controller..*Controller*.*(..))")

*/

@Pointcut("@annotation(mon.annotation.SystemLog)")

publicvoidcontrollerAspect(){

}

/**

*前置通知(在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间

*@paramjoinPoint切点

*@throwsInterruptedException

*/

@Before("controllerAspect()")

publicvoiddoBefore(JoinPointjoinPoint)throwsInterruptedException{

##线程绑定变量(该数据只有当前请求的线程可见)

DatebeginTime=newDate();

beginTimeThreadLocal.set(beginTime);

}

/**

*后置通知(在方法执行之后并返回数据)用于拦截Controller层无异常的操作

*@paramjoinPoint切点

*/

@AfterReturning("controllerAspect()")

publicvoidafter(JoinPointjoinPoint){

try{

Stringusername="";

Stringdescription=getControllerMethodInfo(joinPoint).get("description").toString();

Map<String,String[]>logParams=request.getParameterMap();

Stringprincipal=SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();

##判断允许不用登录的注解

if("anonymousUser".equals(principal)&&!description.contains("短信登录")){

return;

}

if(!"anonymousUser".equals(principal)){

UserDetailsuser=(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();

username=user.getUsername();

}

if(description.contains("短信登录")){

if(logParams.get("mobile")!=null){

Stringmobile=logParams.get("mobile")[0];

username=userService.findByMobile(mobile).getUsername()+"("+mobile+")";

}

}

Loglog=newLog();

##请求用户

log.setUsername(username);

##日志标题

log.setName(description);

##日志类型

log.setLogType((int)getControllerMethodInfo(joinPoint).get("type"));

##日志请求url

log.setRequestUrl(request.getRequestURI());

##请求方式

log.setRequestType(request.getMethod());

##请求参数

log.setMapToParams(logParams);

##请求开始时间

DatelogStartTime=beginTimeThreadLocal.get();

longbeginTime=beginTimeThreadLocal.get().getTime();

longendTime=System.currentTimeMillis();

##请求耗时

LonglogElapsedTime=endTime-beginTime;

log.setCostTime(logElapsedTime.intValue());

##调用线程保存至log表

ThreadPoolUtil.getPool().execute(newSaveSystemLogThread(log,logService));

}catch(Exceptione){

log.error("AOP后置通知异常",e);

}

}

/**

*保存日志至数据库

*/

privatestaticclassSaveSystemLogThreadimplementsRunnable{

privateLoglog;

privateLogServicelogService;

publicSaveSystemLogThread(LogesLog,LogServicelogService){

this.log=esLog;

this.logService=logService;

}

@Override

publicvoidrun(){

logService.save(log);

}

}

/**

*获取注解中对方法的描述信息用于Controller层注解

*@paramjoinPoint切点

*@return方法描述

*@throwsException

*/

publicstaticMap<String,Object>getControllerMethodInfo(JoinPointjoinPoint)throwsException{

Map<String,Object>map=newHashMap<String,Object>(16);

##获取目标类名

StringtargetName=joinPoint.getTarget().getClass().getName();

##获取方法名

StringmethodName=joinPoint.getSignature().getName();

##获取相关参数

Object[]arguments=joinPoint.getArgs();

##生成类对象

ClasstargetClass=Class.forName(targetName);

##获取该类中的方法

Method[]methods=targetClass.getMethods();

Stringdescription="";

Integertype=null;

for(Methodmethod:methods){

if(!method.getName().equals(methodName)){

continue;

}

Class[]clazzs=method.getParameterTypes();

if(clazzs.length!=arguments.length){

##比较方法中参数个数与从切点中获取的参数个数是否相同,原因是方法可以重载

continue;

}

description=method.getAnnotation(SystemLog.class).description();

type=method.getAnnotation(SystemLog.class).type().ordinal();

map.put("description",description);

map.put("type",type);

}

returnmap;

}

}

流程补充:

通过 @Pointcut 定义带有 @SystemLog 注解的方法或类为切入点,可以理解成,拦截所有带该注解的方法。@Before 前置通知用于记录请求时的时间@AfterReturning 用于获取返回值,主要使用 getControllerMethodInfo() 方法,采用类反射机制获取请求参数,最后调用 LogService 保存至数据库。

额外补充:

关于 SecurityContextHolder 的使用为 Spring Security 用于获取用户,实现记录请求用户的需求,可根据自己框架情况选择,如使用 shiro 获取当前用户为 SecurityUtils.getSubject().getPrincipal(); 等等。

如果文章有错的地方欢迎指正,大家互相留言交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:niceyoo

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。