1. 前言
好久没有写博客了。之前碰到过一个需求,用户登录的时候,需要写日志,这个日志不仅仅写入到日志文件中,还需要写到数据库中,因为日志功能是可以通过Spring AOP来进行实现,这两天就把该功能进行实现一下:
2.代码实现
2.1 模拟登录
首先,模拟一个最简单的登录请求:
@Controller
public class TestController {
/**
* 首先,模拟一个最简单的登录请求
* @return
*/
@RequestMapping("/login")
@ResponseBody
public String login(String username) {
System.out.println("username:" + username);
return "success";
}
}
2.2 提出AOP的应用
由于,我们知道当我们需要把登录的数据通过数据库保存下来时,这是一个非业务代码,对于这种非业务代码,而且它很可能是能实现复用的,这个时候,我们就可以使用AOP功能来进行实现。
在AOP中,主要要素有三点:切面、连接点和通知。
切面:代表要干的事情。
连接点: 代表哪些方法需要进行切面。
通知:代表的就是切面的执行时间,在目标方法之前执行切面,还是在目标方法之后执行切面。
总得来说就是:在什么方法上,在什么时间,执行相应的切面。 (具体细节可看)
2.3 代码实现
既然是日志功能,我们就需要对一些细节进行记录。比如,我们需要记录一下打印日志的过程中,传入的参数啊,调用的方法啊,等相关的内容。
@Controller
@Aspect
public class LogAop {
@Around("execution(public String com.spring.aoptest.controller.TestController.login(String))")
public Object around(ProceedingJoinPoint point) throws Throwable{
//先执行业务
Object result = point.proceed();
try {
handle(point);
} catch (Exception e) {
System.out.println("日志记录出错!");
}
return result;
}
private void handle(ProceedingJoinPoint point) throws Exception {
// 1. 获取当前拦截的方法名
MethodSignature sig = (MethodSignature) point.getSignature();
Object target = point.getTarget();
Method currentMethod = target.getClass().getMethod(sig.getName(), sig.getParameterTypes());
String methodName = currentMethod.getName();
System.out.println("methodName:" + methodName);
// 2.获取拦截方法的参数
String className = point.getTarget().getClass().getName();
Object[] params = point.getArgs();
System.out.println("className:" + className);
for (Object param: params
) {
System.out.println(param);
}
}
}
经过测试,当系统登入的时候,(调用login()方法时)相关的内容是可以被记录的。 测试结果如下图所示:
2.4 系统升级
当前,我们能够实现日志功能的记录,但是,该日志功能只能支持login()这个方法。因为
该aop只配置了这个方法而已。当然,根据约定大于配置的情况,我们是可以对让想要实现该AOP功能的类在同一个包下,或者通过命名来进行区分。这样在@Around中是可以指定这些配置来实现该AOP功能。
但是,问题来了,这也是很麻烦的,毕竟项目是会变的,要整个项目组都支持这个包的约定或者说指定的命名约定,成本会比较大。如果,没有合适的交接可能导致有些类没有用上该AOP功能,有些类多用了该AOP功能,这就比较麻烦了。
为了,解决这个问题,其实可以使用注解(注解理解很简单,就是一个接口 代表一个标记)这样的话,向用该注解的类,搞个注解,不想用AOP增强的类,就不用该注解。(这也是为什么代码加功能后,如果不想该原来代码,就可以使用AOP的原因,因为真的很简单,还挺解耦的) 这样使用上该注解后,就代表该方法需要进行AOP增强。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface BussinessLog {
}
然后,在LogAop中进行配置,将AOP的范围进行修改。
然后,在要调用的方法中,使用相应的注解。经过测试,符合预期。
这样就通过注解+AOP组合实现了登录日志功能。
2.4 注解信息
注解本身会可以携带信息,本小结就对注解进行信息携带的学习。
然后,在使用过程中,进行传参。
最后,在AOP中,打印出相应的参数。
经过测试,符合预期。
3. 小结
本文实现了注解 + AOP方式来实现日志的登录。以后要通过AOP来实现非业务代码的解耦时,可以通过注解+AOP的方式,来提升灵活性。