在一些Web管理系统项目中,日志管理功能也常见,管理系统中日志主要是记录一些谁操作了什么东西,系统是否有异常等信息。那么如何在编程中实现呢?在Java面对对象语言中要实现日志管理功能时,是比较麻烦的。在此,引入了Spring框架中的AOP思想,AOP的主要作用是可以横向插入可重复代码(日志管理),在面向对象语言中是无法做到的,它是面向对象的一种延伸。而AspectJ是AOP的一个纯Java框架,而AspectJ实现AOP有两种方式(要添加相应jar包):一种是基于xml实现,另外一种是基于注解实现。实现日志管理是用基于注解来实现的,因为使用注解方式比较灵活。
在实现功能之前,首先要了解AOP相关的一些术语:
Aspect (切面):在实际应用中,切面通常是指封装的用于横向插入系统功能(如事务、日志等)的类。
Joinpoint (连接点):方法的调用。(调用要记录日志的方法)
Pointcut (切入点):通常是指类名、方法名或者方法上的注解(要记录日志的方法名或者方法上的注解)
Advice(通知/增强处理):可以将其理解为切面类中的方法,它是切面的具体实现(日志类实现方法)。
那么,该如何使用AspectJ基于注解来实现日志管理呢?
实现的大致思路是:
1.设计日志表和日志类,编写日志Dao和Service以及实现
2.自定义注解,注解中加入几个属性,属性可以标识操作的类型(方法是做什么的)
3.编写切面,切点表达式使用上面的注解直接定位到使用注解的方法,
4.编写通知,通过定位到方法,获取上面的注解以及注解的属性,然后从session中直接获取或者从数据库获取当前登录用户的信息,最后根据业务处理一些日志信息之后调用日志Service存储日志。
- 设计相关的日志表,编写相关的接口和接口实现类(Dao、Service)
-
自定义日志注解
1
2
3
4
5
6
7 1@Retention(RetentionPolicy.RUNTIME)
2@Target(ElementType.METHOD)
3public @interface LogAnnotation {
4 String operateType();//记录日志操作类型
5}
6
7
- 编写切面类,注入service层的对象,配置切入点,该切入点是方法上的注解。日志管理使用的是@Around环绕通知,然后通过ProceedingJoinPoint对象来获取到方法(切入点),再通过该方法获取到方法上的注解,获取到注解里面的属性值(日志操作说明)。获取到用户信息,把相关值设置到Log对象中,然后执行ProceedingJoinPoint对象proceed()方法(连接点),然后包Log对象保存到数据库中。
注意:如果涉及到事务或者自定义异常类还需要在切面类try…catch…捕获异常中的catch抛出异常,给上一级调用者处理,要不然会把异常处理掉,不能抛出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 1@Aspect
2@Component//声明一个bean
3public class LogAspect {
4 @Autowired
5 private LogService logService;//日志service
6 //切入点
7 @Pointcut("@annotation(com.koi.annotation.LogAnnotation)")
8 public void myPointcut(){}
9 //环绕通知 使用@LogAnnotation注解的方法
10 @Around("myPointcut()")
11 public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
12 Signature signature = pjp.getSignature();//获取方法签名
13 Method method = ( (MethodSignature)signature ).getMethod();//获取方法
14 //这个方法才是目标对象上有注解的方法
15 Method declaredMethod = pjp.getTarget().getClass().getDeclaredMethod(signature.getName(), method.getParameterTypes());
16 LogAnnotation annotation = declaredMethod.getAnnotation(LogAnnotation.class);//获取方法上的LogAnnotation注解
17 String operateType = annotation.operateType();//获取操作说明
18 //获取操作用户信息
19 Subject subject = SecurityUtils.getSubject();
20 UUser user = (UUser)subject.getPrincipal();
21 // 创建一个日志对象(准备记录日志)
22 Log log=new Log();
23 log.setOperateType(operateType);//操作说明
24 log.setOperateor(user.getNickname());//设置操作人
25 Object obj=null;
26 try {
27 obj=pjp.proceed();
28 log.setOperateResult("操作成功");//设置操作状态
29 } catch (CustomException e) {
30 log.setOperateResult("操作失败");//设置操作状态
31 throw e;//抛给自定义异常处理器处理
32 }finally{
33 log.setOperateDate(new Date());//设置当前时间
34 logService.addLog(log);
35 }
36 return obj;
37 }
38}
39
40
在需要记录日志的方法(一般是Service实现类的方法)上添加自定义的日志注解@LogAnnotation
1
2
3
4
5
6
7
8 1@LogAnnotation(operateType="修改密码")
2 @Override
3 public ResultInfo updatePassword(UUser user, String oldPassword)
4 throws Exception {
5 .....
6 }
7
8
最后,在spring.xml容器中(父容器)添加 <aop:aspectj-autoproxy /> 配置,启动对aspectj的支持。
如果日志操作(自定义日志注解)可以在controller控制层方法中执行,该<aop:aspectj-autoproxy />还要配置在spring-mvc.xml容器(子容器)中。因为spring.xml与spring-mvc.xml是父子容器关系,父容器不可以访问到子容器的对象,而子容器可以访问到父容器的对象,但只是针对对象来说,而<aop:aspectj-autoproxy />该配置不是对象,只是开启对aspectj的支持。如果把该配置单单放在父容器中,控制层(spring-mvc.xml子容器)就对aspectj的不支持了,所以应该在父子容器都配置<aop:aspectj-autoproxy />。